# V1 Domain Workbenches — SOTA Build-Out Master Checklist

- Opened: 2026-07-23
- Last scope-and-coverage audit: 2026-07-23
- Last execution-granularity audit: 2026-07-23
- Audit pass: third pass; bidirectional feature coverage plus manual semantic
  execution decomposition against current code, contracts, domain inventories,
  deep dives, walkthroughs, and specialist plans
- Status: proposed execution ledger; no unchecked item is implied complete

This is the implementation ledger for seven related authoring and operations
initiatives: Isis, Metis, Yemaya, Veritas, Euterpe, Aja, and Bellona. It expands
the original outline into independently verifiable work units and corrects its
greenfield assumption. The repository already contains substantial domain
libraries, BFF routes, standalone applications, and tests. The goal is to
integrate, harden, and complete those systems without duplicating them or
violating the product-surface boundaries already ratified elsewhere.

The second audit explicitly expanded capability families that the first deep
ledger left implicit: Isis production asset/control matrices; Metis credentials,
lecture/media, discipline, and correctness systems; Yemaya's complete existing
AAA suite; Veritas investigative, channel, regulatory, community, B2B, USSD,
commercial-boundary, and multimedia operations; Euterpe's structural DAW,
plugin/interchange, AI-audio, spatial, stage, and DJ workflows; Aja live and
multimodal capture plus vertical/CG/generative pipelines; and Bellona render,
gameplay, virtual-production, XR, mocap, MetaHuman, specialist DCC,
remote-agent, package, and event operations. This third pass reviews every
parent outcome and manually decomposes compound work into domain-specific,
independently provable child tasks wherever that creates a more executable
ledger. Section 1.2 makes atomic, bidirectional feature disposition—not the
presence of a broad phase—the permanent coverage invariant.

Tara remains the quality exemplar:

- [`TARA_CONTENT_WORKBENCH_TODOS_2026-07-19.md`](TARA_CONTENT_WORKBENCH_TODOS_2026-07-19.md)
- [`V1/TARA_CONTENT_WORKBENCH_DESIGN_2026-07-19.md`](V1/TARA_CONTENT_WORKBENCH_DESIGN_2026-07-19.md)
- `apps/oshun/web/src/app/studio/tara-workbench`
- `libs/oshun/tara-workbench`
- `apps/oshun/bff/src/tara-workbench`

Tara is a quality floor, not a UI template to clone blindly. Every initiative
must meet its domain's professional workflow, safety model, interoperability
standards, latency profile, and evidence burden.

---

## 1. Authoritative scope correction

The previous revision said that all seven domains had rich backends but zero
interactive authoring UI. That statement is not supportable against the
worktree. The 2026-07-23 audit found mature UI code in `apps/isis/web`,
`apps/yemaya/studio-web`, `apps/yemaya/studio-desktop`,
`apps/euterpe/studio-web`, `apps/bellona/control-room`, `apps/metis/admin`,
`apps/metis/web`, and `apps/veritas/web`. The real gap is fragmented coverage,
uncertain live wiring, duplicated route trees, incomplete cross-surface
workflows, and inconsistent production proof.

The canonical boundary in
[`docs/oshun/studio-vs-yemaya-studio.md`](docs/oshun/studio-vs-yemaya-studio.md)
also forbids rebuilding raw ComfyUI, model, LoRA, GPU, voice-cloning, full 3D,
or music-stem controls inside contemplative Oshun Studio. Those tools belong to
Yemaya Studio or operator applications.

A source-mounted page is not proof that a surface is authorized or reachable.
For example, `/studio/isis/nvidia-rtx-processing` has an interactive page module
but remains AAA-only under the proxy because
`STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS` is empty. The baseline must therefore test
effective middleware behavior as well as count files and routes.

| Initiative | Product classification                                                 | Canonical host to ratify in P0                                                                      | Existing implementation to integrate                                           | Boundary that must not regress                                             |
| ---------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| Isis       | V1 governed-generation substrate plus operator factory                 | `apps/isis/web` for operators; curated cards/gallery in Oshun Studio; AAA controls in Yemaya Studio | `apps/isis/web`, 70+ `libs/isis/*`, V1 Isis adapter/BFF                        | No raw provider/model/workflow/GPU controls under `/studio/isis/*`         |
| Metis      | V1 customer domain plus institutional authoring                        | Oshun Studio workbench backed by Metis services; operator review remains `/operator/metis`          | `apps/metis/{admin,web}`, `libs/metis/*`, Metis contracts/BFF                  | Do not conflate learner delivery, institutional admin, and authoring roles |
| Yemaya     | AAA creative-production suite and V1 rendering substrate               | `apps/yemaya/studio-{web,desktop}`                                                                  | existing Studio apps, study workspace, case pipeline, asset/production systems | Do not present the AAA suite as a new V1 customer domain tab               |
| Veritas    | V1 customer domain plus newsroom/editorial authoring                   | Oshun Studio newsroom integrated with Veritas CMS/services                                          | `apps/veritas/{web,cms}`, `libs/veritas/*`, canonical V1 contracts             | Reader surfaces and protected newsroom surfaces remain separate            |
| Euterpe    | Broader-platform music product, not yet ratified as an Oshun V1 domain | `apps/euterpe/studio-web`                                                                           | existing DAW, audio engine, mastering, rights, distribution systems            | No automatic V1 shell registration without an explicit product decision    |
| Aja        | Cross-domain motion subsystem and operator pipeline                    | Host decision between an Aja control room and a Studio operator route                               | Aja services/libs plus existing static Studio route inventory                  | Metis reaches Aja through the canonical adapter, not private internals     |
| Bellona    | Studio-only V1 engine/build substrate                                  | `apps/bellona/control-room` with a governed Oshun Studio entry/facade                               | existing control room, bridge hosts, build services, Studio routes             | No customer-domain route; no duplicate control room implementation         |

### 1.1 Baseline evidence to refresh before implementation

These values are discovery evidence only. They are not readiness claims.

| Domain  | Top-level library directories | Test files under `libs/<domain>` | Existing application evidence                         |
| ------- | ----------------------------: | -------------------------------: | ----------------------------------------------------- |
| Isis    |                            77 |                            1,206 | `apps/isis/web`: 69 TSX files and Playwright coverage |
| Metis   |                            27 |                              365 | `apps/metis/admin` and `apps/metis/web`               |
| Yemaya  |                            61 |                            1,118 | `studio-web`: 90 TSX; `studio-desktop`: 12 TSX        |
| Veritas |                            65 |                               98 | `apps/veritas/web`: 27 TSX; CMS and service apps      |
| Euterpe |                            52 |                              336 | `apps/euterpe/studio-web`: 115 TSX and 200+ tests     |
| Aja     |                            42 |                               65 | service/CLI apps; static Oshun Studio route inventory |
| Bellona |                            41 |                              493 | `apps/bellona/control-room`: 10 TSX and Playwright    |

- [x] BASE.1 Regenerate the package, route, application, and test inventory from
      the current branch. Evidence: a committed machine-readable snapshot and
      the exact command used to produce it.
      <!-- evidence: evidence/v1-workbenches/inventory/ (manifest/packages/applications/routes/tests/reconciliation.json + README.md); generator scripts/v1-workbenches/generate-inventory.mjs; command `pnpm inventory:v1-workbenches`; verify `pnpm verify:inventory:v1-workbenches` (drift-check + 8 known-value tests, all green at SHA 050e5d67). 473 packages, 48 apps, 329 routes, 4812 tests, 0 unexplained records. -->
  - [x] BASE.1.a Pin the audited commit SHA, workspace roots, ignored paths, and
        tool versions before scanning. Evidence: inventory manifest records all
        four inputs.
        <!-- evidence: manifest.json inputs.{auditedCommitSha, workspaceRoots, ignoredPathGlobs, toolVersions} -->
  - [x] BASE.1.b Enumerate workspace packages and top-level domain libraries
        from manifests and project configuration. Evidence: package rows retain
        manifest path, project name, domain, and target metadata.
        <!-- evidence: packages.json — 473 rows with manifests[], projectName, domain, targets[], exports[], language (npm/Nx/Python/C++ union so no project is hidden) -->
  - [x] BASE.1.c Enumerate deployable applications, services, workers, CLIs,
        desktop shells, and bridge hosts. Evidence: application rows identify
        runtime type, entry point, and owning project.
        <!-- evidence: applications.json — 48 rows with runtimeType (web-frontend/service/worker/cli/desktop-shell/engine-bridge-host/gpu-worker/remote-agent-host/api-gateway/mobile-app/agent-runtime), entryPoint, projectName, root -->
  - [x] BASE.1.d Enumerate filesystem, framework, proxy, redirect, and
        middleware-derived routes independently. Evidence: route rows retain
        route source and effective host.
        <!-- evidence: routes.json — framework-aware: Next.js filesystem routes (app-router-page/app-router-handler/pages-router-page/pages-api-route/middleware) are gated on an ancestor next.config so Vite apps' src/pages are not miscounted; React Router `<Route path=>` framework routes captured as source `react-router`; config-redirect/config-rewrite from next.config; test/util files excluded. 125 routes (was 329 with Vite/test false positives). manifest.proxyGovernance records the external Studio proxy (apps/oshun/web/src/proxy.ts) + verified fail-closed STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS=[]. -->
  - [x] BASE.1.e Enumerate unit, component, contract, integration, browser,
        mobile, desktop, accessibility, performance, security, and evaluation
        test files. Evidence: test rows identify harness and owning target.
        <!-- evidence: tests.json — 4812 rows with harness (vitest/playwright/maestro/node:test), category (unit/component/contract/integration/accessibility/performance/security/evaluation/browser-or-device), owningTarget -->
  - [x] BASE.1.f Reconcile generated records with workspace configuration so
        aliases neither duplicate nor hide a package, application, route, or
        test. Evidence: reconciliation report has zero unexplained records.
        <!-- evidence: reconciliation.json unexplainedRecords=0; surfaced 11 cross-domain name leaks (stale lilith->aja) + non-npm projects as owner-routed findings; benign scope-strip conventions classified -->
  - [x] BASE.1.g Commit the normalized snapshot together with its deterministic
        command and input SHA. Evidence: a clean regeneration produces no diff.
        <!-- evidence: `pnpm verify:inventory:v1-workbenches` → "inventory: no drift."; no timestamps in output; git ls-files driven; determinism test green -->

- [ ] BASE.2 Classify every existing surface as live, fixture-backed,
      documentation-only, dead, redirected, or provider-gated. Evidence: every
      discovered route has exactly one classification.
  - [x] BASE.2.a Define mutually exclusive classification criteria, including
        how mixed live/fixture and partially wired surfaces are represented.
        Evidence: reviewed classification schema and examples.
        <!-- evidence: evidence/v1-workbenches/surface-classification-schema.md — 6 mutually-exclusive primary classes with a precedence decision order (one class per surface), sub-state fields (wiringCompleteness/fixtureLeakage/providerStates/roleMatrix/dataStates/evidenceRefs) for mixed & partial surfaces, and repo-grounded positive + negative examples (Studio-Isis fail-closed provider-gating, comfy-cloud provider_not_configured, metis NextResponse.redirect, veritas reading-list live). Runtime application is BASE.2.b-g. -->
  - [ ] BASE.2.b Resolve each route through its real host, middleware, feature
        flags, and authorization policy rather than inferring reachability from
        a source file. Evidence: effective-route results for every route row.
  - [ ] BASE.2.c Trace each reachable surface to its actual data source and
        record whether fixtures, hard-coded values, or fabricated adapters can
        enter the result. Evidence: route-to-data-source links.
  - [ ] BASE.2.d Exercise provider-gated surfaces with provider configuration
        present and absent. Evidence: both configured and `not_configured`
        results are captured.
  - [ ] BASE.2.e Verify redirects at the final destination and distinguish
        intentional compatibility redirects from abandoned route shells.
        Evidence: redirect-chain and destination-status records.
  - [ ] BASE.2.f Assign an owner and remediation or retirement action to every
        dead, ambiguous, or mixed-classification surface. Evidence: zero
        ownerless exception rows.
  - [ ] BASE.2.g Independently review the completed classification against the
        discovered route inventory. Evidence: signed review with zero
        unclassified routes.
- [ ] BASE.3 Map every top-level library to reuse, refactor, retire, or
      explicitly out-of-scope. Evidence: no library directory is absent from the
      signed inventory.
  - [x] BASE.3.a Record every library's manifest, exported entry points, public
        symbols, and current domain ownership. Evidence: source-linked library
        rows.
        <!-- evidence: evidence/v1-workbenches/inventory/library-graph.json (per-package manifest/root/domain/exports/publicSymbolCount) + library-public-symbols.json (96,094 symbols across 412/415 libs; barrel-following through .js/.d.ts/dist entry indirection). 3 zero-symbol libs surfaced (2 Unity C# plugins + @oshun/documentation broken entry). command `pnpm inventory:v1-workbenches`. -->
  - [x] BASE.3.b Identify all application, service, worker, CLI, test, and
        library consumers for each library. Evidence: inbound-consumer map.
        <!-- evidence: library-graph.json packages[].inboundConsumers (distinct consumer projects, whole-repo git-grep scan so consumers outside the 7 domains are counted; multi-line `} from '@scope/x'` handled; string-literal mentions excluded). Guarded by generate-library-graph.test.mjs. -->
  - [x] BASE.3.c Identify each library's runtime and build-time dependencies,
        including cross-domain and private-internal imports. Evidence:
        outbound-dependency map.
        <!-- evidence: library-graph.json packages[].outboundDependencies with crossDomain + deep (private-internal, past-public-export) flags per edge; outboundCrossDomainCount/outboundDeepImportCount rollups. -->
  - [ ] BASE.3.d Assign exactly one reuse, refactor, retire, or out-of-scope
        disposition with rationale and owner. Evidence: zero ambiguous or
        placeholder dispositions.
        <!-- partial: library-graph.json packages[].proposedDisposition + dispositionRationale provide a machine recommendation (258 reuse / 191 retire-candidate / 12 refactor-candidate); OWNER assignment is a human decision and is not fabricated, so this stays unchecked. -->
  - [ ] BASE.3.e For refactor rows, name the target boundary, migration order,
        compatibility period, and removal condition. Evidence: executable
        migration references.
  - [ ] BASE.3.f For retire or out-of-scope rows, identify every affected
        consumer and the replacement, compatibility, or removal path. Evidence:
        consumer-complete impact records.
  - [ ] BASE.3.g Reconcile the signed mapping back to the filesystem. Evidence:
        a path-level check reports neither missing nor extra libraries.
- [ ] BASE.4 Record route ownership and canonical host decisions before adding a
      route. Evidence: approved decision records resolve every row above.
  - [ ] BASE.4.a Identify the audience, trust boundary, data classification,
        latency profile, and operator/customer role for every candidate route.
        Evidence: completed route-context fields.
  - [ ] BASE.4.b Enumerate all plausible existing hosts and the reuse,
        duplication, navigation, and deployment consequences of each. Evidence:
        host-option matrix.
  - [ ] BASE.4.c Select one canonical host and owning team for every retained
        route. Evidence: approved route-to-host and route-to-owner mappings.
  - [ ] BASE.4.d Decide whether each noncanonical route is removed, redirected,
        proxied, or retained for compatibility. Evidence: migration behavior and
        end date per noncanonical route.
  - [ ] BASE.4.e Record cross-host authentication, authorization, deep-link, and
        data-handoff requirements. Evidence: approved boundary contract.
  - [ ] BASE.4.f Obtain product, architecture, security, and domain-owner
        approval where the decision changes an existing product boundary.
        Evidence: named approval records.
- [ ] BASE.5 Reconcile this ledger with `domains.json`, V1 architecture,
      `DOMAINS.md`, and the Studio/Yemaya boundary. Evidence: the domain
      registry checker passes and no documentation contradiction remains.
  - [x] BASE.5.a Extract domain classification, host, audience, ownership, and
        boundary assertions from each authoritative source. Evidence:
        source-and-heading-indexed assertion table.
        <!-- evidence: registry-reconciliation.json authoritativeAssertions — per-initiative table indexed by source + heading provenance across domains.json (status/npmScope/owner/projects), ledger §1 (classification + boundary), and DOMAINS.md (tagline + line). e.g. Euterpe: DOMAINS.md:178 "Music & Sonic Creation" vs domains.json active vs ledger "not yet ratified". -->
  - [x] BASE.5.b Compare `domains.json` identifiers and lifecycle fields with
        the ledger's seven initiatives. Evidence: field-level registry diff.
        <!-- evidence: evidence/v1-workbenches/registry-reconciliation.json + scripts/v1-workbenches/generate-registry-reconciliation.mjs — per-initiative field checks (npmScope/libsPath/appsPath/projects vs inventory reality) and 3 surfaced contradictions for BASE.5.f: euterpe ratification (domains.json status=active vs ledger "not yet ratified as an Oshun V1 domain"), bellona + yemaya project-count drift. Deterministic + 5 tests. -->
  - [ ] BASE.5.c Compare V1 architecture host and dependency rules with every
        route and integration proposed here. Evidence: architecture-rule diff.
  - [ ] BASE.5.d Compare `DOMAINS.md` product classifications and ownership with
        the ledger. Evidence: classification-and-owner diff.
  - [x] BASE.5.e Compare every Studio and Yemaya exposure with the ratified
        Studio/Yemaya boundary. Evidence: route-and-control boundary matrix.
        <!-- evidence: evidence/v1-workbenches/studio-boundary-matrix.json +
        scripts/v1-workbenches/generate-studio-boundary-matrix.mjs — verifies
        the 31 AAA-only segments in docs/oshun/studio-vs-yemaya-studio.md agree
        EXACTLY with AAA_ONLY_STUDIO_ROUTES in
        libs/isis/entitlements/src/studio-boundary.ts (docCodeAgree, 0 diff),
        STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS is empty (failClosed), and every AAA
        control (ComfyUI/LoRA/GPU/voice/3D/music) is blocked on the
        contemplative product. --check fails on any boundary regression. 5
        tests. -->
  - [ ] BASE.5.f Resolve each contradiction in its authoritative source rather
        than documenting competing truths. Evidence: zero unresolved diff rows.
  - [x] BASE.5.g Run the registry checker and validate every changed internal
        document link. Evidence: passing command outputs.
        <!-- evidence: `node tools/domains/check-registry.mjs` → "✓ domains.json in sync (58 domains, 63 scope tags)" (exit 0). Every relative link + referenced file path in the changed docs (evidence/v1-workbenches/README.md, inventory/README.md, and the ledger's markdown links) resolves to a real path (link-check clean, 0 broken). Note: this validates link/registry integrity; the Euterpe classification contradiction is an inter-source contradiction for BASE.5.f (human), not a broken link. -->
- [ ] BASE.6 Capture a browser walkthrough of every existing candidate host.
      Evidence: dated screenshots, network traces, console logs, and a route
      coverage matrix are linked from the inventory.
  - [ ] BASE.6.a Define the supported browser, viewport, locale, tenant, role,
        feature-flag, provider, and data-state matrix for the walkthrough.
        Evidence: reproducible walkthrough manifest.
  - [ ] BASE.6.b Provision nonfixture accounts and deterministic live-data
        records for every authorized role. Evidence: fixture-independent setup
        record with secret-safe identifiers.
  - [ ] BASE.6.c Visit every inventory route through normal navigation and by
        direct deep link. Evidence: navigation and deep-link result per route.
  - [ ] BASE.6.d Capture the initial, loaded, empty, partial, unauthorized,
        unavailable, and error states that the route can produce. Evidence:
        state-indexed screenshots or approved nonapplicability.
  - [ ] BASE.6.e Archive request/response metadata, redirects, console errors,
        failed resources, and runtime warnings for each route. Evidence:
        timestamped trace and console bundle.
  - [ ] BASE.6.f Record broken navigation, unreachable source-mounted pages,
        fixture leakage, stale content, and host-boundary violations as owned
        findings. Evidence: finding IDs linked to route rows.
  - [ ] BASE.6.g Reconcile captured routes with the candidate-host inventory.
        Evidence: matrix reports zero unvisited reachable routes.
- [ ] BASE.7 Probe each current API from its real UI with fixtures disabled.
      Evidence: the matrix distinguishes live data, empty data, 503
      `not_configured`, and accidental mock data.
  - [ ] BASE.7.a Enumerate every API request initiated by each current UI and
        map it to the initiating user action. Evidence: action-to-request map.
  - [ ] BASE.7.b Disable documented fixture, demo, mock-service, interception,
        and development-fallback switches. Evidence: captured runtime
        configuration with all fixture paths off.
  - [ ] BASE.7.c Exercise an authorized live-data response and verify displayed
        values against persisted or provider source data. Evidence: source/UI
        value comparison.
  - [ ] BASE.7.d Exercise a legitimate empty result and verify the UI does not
        substitute sample content. Evidence: empty-state trace and screenshot.
  - [ ] BASE.7.e Remove required provider configuration and verify the API and
        UI preserve explicit `not_configured` semantics. Evidence: 503 body,
        trace, and visible state.
  - [ ] BASE.7.f Exercise authentication, authorization, and tenant-isolation
        failures for each protected API family. Evidence: negative matrix with
        no data disclosure.
  - [ ] BASE.7.g Detect responses originating from hard-coded adapters, request
        interception, seeded demos, or fallback fixtures. Evidence: zero
        unexplained mock signatures.
  - [ ] BASE.7.h Link every API finding to the owning route, service, and
        remediation task. Evidence: zero ownerless findings.
- [ ] BASE.8 Establish the baseline accessibility, performance, reliability, and
      security results for every host. Evidence: raw reports are archived before
      feature work begins.
  - [ ] BASE.8.a Pin the host build, environment, accounts, datasets, browsers,
        devices, clocks, and network profiles used by all baseline runs.
        Evidence: baseline environment manifest.
  - [ ] BASE.8.b Run automated accessibility scans over every reachable route
        and state in the walkthrough matrix. Evidence: raw route-level results.
  - [ ] BASE.8.c Run keyboard and screen-reader smoke journeys for each host's
        critical workflow. Evidence: journey-level findings and recordings.
  - [ ] BASE.8.d Measure navigation, interaction, rendering, memory, and
        transfer budgets under the supported device/network profiles. Evidence:
        raw performance traces and budget comparison.
  - [ ] BASE.8.e Measure API availability, latency, error rate, queue age,
        retry, and recovery behavior for exercised dependencies. Evidence:
        reliability baseline report.
  - [ ] BASE.8.f Run dependency, secret, configuration, header, transport,
        authorization, upload, and remote-fetch security checks as applicable.
        Evidence: raw security reports.
  - [ ] BASE.8.g Deduplicate findings without discarding route/state provenance,
        then assign severity and owner. Evidence: triaged baseline register.
  - [ ] BASE.8.h Archive immutable raw reports before remediation begins.
        Evidence: checksummed evidence index with capture timestamps.

### 1.2 Atomic feature-completeness rule

The audit unit is an atomic capability, not a package, route, page, or broad
phase label. A capability is in scope for reconciliation when it is asserted or
implemented by any current application route, component, library, public
contract, API/worker/CLI command, schema or migration, event, test, feature
inventory, domain deep dive, walkthrough, proposal, or release specification.
Every discovered capability must have one explicit disposition:

1. exposed in the named workbench for an authorized audience;
2. exposed in a separate operator, AAA, consumer, institutional, or developer
   surface with a governed workbench handoff where appropriate;
3. runtime-only but observable and controllable through an authorized
   operational projection;
4. retained for compatibility with no new exposure;
5. retired through a dated migration; or
6. excluded by a named product/safety decision with rationale, owner,
   compensating control, and review date.

Broad tasks such as "map every library" do not by themselves prove feature
coverage. The machine-readable inventory must split multi-feature packages and
pages into atomic rows and map each row to its contract, owner, audience, host,
route or entry point, backing API/job/store, permissions, safety and quality
gates, failure states, tests, documentation, and disposition evidence.

- [ ] BASE.9 Generate an atomic capability inventory from every source class
      named above and retain source file plus symbol/route/heading provenance.
      Evidence: deterministic committed inventory and generator command.
  - [x] BASE.9.a Define the canonical capability-row schema, identifier rules,
        and the boundary between one atomic behavior and a compound feature.
        Evidence: reviewed schema with positive and negative examples.
        <!-- evidence: evidence/v1-workbenches/capability-inventory.manifest.json — rowSchema (id `cap:<kind>:<domain>:<slug>`, fields, sourceFields, kinds) + atomicityBoundary with the one-behavior-per-row rule and positive/negative examples (single route/operation-symbol/entity/CLI-verb = atomic; package/dual-purpose page/getter/factory = split or excluded). -->
  - [x] BASE.9.b Extract capability candidates from application routes and
        interactive components. Evidence: source path, route, export, and
        interaction provenance per candidate.
        <!-- evidence: capability-inventory.json kind=surface (110), one row per route from BASE.1 routes.json with source.{file,ref=routePath,provenance=route:<source>,host}. -->
  - [x] BASE.9.c Extract capability candidates from library public exports and
        externally consumed internal symbols. Evidence: symbol-level provenance.
        <!-- evidence: capability-inventory.json kind=operation (3222) from BASE.3 library-public-symbols.json with a tight action-verb filter (excludes getters/factories/pure helpers); source.{file=pkg,ref=symbol,verb}. Deep externally-consumed-internal imports are captured at package granularity in library-graph.json (outboundDeepImportCount). -->
  - [x] BASE.9.d Extract capability candidates from public contracts, API
        operations, workers, jobs, events, and CLI commands. Evidence:
        operation-level provenance.
        <!-- evidence: capability-inventory.json — 654 kind=api-operation (Express/Fastify `METHOD /path` handlers, provenance=http-route), 2227 kind=event (emit/publish/dispatch string event names, provenance=event-emit), 37 kind=command (CLI, provenance=cli-command), 2 kind=job (worker/queue, provenance=worker-queue). Contract operations are represented by their concrete HTTP api-operation rows. -->
  - [ ] BASE.9.e Extract capability candidates from schemas, migrations,
        persisted entities, state machines, and policy definitions. Evidence:
        entity/transition/rule provenance.
        <!-- partial: 162 persisted entities (Prisma models + SQL tables, deduped) extracted with provenance; state-machine transitions and policy-rule definitions are NOT yet extracted, so this stays unchecked. -->
  - [ ] BASE.9.f Extract capability candidates from tests, feature inventories,
        deep dives, walkthroughs, proposals, and release specifications.
        Evidence: test or document heading provenance.
  - [ ] BASE.9.g Split candidates that combine distinct audiences, mutations,
        lifecycle transitions, policies, failure modes, or outputs. Evidence:
        one-behavior-per-row review results.
  - [x] BASE.9.h Preserve every original source reference when normalizing
        candidates into canonical rows. Evidence: zero source records without a
        canonical row.
        <!-- evidence: every capability-inventory.json row carries source.{file,ref,provenance}; the generate-capability-inventory.test.mjs schema test asserts no row lacks provenance or ref. -->
  - [x] BASE.9.i Commit the deterministic inventory and exact extraction
      command. Evidence: rerunning from the pinned SHA produces no diff. <!--
      evidence:
      `node scripts/v1-workbenches/generate-capability-inventory.mjs --check` →
      "capability-inventory: no drift."; sorted output, no timestamps; command
      wired into `pnpm inventory:v1-workbenches` /
      `verify:inventory:v1-workbenches`. -->
  <!-- BASE.9.f (documented from feature-inventories/deep-dives/proposals beyond tests) and BASE.9.g (active compound-candidate splitting review) remain unchecked; the parent BASE.9 stays unchecked until d/e/f/g complete. -->
- [ ] BASE.10 Reconcile aliases and duplicates without collapsing distinct
      audience, lifecycle, policy, or data behaviors. Evidence: every source
      capability maps to exactly one canonical capability ID and all aliases
      remain searchable.
  - [ ] BASE.10.a Normalize spelling, casing, route aliases, deprecated names,
        and equivalent public/internal terminology. Evidence: searchable alias
        candidates with source provenance.
  - [ ] BASE.10.b Group only candidates whose actors, inputs, outputs,
        mutations, policies, lifecycle, and failure semantics are equivalent.
        Evidence: equivalence criteria recorded for every merge.
  - [ ] BASE.10.c Keep separate rows when audience, lifecycle, policy, data
        classification, side effects, or recovery behavior differs. Evidence:
        reviewed false-merge challenge set.
  - [ ] BASE.10.d Select a stable canonical identifier and preferred label for
        every accepted group. Evidence: canonical-ID assignment table.
  - [ ] BASE.10.e Retain bidirectional links from every alias and duplicate
        source to its canonical row. Evidence: alias lookup tests.
  - [ ] BASE.10.f Resolve ambiguous candidate groups with the named domain
        owner. Evidence: zero unresolved merge decisions.
  - [ ] BASE.10.g Check that every source candidate maps to exactly one
        canonical ID and no canonical row is source-empty unless labeled
        net-new. Evidence: passing cardinality report.
- [ ] BASE.11 Assign every canonical capability one of the six dispositions
      above. Evidence: zero missing, ambiguous, or generic "covered elsewhere"
      dispositions.
  - [ ] BASE.11.a Record the authorized audience and canonical surface for every
        capability proposed for direct workbench exposure. Evidence:
        audience-and-surface fields.
  - [ ] BASE.11.b Record the separate surface and governed handoff for every
        operator, AAA, consumer, institutional, or developer exposure. Evidence:
        handoff contract or approved no-handoff rationale.
  - [ ] BASE.11.c Record the observability and control projection for every
        runtime-only capability. Evidence: operational projection references.
  - [ ] BASE.11.d Record compatibility owner, supported consumers, and exit
        condition for every compatibility-only capability. Evidence:
        compatibility register.
  - [ ] BASE.11.e Record migration, consumer impact, removal date, and rollback
        for every retired capability. Evidence: dated retirement plan.
  - [ ] BASE.11.f Record approver, rationale, compensating control, and review
        date for every excluded capability. Evidence: approved exclusion record.
  - [ ] BASE.11.g Reject placeholder dispositions such as "covered elsewhere"
        until the exact capability and destination are linked. Evidence: lint
        reports zero generic dispositions.
  - [ ] BASE.11.h Obtain domain-owner review of all assignments and resolve
        conflicts between source intent and product scope. Evidence: signed
        disposition review.
- [ ] BASE.12 For every exposed or operationally projected capability, record
      create/read/update/delete or invoke/cancel/retry semantics, bulk behavior,
      import/export, search/discovery, history/diff, collaboration/review,
      accessibility, localization, offline/degraded behavior, observability,
      recovery, and release/retirement applicability. Evidence: applicability
      matrix contains an implementation/test link or approved N/A record in
      every cell.
  - [ ] BASE.12.a Record create, read, update, delete, and lifecycle-transition
        applicability per capability. Evidence: mutation/read cells contain
        implementation and test links or reviewed N/A records.
  - [ ] BASE.12.b Record invoke, cancel, retry, resume, timeout, and
        compensation applicability for command and job capabilities. Evidence:
        command-state cells are complete.
  - [ ] BASE.12.c Record single-item and bulk eligibility, limits, partial
        success, and rollback behavior. Evidence: bulk-operation cells are
        complete.
  - [ ] BASE.12.d Record import, export, interchange version, validation,
        round-trip, and unsupported-format behavior. Evidence: interchange cells
        are complete.
  - [ ] BASE.12.e Record search, filter, sort, pagination, saved view, catalog,
        and discovery behavior. Evidence: discovery cells are complete.
  - [ ] BASE.12.f Record immutable revision, history, diff, restore, fork, and
        provenance behavior. Evidence: revision cells are complete.
  - [ ] BASE.12.g Record collaboration, comment, assignment, review, approval,
        override, and notification behavior. Evidence: governance cells are
        complete.
  - [ ] BASE.12.h Record keyboard, screen-reader, nonvisual alternative,
        responsive, locale, time-zone, and bidirectional-text behavior.
        Evidence: accessibility/localization cells are complete.
  - [ ] BASE.12.i Record offline, reconnect, partial, stale, conflict, degraded,
        unavailable, and recovery behavior. Evidence: resilience-state cells are
        complete.
  - [ ] BASE.12.j Record audit, trace, metric, log, cost, freshness, support,
        and incident-diagnostic behavior. Evidence: operability cells are
        complete.
  - [ ] BASE.12.k Record release, migration, rollback, compatibility,
        deprecation, retirement, retention, and deletion behavior. Evidence:
        lifecycle-release cells are complete.
  - [ ] BASE.12.l Validate that every matrix cell has a precise implementation
        and test reference or an approved child-specific N/A record. Evidence:
        zero blank, generic, or unapproved cells.
- [ ] BASE.13 Diff the atomic capability inventory in CI. Evidence: a new route,
      exported symbol, contract operation, job, schema entity, CLI command,
      documented feature, or walkthrough fails the gate until dispositioned.
  - [x] BASE.13.a Serialize the canonical inventory in deterministic source and
        identifier order. Evidence: repeated generation is byte-stable. <!--
        evidence: capability-inventory.json is stable-stringified (sorted keys)
        and ID-sorted; `generate-capability-inventory.mjs --check` → "no drift".
        The gate compares by semantic id with key-canonicalized source. -->
  - [x] BASE.13.b Detect additions, removals, and semantic changes in route and
        interactive-component candidates. Evidence: route/component mutation
        tests.
        <!-- evidence: scripts/v1-workbenches/capability-diff-gate.mjs + capability-diff-gate.test.mjs — injecting a net-new `surface` capability is detected as ADDED; removing/moving one is detected as REMOVED/CHANGED. -->
  - [x] BASE.13.c Detect additions, removals, and semantic changes in exported
        symbols and packages. Evidence: library mutation tests.
        <!-- evidence: gate test injects a net-new `operation` capability (library export) and asserts it fails the gate. -->
  - [x] BASE.13.d Detect additions, removals, and semantic changes in contracts,
        API operations, jobs, events, and CLI commands. Evidence: interface
        mutation tests.
        <!-- evidence: the capability diff gate now covers api-operation, event, command, and job kinds; the gate test injects a net-new capability of every kind (incl. api-operation and event) and asserts each fails the gate. -->
  - [x] BASE.13.e Detect additions, removals, and semantic changes in schemas,
        migrations, and persisted entities. Evidence: data-model mutation tests.
        <!-- evidence: gate test injects a net-new `entity` capability (persisted schema/table) and asserts it fails the gate. -->
  - [ ] BASE.13.f Detect additions, removals, and semantic changes in documented
        features and walkthrough findings. Evidence: documentation mutation
        tests.
        <!-- not covered: the gate operates on the core capability set; documented (test/doc heading) rows and walkthrough findings are not gated yet. -->
  - [x] BASE.13.g Fail with the exact undispositioned source and remediation
        command rather than an opaque snapshot mismatch. Evidence: reviewed
        failure output.
        <!-- evidence: the gate prints ADDED/REMOVED/CHANGED capability IDs grouped by kind plus the remediation command (`pnpm inventory:v1-workbenches`) and exits 1, instead of a byte-diff. -->
  - [x] BASE.13.h Prove that an explicitly dispositioned change passes while
      unrelated formatting changes do not create false capability churn.
      Evidence: positive and noise-control CI tests.
      <!-- evidence: gate test asserts committed baseline == freshly built passes (dispositioned), and reordering source object keys registers NO change (ids are semantic kind:domain:behavior, path-free). -->
  <!-- BASE.13.d/f and the parent BASE.13 stay unchecked (events/contracts + documented sources not yet gated). -->
- [ ] BASE.14 Run a bidirectional audit: every inventory row maps to a ledger
      requirement and every domain ledger requirement maps back to one or more
      authoritative inventory rows or a clearly labeled net-new requirement.
      Evidence: zero orphan source capabilities and zero unexplained checklist
      capabilities.
  - [ ] BASE.14.a Link every canonical inventory row to its exact parent or
        child ledger identifier. Evidence: zero inventory rows without ledger
        links.
  - [ ] BASE.14.b Link every ledger capability requirement to one or more
        canonical inventory identifiers. Evidence: zero ledger requirements
        without inventory or net-new links.
  - [ ] BASE.14.c Label net-new requirements explicitly and record their
        product, architecture, and domain authority. Evidence: approved net-new
        register.
  - [ ] BASE.14.d Detect many-to-one mappings that conceal distinct audience,
        policy, lifecycle, or failure behaviors. Evidence: reviewed mapping
        cardinality exceptions.
  - [ ] BASE.14.e Detect inventory capabilities linked only to broad phase
        labels instead of executable requirements. Evidence: zero phase-only
        links.
  - [ ] BASE.14.f Resolve orphan source capabilities to exposure, separate
        surface, runtime projection, compatibility, retirement, or exclusion.
        Evidence: zero source-side orphans.
  - [ ] BASE.14.g Resolve unexplained checklist capabilities to authoritative
        source evidence or approved net-new status. Evidence: zero ledger-side
        orphans.
  - [ ] BASE.14.h Publish the final forward and reverse coverage reports.
        Evidence: checksummed reports generated from the same inventory version.
- [ ] BASE.15 Require named domain-owner review of the final atomic inventory,
      including consumer/runtime-only exclusions. Evidence: signed review
      records for all seven domains; an engineering agent cannot self-approve
      product scope.
  - [ ] BASE.15.a Prepare one domain-specific review packet containing source
        coverage, canonical capabilities, aliases, dispositions, exclusions,
        mappings, and unresolved findings. Evidence: seven versioned packets.
  - [ ] BASE.15.b Obtain Isis owner review and resolve every Isis comment.
        Evidence: named Isis approval and closed comment log.
  - [ ] BASE.15.c Obtain Metis owner review and resolve every Metis comment.
        Evidence: named Metis approval and closed comment log.
  - [ ] BASE.15.d Obtain Yemaya owner review and resolve every Yemaya comment.
        Evidence: named Yemaya approval and closed comment log.
  - [ ] BASE.15.e Obtain Veritas owner review and resolve every Veritas comment.
        Evidence: named Veritas approval and closed comment log.
  - [ ] BASE.15.f Obtain Euterpe owner review and resolve every Euterpe comment.
        Evidence: named Euterpe approval and closed comment log.
  - [ ] BASE.15.g Obtain Aja owner review and resolve every Aja comment.
        Evidence: named Aja approval and closed comment log.
  - [ ] BASE.15.h Obtain Bellona owner review and resolve every Bellona comment.
        Evidence: named Bellona approval and closed comment log.
  - [ ] BASE.15.i Re-run both coverage directions after all owner-requested
        changes. Evidence: final zero-orphan reports at the approved revision.

---

## 2. Execution contract

### 2.1 Checkbox and evidence rules

- `[ ]` and `[x]` are the sole task-completion source of truth.
- Each checkbox represents one independently reviewable outcome.
- A checkbox may flip only when the evidence named by the item exists.
- A phase heading is organizational; it is never a completion signal.
- An item marked not applicable must remain unchecked until a decision record
  identifies the approver, rationale, replacement control, and review date.
- Existing code is evidence only after it has been inspected and its relevant
  verification has passed on the current branch.
- A unit test does not prove a browser journey; a fixture does not prove live
  integration; a screenshot does not prove authorization; a narrow build does
  not prove a release gate.
  <!-- AND A SPECIFICATION DOES NOT PROVE A PRODUCT — measured 2026-08-16.
  evidence/v1-workbenches/v1-reference-implementation-reach.json —
  generate-v1-reference-implementation-reach.mjs + .test.mjs (8 tests, 2
  ratchets, 3 controls), wired into inventory + verify.

  A large share of this checklist's closed cells cite a "COMPILING reference"
  under `evidence/v1-workbenches/<name>/*.ts`. There are 88 such directories,
  692 typed source files and 8,715 exported symbols. Every one carries its own
  strict tsconfig, most carry a `check.ts`, and the verify lane runs them, so
  they compile and their tests pass. NOT ONE LINE UNDER `libs/` OR `apps/`
  IMPORTS A MODULE FROM ANY OF THEM. The mentions that exist are JSON artifact
  paths and prose in comments, which is why the scan matches an IMPORT
  SPECIFIER and not a path string.

  693 CHECKLIST CELLS CITE ONE OF THESE REFERENCES AND 683 OF THEM ARE CHECKED.
  That is not automatically wrong. A cell whose evidence clause asks for a
  schema, a contract, a taxonomy or a row is legitimately satisfied by an
  executable specification — that is what a specification is, and these are
  unusually good ones. A cell whose clause asks for TESTS, fixtures, a probe, a
  gate or a report is asking for something EXECUTED, and then what matters is
  what it was executed against: the answer is the specification, because nothing
  imports it.

  CLASSIFIED BY EACH CELL'S OWN EVIDENCE SENTENCE, executed evidence winning
  when a clause names both: 588 behavioural, 51 specifiable, 54 unclassified —
  and 582 CHECKED cells whose evidence asks for something executed cite a
  reference no product code imports. A first cut of the classifier matched only
  named phrases like "integration tests" and left 530 of the 693 clauses
  unclassified, which would have made this split meaningless; most cells say
  "lifecycle tests", "eligibility tests", "race tests".

  This artifact draws a distinction the checklist itself does not, because a
  cell records only that its evidence exists. -->

- Human governance approvals remain unchecked until performed by the named
  human. An agent may prepare an engineering-readiness packet but may not
  fabricate approval.
- A top-level identifier such as `I3.4` remains the accountable outcome. When
  that outcome contains separately buildable behavior, sequential gates,
  independently owned work, multiple artifacts, or multiple proof obligations,
  nested `.a`, `.b`, and later identifiers define its executable child tasks.
- Child tasks must be written from the parent's domain semantics. Do not add
  generic lifecycle boilerplate merely to increase the checkbox count, and do
  not derive work mechanically from punctuation or keywords.
- A parent may flip to `[x]` only after every applicable nested checkbox is
  complete and linked from the parent's rollup evidence. The parent must remain
  unchecked while any child is unchecked, failed, stale, or awaiting approval.
- A nested task may be marked not applicable only through the same reviewed N/A
  record required above. A broad N/A decision may cover multiple children only
  when it names each child identifier and gives a child-specific rationale.
- Child identifiers are stable evidence keys and follow execution order where
  ordering exists. Renaming or resequencing a child requires updating every
  evidence and dependency reference to it.
- An already atomic parent needs no ceremonial children. During implementation,
  newly discovered semantic work must become a new child or parent when it has
  independent behavior, ownership, sequencing, failure handling, or approval.
- Automation may identify review candidates and validate checklist structure,
  identifiers, evidence clauses, and parent/child completion consistency. It
  must not author or regenerate the semantic task text.

### 2.2 Quality and honesty rules

- Every scorer, gate, editor, dispatcher, and pipeline stage ships real domain
  logic with known-value tests or fails loudly behind an explicit
  `not_configured` or `unavailable` result.
- Generated, inferred, scored, or processed values must never be invented in UI
  adapters.
- Provider errors, partial results, stale reads, degraded dependencies, retries,
  and cancellations must be visible and machine-readable.
- Fixtures must carry a persistent `fixture` badge and must be impossible to
  enable accidentally in production.
- All mutations are authenticated, authorized at object and property level,
  audited, idempotent where retryable, and durable before acknowledgement.
- All publish, release, export, and promotion paths are fail-closed.
- All high-risk AI operations preserve input, prompt/template version, model
  identity, parameters, safety results, output hashes, cost, actor, and
  timestamps.
- Destructive operations expose impact previews, require typed confirmation or
  step-up authorization proportionate to risk, and provide a documented recovery
  path.

### 2.3 UI and interaction thesis

The shared visual thesis is a calm, professional production surface: strong
typography, dense but readable information, restrained color, and one dominant
working canvas. The content plan is workspace first, context/inspector second,
status and evidence always visible, with no marketing hero inside an operator
tool. The interaction thesis is:

1. fast workspace-to-inspector transitions that preserve selection and focus;
2. visible, non-ornamental state transitions for jobs, reviews, and publishes;
3. keyboard-first command, comparison, and triage flows with reduced-motion
   equivalents.

- [ ] UX.1 Define a shared utility-first information architecture: global
      navigation, primary workspace, contextual inspector, and status region.
      Evidence: approved responsive wireframes for both archetypes.
  - [ ] UX.1.a Define the global navigation hierarchy, route labels, current
        location treatment, and cross-workbench exits. Evidence: annotated
        navigation map.
  - [ ] UX.1.b Define the primary workspace's canvas, editor, table, graph,
        timeline, or queue responsibilities for each workbench archetype.
        Evidence: archetype-by-region responsibility matrix.
  - [ ] UX.1.c Define contextual inspector opening, pinning, replacement,
        dismissal, and focus-return behavior. Evidence: annotated inspector
        interaction flow.
  - [ ] UX.1.d Define the persistent status region for save, sync, job, review,
        gate, publish, fixture, degraded, and connectivity state. Evidence:
        status-content specification.
  - [ ] UX.1.e Define how selection and working context survive transitions
        between navigation, workspace, inspector, and status regions. Evidence:
        state-preservation interaction cases.
  - [ ] UX.1.f Produce wide and compact wireframes for editor-centric and
        operations-centric archetypes. Evidence: four approved responsive
        wireframes.
- [ ] UX.2 Define density modes without card mosaics or decorative dashboards.
      Evidence: the design review confirms cards exist only when the card is the
      interaction.
  - [ ] UX.2.a Define comfortable and compact spacing, row-height, control-size,
        and information-priority tokens. Evidence: density token table.
  - [ ] UX.2.b Identify the default density for editors, tables, queues,
        inspectors, timelines, and touch-oriented review. Evidence:
        surface-density matrix.
  - [ ] UX.2.c Define which information may collapse, abbreviate, wrap, or move
        to an inspector at each density. Evidence: content-priority rules.
  - [ ] UX.2.d Define when a card is itself a selectable, draggable, comparable,
        or previewable object and prohibit decorative container cards elsewhere.
        Evidence: reviewed card-use inventory.
  - [ ] UX.2.e Preserve the user's density preference per workbench and provide
        an accessible reset. Evidence: preference contract and interaction test.
  - [ ] UX.2.f Validate both densities at 200% zoom and with long translated
        labels. Evidence: no-clipping visual test matrix.
- [ ] UX.3 Define one accent color and semantic status tokens that remain
      distinguishable without color. Evidence: token contrast tests pass.
  - [ ] UX.3.a Select the accent token and specify permitted emphasis,
        selection, focus, and action uses. Evidence: approved accent-use rules.
  - [ ] UX.3.b Define independent semantic tokens for informational, success,
        warning, failure, destructive, stale, fixture, degraded, and pending
        states. Evidence: token inventory.
  - [ ] UX.3.c Pair every semantic state with text, iconography, pattern, shape,
        or position so color is never the sole signal. Evidence: noncolor cue
        matrix.
  - [ ] UX.3.d Define foreground, background, border, focus, chart, and disabled
        token pairs for every supported theme. Evidence: theme token mappings.
  - [ ] UX.3.e Test text, nontext controls, focus indicators, and adjacent
        status colors against required contrast ratios. Evidence: automated
        contrast report with zero violations.
- [ ] UX.4 Define at most two type families and a type scale optimized for
      long-form editing, tables, graphs, code, and metadata. Evidence: rendered
      specimen passes at 200% zoom.
  - [ ] UX.4.a Choose the primary interface/editor family and any secondary
        monospace or specialist family with licensed font sources and fallbacks.
        Evidence: typography decision record.
  - [ ] UX.4.b Define display, heading, body, label, metadata, table, code, and
        numeric styles with size, weight, line height, and tracking. Evidence:
        typed token scale.
  - [ ] UX.4.c Define tabular-number, code, long-form prose, dense metadata, and
        graph-label treatments. Evidence: content-type specimen.
  - [ ] UX.4.d Validate fallbacks, missing glyphs, diacritics, bidirectional
        text, vertical-script metadata, and long translations. Evidence:
        multilingual glyph specimen.
  - [ ] UX.4.e Validate reflow, clipping, truncation disclosure, and reading
        order at 200% and 400% text zoom. Evidence: zoom test report.
- [ ] UX.5 Define focus, selection, hover, active, disabled, stale, fixture,
      degraded, conflict, and destructive states. Evidence: Storybook or
      equivalent state fixtures cover each state.
  - [ ] UX.5.a Define keyboard focus appearance, focus-visible behavior, focus
        order, and focus restoration. Evidence: focus-state fixtures and tests.
  - [ ] UX.5.b Define single, multiple, range, remote-user, and unavailable
        selection appearance. Evidence: selection-state fixtures.
  - [ ] UX.5.c Define hover behavior that adds no hover-only information or
        action. Evidence: pointer/no-pointer comparison fixtures.
  - [ ] UX.5.d Define pressed, active-route, active-tool, and in-progress action
        treatment. Evidence: active-state fixtures.
  - [ ] UX.5.e Define disabled versus read-only semantics, explanations, and
        keyboard exposure. Evidence: disabled/read-only fixtures and semantic
        assertions.
  - [ ] UX.5.f Define stale-data indicators, freshness details, and refresh or
        recompute actions. Evidence: stale-state fixtures.
  - [ ] UX.5.g Define persistent fixture labeling and prevent fixture state from
        visually resembling live production data. Evidence: fixture-state
        fixtures and production guard test.
  - [ ] UX.5.h Define degraded and unavailable states with affected capability,
        retained capability, cause, and recovery action. Evidence:
        degradation-state fixtures.
  - [ ] UX.5.i Define local/remote edit conflicts, comparison, resolution, and
        unresolved-state treatment. Evidence: conflict-state fixtures.
  - [ ] UX.5.j Define destructive action hierarchy, impact preview, typed or
        step-up confirmation, completion, and recovery treatment. Evidence:
        destructive-state fixtures.
- [ ] UX.6 Define three responsive arrangements: wide three-region workspace,
      compact two-region workspace, and single-region mobile/tablet review.
      Evidence: no horizontal page overflow at supported viewports.
  - [ ] UX.6.a Define wide-layout minimum dimensions and the allocation,
        resizing, and collapse rules for navigation, workspace, and inspector.
        Evidence: wide-layout specification.
  - [ ] UX.6.b Define compact-layout region pairing, inspector overlay/drawer
        behavior, and selection persistence. Evidence: compact-layout
        specification.
  - [ ] UX.6.c Define single-region review navigation, context breadcrumbs,
        inspector replacement, and back behavior. Evidence: single-region flow.
  - [ ] UX.6.d Define content-driven transition thresholds without assuming
        device labels or orientation. Evidence: breakpoint rationale and tests.
  - [ ] UX.6.e Define minimum and maximum pane sizes plus keyboard-accessible
        resizing and reset. Evidence: pane-resize interaction tests.
  - [ ] UX.6.f Test every arrangement with zoom, translated strings, persistent
        scrollbars, and software keyboards. Evidence: zero page-level overflow
        or obscured-action failures.
- [ ] UX.7 Define motion durations, easing, interruption, and
      `prefers-reduced-motion` behavior. Evidence: automated reduced-motion
      assertions cover all shared transitions.
  - [ ] UX.7.a Define duration tiers for immediate feedback, region transition,
        route transition, progress, and explanatory motion. Evidence: duration
        token table.
  - [ ] UX.7.b Define easing and direction rules that communicate spatial or
        lifecycle relationships without decorative movement. Evidence: approved
        motion examples.
  - [ ] UX.7.c Define interruption, reversal, cancellation, rapid-repeat, and
        focus behavior during animation. Evidence: interruption test cases.
  - [ ] UX.7.d Define reduced-motion replacements that preserve state change and
        orientation without translation, scale, parallax, or flashing. Evidence:
        transition-by-transition alternative matrix.
  - [ ] UX.7.e Enforce flashing, autoplay, and vestibular-safety limits.
        Evidence: automated motion-safety assertions.
  - [ ] UX.7.f Test every shared transition with reduced motion enabled before
        route load and toggled at runtime. Evidence: passing browser automation.
- [ ] UX.8 Define keyboard maps for navigation, selection, compare, command
      palette, save, undo, redo, comment, and escape. Evidence: collision audit
      covers browser, OS, screen-reader, and editor shortcuts.
  - [ ] UX.8.a Define landmark, region, tab, panel, and focus-mode navigation
        commands. Evidence: navigation key map and journey test.
  - [ ] UX.8.b Define single, range, additive, parent/child, and clear-selection
        commands. Evidence: selection key map and tests.
  - [ ] UX.8.c Define compare entry, side switching, synchronized navigation,
        difference traversal, and compare exit. Evidence: compare key map.
  - [ ] UX.8.d Define command-palette open, search, group navigation, execution,
        disabled-command explanation, and dismissal. Evidence: palette key map.
  - [ ] UX.8.e Define save, save-as-version, undo, redo, and history behavior
        across text, structured edits, and remote conflicts. Evidence:
        editing-command key map.
  - [ ] UX.8.f Define comment creation, anchored navigation, submit, cancel, and
        resolve shortcuts. Evidence: review-command key map.
  - [ ] UX.8.g Define layered Escape behavior for composition, menus, dialogs,
        modes, selections, inspectors, and routes. Evidence: ordered Escape
        contract.
  - [ ] UX.8.h Audit every proposed chord against supported browsers, operating
        systems, assistive technologies, international layouts, and embedded
        editors. Evidence: collision matrix with resolved conflicts.
  - [ ] UX.8.i Publish discoverable shortcut help and allow remapping where
        platform or accessibility conflicts remain. Evidence: help and
        preference interaction tests.
- [ ] UX.9 Define touch and pen alternatives for drag, resize, graph pan, and
      timeline trim. Evidence: every drag operation has a single-pointer,
      keyboard, and non-drag alternative.
  - [ ] UX.9.a Inventory every drag, reorder, resize, pan, scrub, trim, connect,
        lasso, and freehand interaction by surface. Evidence: complete gesture
        inventory.
  - [ ] UX.9.b Define tap/select plus explicit move controls for reordering and
        drag placement. Evidence: non-drag reorder flows.
  - [ ] UX.9.c Define numeric, stepper, preset, and reset controls for pane,
        object, and region resizing. Evidence: non-drag resize flows.
  - [ ] UX.9.d Define directional, minimap, search, and fit controls for graph,
        canvas, waveform, and timeline panning. Evidence: non-gesture navigation
        flows.
  - [ ] UX.9.e Define timecode/numeric in-out fields and frame/sample stepping
        for timeline and media trimming. Evidence: non-drag trim flow.
  - [ ] UX.9.f Define touch/pen hit targets, palm rejection, pressure/tilt
        applicability, cancellation, and accidental-gesture recovery. Evidence:
        input-modality specification.
  - [ ] UX.9.g Test every inventoried gesture with touch, pen, keyboard, switch,
        and a single pointer without path-based movement. Evidence: complete
        modality matrix.
- [ ] UX.10 Define plain-language utility copy for loading, empty, offline,
      unauthorized, not configured, failed, partial, stale, and retrying states.
      Evidence: copy review finds no marketing or false-success language.
  - [ ] UX.10.a Define loading copy that names the requested object or operation
        and does not imply success. Evidence: reviewed loading strings.
  - [ ] UX.10.b Define empty-state copy that distinguishes no records, no
        matches, no permission-visible records, and first-use setup. Evidence:
        reviewed empty-state variants.
  - [ ] UX.10.c Define offline copy that states readable, editable, queued, and
        unavailable capabilities plus reconnection behavior. Evidence: reviewed
        offline strings.
  - [ ] UX.10.d Define unauthorized and forbidden copy without disclosing hidden
        object existence or sensitive policy detail. Evidence: security-reviewed
        authorization strings.
  - [ ] UX.10.e Define not-configured copy that names the missing capability,
        authorized resolver, and safe next step. Evidence: reviewed
        configuration strings.
  - [ ] UX.10.f Define failure copy with affected action, retained data,
        correlation/support reference, retry eligibility, and recovery action.
        Evidence: reviewed failure strings.
  - [ ] UX.10.g Define partial-result copy that quantifies completed, failed,
        skipped, and still-running work. Evidence: reviewed partial-result
        strings.
  - [ ] UX.10.h Define stale-state copy with evidence age, invalidating change,
        impact, and refresh/recompute action. Evidence: reviewed stale strings.
  - [ ] UX.10.i Define retrying copy with attempt state, backoff or next-attempt
        timing, cancellation, and terminal-failure transition. Evidence:
        reviewed retry strings.
  - [ ] UX.10.j Test utility copy with screen readers, narrow layouts, long
        translations, and deliberately induced failures. Evidence: content QA
        matrix with no false-success language.

### 2.4 Current external standards baseline

The versions below are a planning baseline, not a license to hard-code a version
forever. Each domain must pin the implemented version, validate against the real
conformance tooling, and record its upgrade policy.

| Concern               | Baseline                                                                                                                                                                               |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Web accessibility     | [WCAG 2.2](https://www.w3.org/TR/WCAG22/) Level AA, including focus-not-obscured, dragging alternatives, target size, redundant entry, and accessible authentication                   |
| Media provenance      | [C2PA Content Credentials](https://spec.c2pa.org/specifications/) with signed manifests, validation, trust, hard/soft bindings, ingredient lineage, and UX guidance                    |
| 3D interchange        | [OpenUSD](https://openusd.org/) validation plus `usdchecker`; explicit profile/version per target                                                                                      |
| Education interchange | [QTI 3](https://www.1edtech.org/standards/qti/index), [QTI accessibility](https://www.1edtech.org/standards/qti/accessibility), LTI 1.3/LTI Advantage, OneRoster, Caliper, Open Badges |
| News exchange         | [IPTC NewsML-G2](https://iptc.org/standards/newsml-g2/) current supported release, Media Topics, correction/update metadata, rights, packages, and planning items                      |
| Music delivery        | DDEX ERN implemented profile; ITU-R BS.1770 loudness/true peak; [EBU R 128](https://tech.ebu.ch/loudness) where broadcast delivery applies                                             |
| API security          | [OWASP API Security Top 10 2023](https://owasp.org/API-Security/) plus repository security/privacy controls                                                                            |
| Observability         | [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/concepts/semantic-conventions/) for traces, metrics, logs, profiles, and resources                                  |

- [ ] STD.1 Record the exact WCAG 2.2 conformance target and supported
      assistive-technology/browser matrix. Evidence: accessibility test plan.
  - [ ] STD.1.a Record the required WCAG 2.2 level, included success criteria,
        allowed exemptions, and exception approval process. Evidence:
        conformance policy.
  - [ ] STD.1.b Enumerate supported browsers, engines, operating systems,
        viewport classes, input modalities, and zoom levels. Evidence: supported
        platform matrix.
  - [ ] STD.1.c Enumerate supported screen reader/browser/OS combinations and
        other required assistive technologies. Evidence: assistive-technology
        matrix.
  - [ ] STD.1.d Map automated, manual, and journey-level checks to each
        applicable success criterion. Evidence: criterion-to-test index.
  - [ ] STD.1.e Define severity, waiver, remediation deadline, and release-block
        rules for accessibility failures. Evidence: approved exception policy.
- [ ] STD.2 Record the supported C2PA specification and trust-list policy.
      Evidence: provenance compatibility decision record.
  - [ ] STD.2.a Pin supported C2PA and Content Credentials specification
        versions and compatibility behavior for older/newer claims. Evidence:
        version-support table.
  - [ ] STD.2.b Define accepted manifest stores, hard and soft bindings,
        ingredient assertions, actions, and custom assertion policy. Evidence:
        manifest profile.
  - [ ] STD.2.c Define signer identity, certificate-chain, timestamp,
        revocation, trust-list source, update, and offline-cache policy.
        Evidence: cryptographic trust policy.
  - [ ] STD.2.d Define valid, invalid, untrusted, unknown, revoked, expired,
        missing, and partially recoverable UI/API results. Evidence: validation
        result taxonomy.
  - [ ] STD.2.e Define signing-key custody, rotation, compromise, revocation,
        and audit ownership. Evidence: approved key operations record.
  - [ ] STD.2.f Validate the profile with official or independently compatible
        fixtures. Evidence: interoperability report.
- [ ] STD.3 Record the OpenUSD profile, schema set, resolver policy, and target
      DCC/engine version matrix. Evidence: interchange compatibility record.
  - [ ] STD.3.a Pin supported USD file formats, core version, composition
        features, layer conventions, and stage metadata. Evidence: OpenUSD
        profile.
  - [ ] STD.3.b Enumerate required, optional, extended, and forbidden schemas
        with version constraints. Evidence: schema compatibility table.
  - [ ] STD.3.c Define asset resolver schemes, search roots, package behavior,
        credentials, network access, and unresolved-asset failure semantics.
        Evidence: resolver policy.
  - [ ] STD.3.d Define units, axes, frame rates, color management, material,
        texture, animation, skeleton, and variant conventions. Evidence:
        semantic interchange rules.
  - [ ] STD.3.e Record import/export capability and known deltas for every
        target DCC, engine, plugin, and supported version. Evidence: target
        matrix.
  - [ ] STD.3.f Pin `usdchecker` and target-host validation commands plus
        accepted warning policy. Evidence: executable conformance plan.
  - [ ] STD.3.g Validate canonical round trips and unsupported-feature failures
        across the matrix. Evidence: checksummed compatibility fixtures and
        results.
- [ ] STD.4 Record QTI, LTI, OneRoster, Caliper, Open Badges, SCORM, and xAPI
      versions that Metis imports or exports. Evidence: standards matrix.
  - [ ] STD.4.a Record QTI version, profile, accessibility extensions,
        interaction support, import/export direction, and lossy mappings.
        Evidence: QTI support row and fixtures.
  - [ ] STD.4.b Record LTI Core and Advantage services, message flows, security
        profile, platform/tool roles, and unsupported claims. Evidence: LTI
        support row and launch fixtures.
  - [ ] STD.4.c Record OneRoster version, CSV/REST modes, resource coverage,
        identifier mapping, and delta-sync behavior. Evidence: OneRoster support
        row and fixtures.
  - [ ] STD.4.d Record Caliper version, profiles, event coverage, actor/entity
        privacy, transport, and retry semantics. Evidence: Caliper support row
        and fixtures.
  - [ ] STD.4.e Record Open Badges version, assertion/profile fields, evidence,
        signing/verification, revocation, and wallet interoperability. Evidence:
        Open Badges support row and fixtures.
  - [ ] STD.4.f Record SCORM versions, packaging/runtime coverage, sequencing
        limits, import/export direction, and compatibility policy. Evidence:
        SCORM support row and fixtures.
  - [ ] STD.4.g Record xAPI version, statement profiles,
        endpoint/authentication, identifier mapping, and delivery/retry
        behavior. Evidence: xAPI support row and fixtures.
  - [ ] STD.4.h Define precedence and conflict handling when the same course,
        roster, activity, result, or credential crosses multiple standards.
        Evidence: cross-standard mapping decisions.
  - [ ] STD.4.i Validate declared support using conformance tooling or
        independently produced provider/consumer fixtures. Evidence: complete
        standards conformance report.
- [ ] STD.5 Record the NewsML-G2 conformance level, catalog, vocabulary, and
      update/correction policy. Evidence: Veritas exchange profile.
  - [ ] STD.5.a Pin the supported NewsML-G2 release, conformance level, item
        types, package structures, and processing model. Evidence: versioned
        exchange profile.
  - [ ] STD.5.b Define provider, service, product, subject, genre, role,
        urgency, and Media Topics catalog/vocabulary mappings. Evidence: catalog
        mapping.
  - [ ] STD.5.c Define identifier, version, timestamp, language, location,
        relationship, and package-reference rules. Evidence: item identity and
        linkage rules.
  - [ ] STD.5.d Define rights, usage terms, embargo, source, credit, and
        confidential/protected metadata mappings. Evidence: rights mapping.
  - [ ] STD.5.e Define update, correction, replacement, cancellation,
        retraction, and tombstone semantics. Evidence: lifecycle transition
        profile.
  - [ ] STD.5.f Define planning-item ingest/export and assignment/calendar
        mappings. Evidence: planning interoperability profile.
  - [ ] STD.5.g Validate producer and consumer behavior with canonical,
        third-party, invalid, and version-transition fixtures. Evidence:
        conformance results.
- [ ] STD.6 Record the DDEX ERN profile and every distributor-specific delta.
      Evidence: Euterpe delivery compatibility matrix.
  - [ ] STD.6.a Pin the ERN version, implementation profile, release types,
        resource types, deal model, and allowed message flows. Evidence: DDEX
        profile.
  - [ ] STD.6.b Define party, contributor, rights-controller, identifier,
        territory, date, takedown, and update mappings. Evidence: semantic
        mapping table.
  - [ ] STD.6.c Define audio, image, lyric, metadata, checksum, naming,
        directory, and transport package rules. Evidence: delivery-package
        profile.
  - [ ] STD.6.d Inventory every distributor's required, optional, forbidden,
        transformed, and defaulted fields. Evidence: distributor delta rows.
  - [ ] STD.6.e Define validation, rejection, warning, partial-acceptance,
        redelivery, acknowledgement, and reconciliation behavior. Evidence:
        delivery state model.
  - [ ] STD.6.f Validate canonical and distributor-specific packages against
        available schemas, validators, and sandbox endpoints. Evidence:
        destination-level compatibility results.
- [ ] STD.7 Record loudness, peak, sample-rate, bit-depth, channel-layout, and
      codec targets per delivery destination. Evidence: audio delivery profile.
  - [ ] STD.7.a Inventory streaming, download, broadcast, cinema, immersive,
        social, preview, archive, and distributor destinations. Evidence:
        destination register.
  - [ ] STD.7.b Record integrated, short-term, and momentary loudness targets
        and measurement gating per destination. Evidence: loudness rows with
        units and tolerance.
  - [ ] STD.7.c Record true-peak and sample-peak ceilings plus measurement
        oversampling policy. Evidence: peak rows with units and tolerance.
  - [ ] STD.7.d Record sample rate, bit depth, dither, channel layout,
        bed/object, and downmix requirements. Evidence: PCM/layout rows.
  - [ ] STD.7.e Record codec, profile, bitrate or quality mode, container,
        metadata, gapless, and artwork requirements. Evidence: encoded-delivery
        rows.
  - [ ] STD.7.f Record normalization, limiting, resampling, encoding, and
        metadata-transformation rules without overwriting the source master.
        Evidence: derivative-generation policy.
  - [ ] STD.7.g Validate boundary, wrong-unit, clipping, silence, short-program,
        multichannel, and codec round-trip fixtures. Evidence: known-value
        delivery test report.
- [ ] STD.8 Map every workbench API to the OWASP API risk controls. Evidence:
      threat model and automated negative-test index.
  - [x] STD.8.a Inventory every public, internal, bridge, webhook, callback,
        streaming, and administrative API operation. Evidence: operation-level
        API register.
        <!-- workbench-api-register.json: 1,053 operations across two surfaces,
                                                                                                                                                                                                                                                                        source-derived (balanced-paren scan of every app.<method>() in the Oshun
                                                                                                                                                                                                                                                                        BFF + Electron ipcMain.handle + Euterpe Tauri #[command]). 755 HTTP (640
                                                                                                                                                                                                                                                                        administrative under /v1/admin/{isis 164, aja 163, yemaya 113, bellona 100,
                                                                                                                                                                                                                                                                        studio 100}; 115 public studio-authoring incl. 70 Tara + isis/metis/veritas/
                                                                                                                                                                                                                                                                        aja adapters) + 298 bridge (294 Yemaya Electron IPC incl. the Bellona
                                                                                                                                                                                                                                                                        bridge, 4 Euterpe Tauri commands). Classified by the STD.8.a 7-kind
                                                                                                                                                                                                                                                                        transport taxonomy; streaming=0/internal=0/webhook=0 are HONEST findings
                                                                                                                                                                                                                                                                        (no SSE producers or websocket upgrades; the 5 webhook-* paths are admin
                                                                                                                                                                                                                                                                        CONFIG APIs, not inbound receivers, so correctly administrative). Scope is
                                                                                                                                                                                                                                                                        the 7 V1 workbench domains + shared studio + Tara authoring; Hathor (not a
                                                                                                                                                                                                                                                                        V1 phase) and consumer /v1 routes excluded. generate-workbench-api-register.mjs
                                                                                                                                                                                                                                                                        + 8 tests, wired into inventory/verify + CI. STD.8.b-h (per-operation OWASP
                                                                                                                                                                                                                                                                        control mapping, negative tests, exception register) are security-analysis
                                                                                                                                                                                                                                                                        work and stay unchecked; the parent STD.8 stays [ ]. -->
  - [ ] STD.8.b Map broken object/property/function authorization controls and
        tenant boundaries to each operation. Evidence: authorization-control
        rows.
  - [ ] STD.8.c Map authentication, token/session lifecycle, sensitive business
        flow, and automated-abuse controls to each operation. Evidence:
        identity/abuse-control rows.
  - [ ] STD.8.d Map resource consumption, pagination, query complexity, payload,
        upload, storage, compute, queue, and spend controls. Evidence:
        consumption-control rows.
  - [ ] STD.8.e Map SSRF, unsafe downstream API consumption, redirect, DNS,
        egress, timeout, and response-validation controls. Evidence:
        remote-interaction rows.
  - [ ] STD.8.f Map inventory management, version retirement, documentation,
        environment exposure, configuration, and secret controls. Evidence:
        management-control rows.
  - [ ] STD.8.g Add operation-specific negative tests for every mapped control,
        including authenticated cross-tenant and property-level cases. Evidence:
        passing negative-test index.
  - [ ] STD.8.h Record residual risk, compensating control, owner, expiry, and
        retest date for every unautomated or unmet control. Evidence: approved
        exception register.
- [ ] STD.9 Define OpenTelemetry attribute names, cardinality limits, trace
      propagation, and sensitive-data exclusions. Evidence: observability schema
      review.
  - [ ] STD.9.a Define canonical resource attributes for service, deployment,
        domain, host, tenant-safe scope, worker, and provider identity.
        Evidence: resource schema.
  - [ ] STD.9.b Define span names and attributes for HTTP, RPC, database, queue,
        job, model/provider, render, publish, and bridge operations. Evidence:
        trace schema.
  - [ ] STD.9.c Define metric instruments, units, aggregation, histogram
        boundaries, and allowed dimensions. Evidence: metric schema.
  - [ ] STD.9.d Define structured log event names, severity, correlation, error,
        retry, and audit-link fields. Evidence: log schema.
  - [ ] STD.9.e Set cardinality budgets and prohibit raw IDs, unbounded labels,
        content, prompts, source text, and stack traces as dimensions. Evidence:
        attribute allow/deny rules.
  - [ ] STD.9.f Define propagation through browser, API, worker, queue,
        provider, desktop, bridge, and callback boundaries. Evidence:
        propagation map.
  - [ ] STD.9.g Define PII, learner, source-identity, biometric, credential,
        rights, prompt, and customer-content redaction or exclusion. Evidence:
        privacy-reviewed telemetry policy.
  - [ ] STD.9.h Define sampling, retention, regional storage, access, deletion,
        and support-safe export behavior. Evidence: telemetry lifecycle policy.
  - [ ] STD.9.i Validate schema enforcement, propagation continuity, cardinality
        bounds, and sensitive-data absence. Evidence: automated observability
        contract tests.
- [ ] STD.10 Establish quarterly standards-review ownership. Evidence: named
      owner, review cadence, compatibility policy, and backlog link.
  - [ ] STD.10.a Name the primary and backup owner for accessibility and
        assistive-technology support. Evidence: ownership register entry.
  - [ ] STD.10.b Name the primary and backup owner for C2PA provenance and trust
        policy. Evidence: ownership register entry.
  - [ ] STD.10.c Name the primary and backup owner for OpenUSD and target-host
        interoperability. Evidence: ownership register entry.
  - [ ] STD.10.d Name the primary and backup owner for education interchange
        standards. Evidence: ownership register entry.
  - [ ] STD.10.e Name the primary and backup owner for NewsML-G2 and newsroom
        exchange. Evidence: ownership register entry.
  - [ ] STD.10.f Name the primary and backup owner for DDEX and audio-delivery
        standards. Evidence: ownership register entry.
  - [ ] STD.10.g Name the primary and backup owner for OWASP API controls and
        OpenTelemetry conventions. Evidence: ownership register entries.
  - [ ] STD.10.h Define the quarterly review agenda, required evidence, change
        classification, compatibility window, deprecation notice, and emergency
        review trigger. Evidence: approved review procedure.
  - [ ] STD.10.i Link discovered changes to owned backlog items and verify
        closure at the next review. Evidence: standards-review issue template
        and cadence record.

---

### 2.5 Binding execution notes for the remaining work (added 2026-09-11)

These notes come from a review of every open cell against the tree at commit
`48f4bc8ddb`. They bind every remaining phase; a per-cell
`<!-- SPEC 2026-09-11 -->` comment below repeats only what is specific to that
cell. Nothing in this section flips a checkbox.

1. **Name the host before building or testing UI.** Every cell whose verb is
   build, run, exercise, or verify against a page names the host application,
   its port, and the route. Metis today: authoring UI is split between
   `apps/metis/web` (`/create/*` inside the LEARNER app, port 3020, login-only
   middleware with no role gate) and `apps/metis/admin` (hub, workspaces,
   psychometrics, credentials, integrations; port 3021), both backed by the
   Python service `apps/metis/service` (FastAPI, port 8000, env prefix
   `METIS_`). There is NO `/studio/metis` route in `apps/oshun/web`; the
   Section 1 table's "Oshun Studio workbench" host is unratified (M0.1.c is
   open) and `libs/oshun/navigation/src/release-scope.ts` scopes Metis to
   V1.2. Port 3020 is also `apps/oshun/admin`'s port: never run both. For
   every other domain the N0.1 host reconciliation cell (V0.1, Y0.1, E0.1,
   A0.1, B0.1) is a precondition for that domain's UI cells; when the choice
   is a product decision, draft the ADR as Proposed, build against the
   existing host as-is, and say so in the cell. Never build a third host.
   Veritas has no authoring host at all today; the Phase V header note
   records what exists instead.
2. **"Live backing" means no request interception.** The existing Metis
   Playwright suites are mocked: 64 of 65 specs in `apps/metis/web/e2e` and 20
   of 68 in `apps/metis/admin/e2e` (plus every `e2e/support/*-fixtures.ts`)
   intercept `**/api/**` with `page.route`, and `admin-fixtures.ts` signs in
   with a literal `test-admin-token`. A full-journey cell (M10.9–M10.12,
   M10.21, M10.22, and every later `N9.x` journey) passes only when its spec
   contains no `page.route`, no fixture token, and its assertions read state
   back from the service or the database. Reusing those fixtures satisfies a
   UI-state cell, never a journey cell.
3. **A skipped real-database test is not a pass.** The service's
   `*_real_database.py` and `*_postgres_integration.py` suites call
   `pytest.skip` when `METIS_TEST_DATABASE_URL` is unset and shell out to
   `apps/metis/service/.venv/bin/alembic upgrade head`. That `.venv` does not
   exist on this Mac (system python is 3.9; the service needs 3.11+). Recipe:
   `/opt/homebrew/bin/python3.11 -m venv apps/metis/service/.venv`, then
   `apps/metis/service/.venv/bin/pip install -e 'apps/metis/service[dev]'`,
   `docker compose -f docker/docker-compose.dev.yml up -d postgres minio redis`,
   and `METIS_TEST_DATABASE_URL=postgresql+asyncpg://oshun:oshun_dev@127.0.0.1:5432/<isolated db>`.
   Every evidence block reports skipped counts beside passed counts.
4. **Model-backed evaluation binds the cheap route, per feature.** The service
   defaults are wrong for tests: `ai_provider="openai"` (that account has no
   credits) and the floating `deepseek/deepseek-v4-flash`. Every evaluator has
   its own settings (`METIS_<FEATURE>_MODEL_PROVIDER`, `_MODEL`, `_BASE_URL`,
   `_API_KEY` for `SOURCE_CLAIM`, `STANDARDS_ALIGNMENT`, `COURSE_OUTLINE`,
   `LESSON_ASSIST`, and the rest in `config.py`): set provider `openrouter`,
   base URL `https://openrouter.ai/api/v1`, model
   `deepseek/deepseek-v4-flash-0731`, `OPENROUTER_PROVIDER_SORT=price`, and the
   key from `~/Desktop/workspace/drawer/env-master.env` through a throwaway
   `/tmp/<task>-env.sh`. Never `openai` or `anthropic`; never a key in the
   repo.
5. **Corpora and labels may not be invented.** A "licensed/consented
   representative held-out corpus" (M10.3.a, M10.23, I4.4.b, I9.6, and every
   later calibration cell) does not exist in this repository and cannot be
   written by an agent. An agent may build a repo-owned synthetic corpus whose
   labels derive from ground truth the fixture itself fixes (a known key, a
   planted contradiction, an executable worked solution), state in the
   manifest that it is synthetic, unrepresentative and un-adjudicated, and
   report thresholds as provisional. The parent stays open until a named human
   supplies or approves the corpus.
6. **External conformance receipts are one gate wherever they appear.**
   M1.11.g/M10.5.d (QTI: `1edtech-qti3-xsd`, `longsight-qti3-cli`, pinned in
   `libs/contracts/src/metis/qti-package.ts`), M1.12.g/M10.6 (LTI: the 1EdTech
   certification suite plus one independent LTI 1.3 platform), M1.13.g/M10.7
   (OneRoster, Caliper, Open Badges, SCORM, xAPI validators pinned in
   `interoperability-profiles.ts`). A receipt is the tool's own output over
   our artifact, retained under
   `evidence/v1-workbenches/metis-conformance/<tool>/` with the tool version
   and the artifact hash. Our own simulator, contract test, or schema is never
   a receipt. Install the tool when it is publicly installable and record the
   exact command and version; when it needs an account, a licence, or a hosted
   platform, record the attempt and leave the cell open. Docker-heavy tools (a
   Moodle or Canvas platform) run on the Linux dev server, not this Mac.
7. **Load, performance, and full Playwright sweeps obey the machine rules.**
   `memory_pressure | tail -2` (Mac) or `free -m` (Linux) before every heavy
   invocation. 68 admin specs and 65 web specs are sweeps, so
   `next build && next start`, never `next dev`; `--project=chromium` only
   (both configs also list firefox and webkit — do not run them); one Next
   server at a time; kill your PIDs at session end and verify with `lsof`.
   Load tests (M10.15 and every later `N.15`) run on the Linux dev server with
   a stop threshold written into the plan; a load run on this Mac is a recorded
   failed attempt, not evidence.
8. **WCAG 2.2, not the 2.1 helper.** `@oshun/testing/playwright`'s
   `axeAfterEach` runs `WCAG_21_AA_TAGS`. A cell that says 2.2 uses the kit's
   `@oshun/workbench-kit/axe-policy` and `wcag22-coverage` (S11.3) or passes
   tags including `wcag22aa` explicitly, and the evidence says which.
9. **Human approvals stay open, and so do their parents.** Every cell whose
   verb is obtain, ratify, approve, or sign (M0.5.e, M0.11.b/f, M0.12.g,
   M0.13.f, M0.14.g, M10.20, S0.12, S11.24.h, DEP.2, and the same cells in
   every later phase) gets a readiness packet with the approver's name left
   blank. A generator that reports "0 of N earned" needs no re-run; the missing
   input is a person.
10. **Re-measure a blocker comment before acting on it.** Several
    `<!-- blocker -->` notes predate later commits (M0.15.d's first vector
    changed on 2026-09-06). Read the code the note names, restate the current
    state with a date, then work.
11. **Structural invariants.** One checkbox per identifier (a rollup note is
    prose, never a second `- [x] <id>` line); a parent is `[ ]` while any
    child is `[ ]`; every cell carries an `Evidence:` clause; child letters are
    contiguous; an identifier at end of line is still an identifier.
    `node scripts/v1-workbenches/checklist-structure-lint.mjs` checks these
    and exits non-zero; run it before every commit that touches this file.
12. **Ground a phase before building it (added 2026-09-18).** Phases Y, V, E, A
    and B were enumerated, not decomposed: measured that day, more than half of
    the open cells in Y and E pack six or more slash-separated behaviours into
    one box, none names a file, package or route, and the evidence clause
    averages 2.4 words ("task tests."). Such a cell is not yet a task. The first
    work in a phase, after its N0 decisions, is a grounding pass over that
    phase only: for each cell, name the existing file, package, contract or
    route it changes (or say that none exists and where the new one goes), and
    split every cell whose behaviours have independent failure modes into
    children (`.a`, `.b`, …) that each carry their own specific `Evidence:`
    clause — the test file, the journey, the matrix row. Do it when the phase
    starts, never for a phase that is not next: a split made months ahead is
    written against code that will have moved. `TODOS/registry.json` lists
    these phases as deferred for that reason, so the task board does not offer
    their cells until the phase before them is done.
13. **Work order (added 2026-09-18).** The task board's queue for this file is:
    section 1 (BASE), section 4 (DEP), the Phase S residue, the Phase M residue,
    then Phase I starting at I0 — with I0.20, the crosswalk against
    `ISIS_CHROMA_RUNPOD_MVP_TODOS_2026-09-11.md`, before any I6–I9 cell. Sections
    2.3, 2.4 and 3 (UX.*, STD.*, CAP.*) are acceptance criteria that every
    domain phase must meet; they are checked when DONE.3 can be, not first, and
    a phase cell that satisfies one should name its id in its evidence.
    `node tools/todos-board.mjs --next v1-domain-workbenches` prints the next
    cells in that order.
14. **Who owns and who reviews (decision taken under the owner's delegation,
    2026-09-18).** This estate has one accountable person, the repository owner,
    and its delivery work is done by coding agents, so the words "owner",
    "independent review" and "signed" in a cell mean this. The accountable owner
    of every phase is the repository owner. The delivery owner of a cell is the
    agent session that holds its task on the Eve Task Board. The independent
    reviewer is a second agent session that did not author the work; its review
    is "signed" when it is recorded with the session, the date and the commit it
    read, through `./eve verify` once ETB.4.07 exists and as a dated review note
    in the cell until then. That is separation of duties for engineering review,
    and DEP.2.a–f are worked against it. It does not stand in for the named
    human approvals of rule 9 (legal, privacy, education, accessibility, product
    sign-off). Those stay open; once the packet exists the cell carries the
    human blocked tag, which takes it out of the agent queue and puts it on the
    owner's list in `TODOS/PARKED.md`. Seven such cells were tagged on
    2026-09-18 (M0.5.e, M0.11.b, M0.11.f, M0.12.g, M0.13.f, M0.14.g, M10.20).

---

## 3. Cross-cutting SOTA capability contract

Every applicable row below must have domain-specific implementation and proof.
Rejecting a row requires a P0 decision record and a compensating-control review.

### 3.1 Authoring and operations capability

- [ ] CAP.1 Frictionless capture supports keyboard command, paste, upload,
      import, deep link, and contextual creation where applicable. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.1.a Provide a discoverable keyboard command that opens capture with
        focus in the first required field. Evidence: per-domain shortcut and
        focus journey.
  - [ ] CAP.1.b Accept pasted text, URLs, files, and supported structured
        clipboard types without silently discarding formatting or provenance.
        Evidence: per-domain clipboard fixture matrix.
  - [ ] CAP.1.c Accept supported uploads with type, size, checksum, quarantine,
        progress, cancellation, and retry behavior. Evidence: per-domain upload
        journey and negative tests.
  - [ ] CAP.1.d Import each supported domain interchange format with preview,
        validation, conflict, partial-failure, and rollback behavior. Evidence:
        per-domain import conformance tests.
  - [ ] CAP.1.e Open capture from a safe deep link while preserving authorized
        target, tenant, and return context. Evidence: per-domain deep-link
        tests.
  - [ ] CAP.1.f Create an object contextually from its parent, selection,
        timeline position, graph node, source, or review item as applicable.
        Evidence: per-domain contextual-creation journeys.
  - [ ] CAP.1.g Record a reviewed N/A decision for each capture entry point a
        domain does not support. Evidence: capability matrix has no implicit
        omissions.
- [ ] CAP.2 Capture preserves source metadata, rights hints, locale, actor,
      tenant, timestamp, and import checksum. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.2.a Preserve original source identifier, URI/path, filename, media
        type, size, and supplied descriptive metadata. Evidence: source metadata
        round-trip tests.
  - [ ] CAP.2.b Preserve supplied license, copyright, attribution, embargo,
        consent, and usage-condition hints without treating hints as verified
        rights. Evidence: rights-hint fixtures and labeling assertions.
  - [ ] CAP.2.c Preserve declared and detected language, script, region, and
        direction independently. Evidence: locale round-trip fixtures.
  - [ ] CAP.2.d Record the authenticated actor and effective delegated actor at
        the capture boundary. Evidence: actor attribution assertions.
  - [ ] CAP.2.e Record tenant and authorized workspace/project context without
        accepting client-selected cross-tenant ownership. Evidence:
        tenant-spoofing negative tests.
  - [ ] CAP.2.f Record source-observed, client-supplied, server-received, and
        persisted timestamps with explicit semantics. Evidence: clock-controlled
        timestamp tests.
  - [ ] CAP.2.g Compute the import checksum from received bytes and retain the
        algorithm and value through later revisions. Evidence: known-byte hash
        and mutation tests.
- [ ] CAP.3 Structured editors use domain-typed blocks rather than an
      unvalidated generic JSON textarea. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.3.a Inventory the domain object and block types authors must create,
        inspect, reorder, nest, reference, or remove. Evidence: per-domain
        editor type register.
  - [ ] CAP.3.b Define a versioned schema and stable identifier for every
        editable block type. Evidence: machine-readable block contracts.
  - [ ] CAP.3.c Provide type-specific authoring controls, labels, help,
        constraints, and nonvisual semantics. Evidence: component fixtures and
        accessibility assertions per type.
  - [ ] CAP.3.d Validate field and cross-block invariants during editing and
        again at the service boundary. Evidence: shared invalid-fixture tests.
  - [ ] CAP.3.e Preserve unknown forward-compatible fields without exposing
        unsafe arbitrary mutation. Evidence: version-skew round-trip tests.
  - [ ] CAP.3.f Define block-version migration, unsupported-version read-only
        behavior, and lossless export. Evidence: migration and compatibility
        fixtures.
  - [ ] CAP.3.g Remove or confine raw JSON editing to an explicitly authorized
        developer tool with schema validation and audit. Evidence: route and
        authorization audit.
- [ ] CAP.4 Autosave is durable, observable, recoverable, and explicit about
      offline or failed writes. Evidence: per-domain capability-matrix row
      linked to implementation and current proof.
  - [ ] CAP.4.a Define save triggers, debounce, maximum unsaved duration,
        coalescing, and flush-on-navigation behavior. Evidence: clock-controlled
        save scheduling tests.
  - [ ] CAP.4.b Acknowledge saved state only after the authoritative durable
        write and required outbox/event transaction commit. Evidence: forced
        commit-failure tests.
  - [ ] CAP.4.c Display dirty, saving, saved-at, queued-offline, retrying,
        failed, conflict, and recovered states. Evidence: state-transition
        component and browser tests.
  - [ ] CAP.4.d Persist offline edits locally with tenant/user binding,
        encryption where required, size limits, and expiry. Evidence:
        offline-store security and quota tests.
  - [ ] CAP.4.e Reconcile queued offline edits against the current server
        revision without last-write-wins data loss. Evidence: reconnect conflict
        tests.
  - [ ] CAP.4.f Preserve failed payloads and expose retry, copy/export, discard,
        and support-reference actions. Evidence: failure-recovery journey.
  - [ ] CAP.4.g Recover unsaved work after refresh, crash, tab suspension, and
        expired authentication. Evidence: browser lifecycle recovery matrix.
  - [ ] CAP.4.h Emit bounded save latency, failure, retry, conflict, and
        recovery telemetry without content. Evidence: telemetry contract tests.
- [ ] CAP.5 Undo and redo cover local edits, structured operations, and conflict
      resolution without rewriting server history. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.5.a Define transaction boundaries for text entry, composition,
        paste, formatting, and multi-field local edits. Evidence: local
        undo-grouping tests.
  - [ ] CAP.5.b Implement inverse operations for add, remove, move, reorder,
        nest, link, split, merge, resize, and domain-specific structured edits.
        Evidence: operation-by-operation round-trip tests.
  - [ ] CAP.5.c Preserve selection, focus, viewport, and inspector context
        across undo and redo. Evidence: keyboard journey assertions.
  - [ ] CAP.5.d Define undo behavior after autosave, remote edits, validation,
        and server normalization. Evidence: save/remote boundary tests.
  - [ ] CAP.5.e Represent conflict-resolution choices as new reversible local
        operations rather than mutations of historical revisions. Evidence:
        conflict-resolution history tests.
  - [ ] CAP.5.f Clear or branch invalid undo history on restore, permission
        loss, schema migration, and destructive lifecycle transitions. Evidence:
        boundary-condition tests.
  - [ ] CAP.5.g Ensure redo never duplicates server commands or side effects
        such as publish, dispatch, render, or notification. Evidence:
        idempotency and side-effect tests.
- [ ] CAP.6 Version history supports immutable revisions, semantic diff,
      authorship, restore-as-new-version, and durable audit linkage. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.6.a Persist immutable revision identity, parent/base revision,
        schema version, actor, timestamp, reason, and content hash. Evidence:
        revision persistence assertions.
  - [ ] CAP.6.b Prevent update or deletion of retained revision content outside
        an explicit governed retention process. Evidence: authorization and
        database immutability tests.
  - [ ] CAP.6.c Produce domain-semantic diffs for fields, blocks, relations,
        ranges, media/time data, graphs, or artifacts as applicable. Evidence:
        known-change diff fixtures.
  - [ ] CAP.6.d Attribute each change to the authenticated or delegated actor
        without inferring authorship from current ownership. Evidence:
        multi-actor attribution tests.
  - [ ] CAP.6.e Restore a historical revision by creating a new head revision
        that references the restored source. Evidence: restore lineage tests.
  - [ ] CAP.6.f Link revision creation, restore, migration, merge, and lifecycle
        events to immutable audit records. Evidence: revision-to-audit integrity
        tests.
  - [ ] CAP.6.g Paginate and authorize revision history without leaking deleted,
        protected, or cross-tenant content. Evidence: history query negative
        tests.
- [ ] CAP.7 Concurrent editing has presence, selection awareness, merge rules,
      and an explicit conflict surface. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.7.a Define session identity, heartbeat, expiry, reconnect, duplicate
        tab, and privacy behavior for collaborator presence. Evidence:
        clock-controlled presence tests.
  - [ ] CAP.7.b Expose active collaborator identity and location only to
        authorized participants. Evidence: role and cross-tenant negative tests.
  - [ ] CAP.7.c Represent remote cursors, selections, object focus, and edit
        intent without color-only distinction. Evidence: concurrent-selection
        accessibility tests.
  - [ ] CAP.7.d Define merge semantics for text, typed blocks, ordered
        collections, references, metadata, and domain-specific structures.
        Evidence: deterministic concurrent-operation fixtures.
  - [ ] CAP.7.e Detect incompatible base revisions, delete/edit races, schema
        skew, and permission changes. Evidence: conflict-detection tests.
  - [ ] CAP.7.f Provide compare, choose, combine, duplicate-as-variant, retry,
        and preserve-for-support actions on the conflict surface. Evidence:
        conflict-resolution journey.
  - [ ] CAP.7.g Preserve unsent local work across reconnect and prevent remote
        updates from being overwritten silently. Evidence: network-partition
        browser tests.
- [ ] CAP.8 Divergence supports best-of-N generation, manual variants, stable
      comparison, score provenance, and recorded selection rationale. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.8.a Define which domain objects may branch, the permitted variant
        count, and whether variants share or copy dependencies. Evidence:
        per-domain divergence contract.
  - [ ] CAP.8.b Create best-of-N candidates with stable candidate and
        originating run identifiers. Evidence: deterministic run-to-variant
        linkage tests.
  - [ ] CAP.8.c Create, duplicate, rename, archive, and delete manual variants
        without mutating sibling history. Evidence: manual variant lifecycle
        tests.
  - [ ] CAP.8.d Freeze comparison inputs and render semantically aligned
        differences across supported variant types. Evidence: stable comparison
        fixtures.
  - [ ] CAP.8.e Attach raw score, units, evaluator/corpus/model version,
        threshold policy, confidence, and timestamp to each scored candidate.
        Evidence: score provenance assertions.
  - [ ] CAP.8.f Allow selection only among authorized, nonstale, gate-eligible
        candidates or surface the exact blocking reason. Evidence: selection
        guard tests.
  - [ ] CAP.8.g Record selected candidate, considered alternatives, actor,
        rationale, evidence, and supersession. Evidence: decision-record
        integration tests.
- [ ] CAP.9 Decision records capture considered options, evidence, actor,
      rationale, consequences, and reversal or supersession. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.9.a Assign stable identity, decision type, scope, owner, status, and
        effective version. Evidence: decision contract tests.
  - [ ] CAP.9.b Record every considered option, including retain-current-state,
        rejection rationale, and links to dependent decisions. Evidence:
        multi-option fixtures.
  - [ ] CAP.9.c Link immutable source, evaluation, review, experiment, and risk
        evidence with captured versions. Evidence: evidence-reference integrity
        tests.
  - [ ] CAP.9.d Record proposer, decider, reviewers, delegated authority, and
        decision timestamp. Evidence: actor/authority assertions.
  - [ ] CAP.9.e Record rationale, trade-offs, known consequences, affected
        audiences, and accepted residual risks. Evidence: required-field
        validation tests.
  - [ ] CAP.9.f Define proposed, under-review, approved, rejected, effective,
        superseded, and reversed transitions with authorization. Evidence:
        state-machine tests.
  - [ ] CAP.9.g Reverse or supersede through a new linked decision without
        rewriting the original record. Evidence: immutable lineage tests.
- [ ] CAP.10 Maturity or stage transitions use server-enforced guards with
      exact, actionable refusal reasons. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.10.a Define the versioned state machine, permitted transitions, and
        terminal states per domain object type. Evidence: machine-readable
        transition definitions.
  - [ ] CAP.10.b Define required fields, dependencies, gate results, approvals,
        rights, consent, and provenance for each transition. Evidence:
        transition precondition table.
  - [ ] CAP.10.c Enforce object, transition, property, tenant, and
        delegated-role authorization at the service boundary. Evidence:
        transition authorization tests.
  - [ ] CAP.10.d Evaluate guards against the authoritative current revision in a
        concurrency-safe transaction. Evidence: stale-client and race tests.
  - [ ] CAP.10.e Return stable refusal codes plus the exact failed conditions,
        affected references, and permitted remediation. Evidence: negative
        contract fixtures.
  - [ ] CAP.10.f Prevent clients from bypassing guards through direct field
        writes, bulk operations, imports, jobs, or compatibility APIs. Evidence:
        alternate-path negative tests.
  - [ ] CAP.10.g Audit requested, refused, completed, reversed, and overridden
        transitions with correlated evidence. Evidence: audit assertions.
- [ ] CAP.11 Review comments anchor to stable entities, fields, blocks, ranges,
      bars, frames, nodes, or timecodes as the domain requires. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.11.a Define stable anchor identity and revision semantics for whole
        entities and fields. Evidence: entity/field anchor contracts.
  - [ ] CAP.11.b Define stable block and text-range anchors that survive edits,
        splits, merges, and reordering where resolvable. Evidence: edit-rebasing
        fixtures.
  - [ ] CAP.11.c Define measure, beat, bar, sample, frame, and timecode anchors
        with units and frame-rate/time-base identity. Evidence: temporal anchor
        fixtures.
  - [ ] CAP.11.d Define graph node, edge, port, canvas object, scene object, and
        hierarchy anchors. Evidence: structural anchor fixtures.
  - [ ] CAP.11.e Detect deleted, replaced, migrated, permission-hidden, and
        ambiguously rebased anchors. Evidence: orphan-anchor taxonomy tests.
  - [ ] CAP.11.f Preserve the original excerpt, thumbnail, waveform, or
        structural context without leaking protected content. Evidence:
        context-snapshot security tests.
  - [ ] CAP.11.g Provide accessible navigation from comment to current anchor
        and from anchored content back to its comment thread. Evidence:
        keyboard/screen-reader review journey.
- [ ] CAP.12 Review supports assignments, due dates, quorum, required roles,
      abstention, conflict of interest, escalation, and delegated authority.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.12.a Assign individual reviewers and role/group queues with
        acceptance, reassignment, and workload visibility. Evidence: assignment
        lifecycle tests.
  - [ ] CAP.12.b Define due dates with time zone, reminders, overdue state,
        extension, and service-level policy. Evidence: clock-controlled due-date
        tests.
  - [ ] CAP.12.c Define quorum calculation for eligible, completed, recused,
        abstaining, replaced, and delegated reviewers. Evidence: known-value
        quorum tests.
  - [ ] CAP.12.d Require named specialist roles for domain, safety, rights,
        legal, accessibility, security, or release reviews where applicable.
        Evidence: missing-role gate tests.
  - [ ] CAP.12.e Record abstention reason without counting abstention as
        approval, rejection, or silent absence. Evidence: verdict aggregation
        tests.
  - [ ] CAP.12.f Declare, review, enforce, and audit conflicts of interest and
        recusals. Evidence: conflicted-reviewer negative tests.
  - [ ] CAP.12.g Escalate overdue, blocked, disputed, unavailable-reviewer, and
        policy-triggered reviews to the configured authority. Evidence:
        escalation routing tests.
  - [ ] CAP.12.h Delegate authority with explicit scope, start, expiry,
        delegator, delegate, nondelegable actions, and revocation. Evidence:
        delegation authorization tests.
- [ ] CAP.13 Search covers full text, structured fields, status, taxonomy,
      rights, quality, ownership, dates, and stable IDs. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.13.a Define indexed full-text fields, language analysis, stemming,
        phrase, typo, and protected-content behavior. Evidence: multilingual
        known-result tests.
  - [ ] CAP.13.b Index supported structured fields with typed equality, range,
        existence, and relationship filters. Evidence: field-query fixtures.
  - [ ] CAP.13.c Index lifecycle, review, gate, publish, job, stale, and failure
        statuses with current-state semantics. Evidence: status-filter tests.
  - [ ] CAP.13.d Index hierarchical taxonomy and aliases without collapsing
        distinct concepts. Evidence: taxonomy search fixtures.
  - [ ] CAP.13.e Index rights, consent, embargo, provenance, and usage
        eligibility without leaking protected detail. Evidence: rights-filter
        authorization tests.
  - [ ] CAP.13.f Index raw quality values, verdicts, evaluator versions, and
        freshness separately. Evidence: quality-query fixtures.
  - [ ] CAP.13.g Index owner, contributor, assignee, reviewer, team, and
        delegated actor with tenant-aware identity resolution. Evidence:
        ownership-query tests.
  - [ ] CAP.13.h Index created, updated, captured, effective, due, embargo,
        publish, and event dates with explicit time-zone semantics. Evidence:
        clock-boundary tests.
  - [ ] CAP.13.i Resolve exact stable IDs and supported external identifiers
        without fuzzy ambiguity. Evidence: exact-identifier tests.
  - [ ] CAP.13.j Apply object/property authorization, tombstone, retention, and
        tenant filters before counts, facets, snippets, and results. Evidence:
        search disclosure negative tests.
- [ ] CAP.14 Saved views preserve query, columns, sort, grouping, density, and
      sharing scope. Evidence: per-domain capability-matrix row linked to
      implementation and current proof.
  - [ ] CAP.14.a Persist the normalized full-text and structured query,
        including relative-date and current-user semantics. Evidence: query
        round-trip tests.
  - [ ] CAP.14.b Persist visible, hidden, ordered, pinned, sized, and
        type-specific column configuration. Evidence: column-layout round-trip
        tests.
  - [ ] CAP.14.c Persist multi-key sort with direction, null ordering, locale,
        and deterministic tie-breaker. Evidence: stable-sort tests.
  - [ ] CAP.14.d Persist grouping, expansion, aggregation, and group ordering
        without storing unauthorized result data. Evidence: grouping tests.
  - [ ] CAP.14.e Persist density and supported display mode independently from
        the result query. Evidence: presentation-preference tests.
  - [ ] CAP.14.f Define private, named-user, team, role, tenant, and public
        sharing scopes with owner-controlled permissions. Evidence:
        share-authorization tests.
  - [ ] CAP.14.g Handle renamed/removed fields, taxonomy changes, authorization
        loss, and schema version skew. Evidence: saved-view migration tests.
  - [ ] CAP.14.h Expose clone, rename, make-default, transfer, revoke, and
        delete operations with audit. Evidence: saved-view lifecycle tests.
- [ ] CAP.15 Bulk actions expose eligibility counts, skipped reasons, impact
      preview, progress, per-item results, cancellation, and safe retry.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.15.a Freeze the candidate set by stable IDs or a versioned query and
        display total selected versus currently matched. Evidence:
        concurrent-query selection tests.
  - [ ] CAP.15.b Evaluate object/property authorization, state, dependency,
        policy, and capacity eligibility per item before confirmation. Evidence:
        known-eligibility tests.
  - [ ] CAP.15.c Display eligible, ineligible, unknown, and already-satisfied
        counts with item-specific skipped reasons. Evidence: preview assertions.
  - [ ] CAP.15.d Preview resulting state, dependent impact, notifications,
        external side effects, cost, irreversibility, and rollback availability.
        Evidence: action-specific impact fixtures.
  - [ ] CAP.15.e Require confirmation proportional to impact and bind the
        approved action parameters to execution. Evidence: confirmation-tamper
        negative tests.
  - [ ] CAP.15.f Execute large batches as durable bounded-concurrency jobs with
        live completed/failed/skipped/pending progress. Evidence: batch job
        integration tests.
  - [ ] CAP.15.g Persist one result, reason, side-effect reference, and audit
        event per item. Evidence: per-item result completeness tests.
  - [ ] CAP.15.h Cancel unstarted work, report in-flight semantics, and preserve
        completed results without claiming atomic rollback. Evidence:
        cancellation tests.
  - [ ] CAP.15.i Retry only eligible failed items using stable idempotency keys
        and the original approved parameters. Evidence: partial-retry
        duplication tests.

### 3.2 Trust, AI, and provenance capability

- [ ] CAP.16 Every AI affordance is assistive by default and requires explicit
      human acceptance before it changes authored truth. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.16.a Inventory every generation, completion, rewrite, extraction,
        classification, scoring, recommendation, translation, and automation
        affordance. Evidence: per-domain AI affordance register.
  - [ ] CAP.16.b Define the authored source, proposed output, diff, confidence
        or uncertainty, provenance, and limitations visible before acceptance.
        Evidence: affordance-specific review contract.
  - [ ] CAP.16.c Store AI results as proposals, variants, or suggestions that do
        not mutate the authoritative revision on arrival. Evidence:
        persistence-state tests.
  - [ ] CAP.16.d Require an authorized human to accept each proposed change or
        an explicitly bounded group after reviewing its exact diff. Evidence:
        acceptance authorization and UI tests.
  - [ ] CAP.16.e Record accepted, partially accepted, edited-before-acceptance,
        rejected, expired, and superseded outcomes without treating absence as
        acceptance. Evidence: proposal lifecycle tests.
  - [ ] CAP.16.f Prevent automated publish, gate override, rights/consent
        attestation, source verification, grade, or other high-impact truth
        mutation. Evidence: prohibited-action negative tests.
  - [ ] CAP.16.g Link accepted content to the exact proposal and human actor
        while preserving later human edits independently. Evidence: authored
        lineage assertions.
- [ ] CAP.17 AI output is labeled with provider/model/template versions and
      carries the originating run ID through publication. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.17.a Persist provider, model/deployment identifier, immutable model
        version or digest, and runtime region with every run. Evidence: run
        provenance assertions.
  - [ ] CAP.17.b Persist system/policy prompt, template, tool, retrieval, and
        evaluator versions or immutable hashes. Evidence: template/tool
        provenance assertions.
  - [ ] CAP.17.c Persist request parameters, safety policy, seed when supported,
        dependency versions, start/end time, actor, tenant, and cost. Evidence:
        complete run-record tests.
  - [ ] CAP.17.d Assign one stable originating run ID to each output and
        derivative proposal. Evidence: run-to-output cardinality tests.
  - [ ] CAP.17.e Display AI origin and accessible provenance detail in editor,
        compare, review, history, search, and published-preview surfaces.
        Evidence: surface-by-surface labeling tests.
  - [ ] CAP.17.f Preserve the run reference through copy, merge, restore,
        translation, export, publication, correction, and retirement without
        misattributing later edits. Evidence: lineage-transition tests.
  - [ ] CAP.17.g Reject or visibly flag an output whose referenced run record is
        missing, inaccessible, or integrity-invalid. Evidence: broken-lineage
        negative tests.
- [ ] CAP.18 Prompt injection defenses fence user and retrieved content and test
      indirect injection through titles, metadata, attachments, and URLs.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.18.a Separate trusted system/policy instructions, developer
        templates, user requests, retrieved sources, tool results, and prior
        generated content in the execution contract. Evidence: role-tagged
        request fixtures.
  - [ ] CAP.18.b Mark retrieved and user-controlled content as untrusted data
        and prohibit it from granting tools, changing policy, or redefining
        output authority. Evidence: instruction-precedence tests.
  - [ ] CAP.18.c Minimize enabled tools and constrain each tool by typed
        arguments, allowlisted resources, authorization, tenant, and side-effect
        policy. Evidence: tool-capability matrix and negative tests.
  - [ ] CAP.18.d Normalize and inspect filenames, titles, descriptions, EXIF,
        document metadata, hidden text, captions, comments, and archive entries.
        Evidence: metadata injection corpus.
  - [ ] CAP.18.e Fetch URLs through the governed remote-fetch boundary and treat
        redirects, page text, headers, fragments, and linked attachments as
        untrusted. Evidence: URL injection and SSRF tests.
  - [ ] CAP.18.f Parse attachments in isolation with size/depth/type limits and
        retain source boundaries in the model input. Evidence: malicious
        attachment fixtures.
  - [ ] CAP.18.g Detect or constrain exfiltration requests for secrets,
        protected sources, learner/biometric data, unreleased assets, and
        cross-tenant context. Evidence: data-exfiltration negative tests.
  - [ ] CAP.18.h Test direct, indirect, encoded, multilingual, split-content,
        multimodal, tool-result, and multi-turn injections. Evidence: versioned
        adversarial evaluation report.
  - [ ] CAP.18.i Fail closed or require bounded human review when instruction
        trust cannot be determined. Evidence: ambiguous-trust behavior tests.
- [ ] CAP.19 Safety classifiers expose model version, thresholds, confidence,
      uncertainty, and appeal or override policy. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.19.a Define the exact safety classes, applicability, input units,
        possible raw outputs, and non-pass states. Evidence: classifier
        contract.
  - [ ] CAP.19.b Persist provider, model, immutable version/digest, corpus,
        preprocessing, and runtime version with each result. Evidence:
        classifier provenance tests.
  - [ ] CAP.19.c Separate raw class scores from versioned policy thresholds and
        composed verdicts. Evidence: recomputation tests under changed policy.
  - [ ] CAP.19.d Expose value, unit, threshold, confidence/calibration, and
        uncertainty or insufficient-data status. Evidence: API and accessible UI
        assertions.
  - [ ] CAP.19.e Define allow, warn, block, quarantine, human-review,
        unavailable, unsupported, and evaluator-failure behavior. Evidence:
        state taxonomy tests.
  - [ ] CAP.19.f Define who may appeal, the submission evidence, reviewer
        independence, deadline, status, and notification behavior. Evidence:
        appeal lifecycle tests.
  - [ ] CAP.19.g Define override authority, scope, reason, expiry, downstream
        impact, and non-overridable classes. Evidence: override policy tests.
  - [ ] CAP.19.h Calibrate thresholds and subgroup behavior on representative,
        licensed, versioned corpora before release. Evidence: calibration and
        fairness report.
- [ ] CAP.20 High-impact gate overrides require a reason, scoped authority,
      expiry where appropriate, and immutable audit. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.20.a Enumerate overrideable gates, nonoverrideable gates, allowable
        verdicts, and maximum risk scope. Evidence: approved override registry.
  - [ ] CAP.20.b Require a structured reason, supporting evidence, affected
        object/revision, intended consequence, and acknowledged residual risk.
        Evidence: request-validation tests.
  - [ ] CAP.20.c Enforce actor role, tenant, object, gate, target environment,
        lifecycle stage, and delegated-authority scope. Evidence: authorization
        matrix and negative tests.
  - [ ] CAP.20.d Require step-up authentication, separation of duties, quorum,
        or named specialist approval according to impact. Evidence: high-impact
        approval tests.
  - [ ] CAP.20.e Set effective time, expiry or one-use semantics, revocation,
        renewal, and automatic stale behavior. Evidence: clock-controlled tests.
  - [ ] CAP.20.f Bind an override to the exact gate result, inputs, evaluator,
        policy, object revision, and release target. Evidence: changed-input
        invalidation tests.
  - [ ] CAP.20.g Record request, evidence, approvals, refusals, activation, use,
        expiry, revocation, and downstream actions immutably. Evidence:
        append-only audit assertions.
  - [ ] CAP.20.h Surface active overrides and their scope in review, compare,
        publish, release, history, and incident views. Evidence: visibility
        journey tests.
- [ ] CAP.21 Source, rights, canon, consent, or provenance dependencies form a
      traversable impact graph. Evidence: per-domain capability-matrix row
      linked to implementation and current proof.
  - [ ] CAP.21.a Define stable node types for source objects, rights records,
        canon facts, consent grants, provenance claims, authored revisions,
        generated artifacts, gates, and releases. Evidence: graph schema.
  - [ ] CAP.21.b Define typed, directional, versioned edges such as
        derives-from, cites, depends-on, licenses, consents-to, contradicts,
        supersedes, and published-as. Evidence: edge vocabulary.
  - [ ] CAP.21.c Create graph edges transactionally when ingest, generation,
        edit, merge, gate, export, publish, or correction creates a dependency.
        Evidence: mutation-to-edge integration tests.
  - [ ] CAP.21.d Preserve exact dependency version or revision instead of
        resolving only to a mutable latest object. Evidence: historical graph
        tests.
  - [ ] CAP.21.e Traverse upstream ingredients and downstream dependents with
        bounded depth, cycle handling, pagination, and stable ordering.
        Evidence: graph traversal tests.
  - [ ] CAP.21.f Apply tenant, object, property, protected-source, and
        rights/consent authorization to nodes, edges, counts, and paths.
        Evidence: graph disclosure negative tests.
  - [ ] CAP.21.g Expose impact paths, affected gates/releases, and uncertainty
        without overstating missing or inaccessible edges. Evidence: impact-view
        tests.
  - [ ] CAP.21.h Reconcile orphaned, dangling, duplicated, and cycle-invalid
        dependencies. Evidence: graph integrity checker.
- [ ] CAP.22 Dependency changes automatically mark affected gates stale and
      enqueue or require recomputation. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.22.a Define which create, update, delete, revoke, expire, supersede,
        correction, and policy/version changes invalidate each gate type.
        Evidence: dependency-to-gate invalidation table.
  - [ ] CAP.22.b Emit a durable versioned dependency-change event in the same
        transaction as the authoritative mutation. Evidence: outbox atomicity
        tests.
  - [ ] CAP.22.c Traverse direct and transitive affected objects with cycle,
        depth, fan-out, and authorization-safe handling. Evidence: known-impact
        graph tests.
  - [ ] CAP.22.d Atomically mark affected gate results stale with invalidating
        event, old/new dependency versions, timestamp, and reason. Evidence:
        stale-record assertions.
  - [ ] CAP.22.e Enqueue idempotent recomputation where policy permits automatic
        work and record priority and cost/capacity decisions. Evidence: queue
        deduplication tests.
  - [ ] CAP.22.f Require explicit recomputation where consent, cost, policy,
        provider availability, or human judgment prevents automatic work.
        Evidence: manual-recompute state tests.
  - [ ] CAP.22.g Surface pending, running, failed, blocked, cancelled, and
        completed recomputation without restoring freshness prematurely.
        Evidence: recomputation lifecycle tests.
  - [ ] CAP.22.h Recover missed or failed invalidation through periodic
        reconciliation against dependency versions. Evidence: fault-injection
        and repair report.
- [ ] CAP.23 Publish uses current gate evidence; previously passing stale
      evidence never silently authorizes release. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.23.a Define the complete required-gate set for object type,
        revision, audience, channel, territory, environment, and release action.
        Evidence: publish gate policy.
  - [ ] CAP.23.b Resolve gate results by exact input object/revision, dependency
        versions, evaluator, corpus/model, policy/threshold, and target.
        Evidence: exact-match selection tests.
  - [ ] CAP.23.c Reject missing, stale, expired, overridden-out-of-scope,
        unavailable, failed, or unsupported evidence with stable reasons.
        Evidence: publish refusal fixtures.
  - [ ] CAP.23.d Recheck dependencies and gate freshness in the authoritative
        publish transaction or reservation boundary. Evidence:
        publish/invalidate race tests.
  - [ ] CAP.23.e Freeze the accepted gate-evidence set and hashes into the
        release candidate. Evidence: release manifest assertions.
  - [ ] CAP.23.f Invalidate or abort a reserved candidate when a required input
        changes before final publication. Evidence: mid-release change tests.
  - [ ] CAP.23.g Audit the exact evidence consulted, refusals, overrides, actor,
        target, and final outcome. Evidence: publish audit integrity tests.
- [ ] CAP.24 Artifact hashes are computed from stored bytes and verified again
      at import, promotion, publish, and retrieval boundaries. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.24.a Define approved hash algorithms, canonical identifier format,
        chunk/multipart behavior, and algorithm migration policy. Evidence:
        artifact-integrity profile.
  - [ ] CAP.24.b Compute the authoritative digest while ingesting or after
        reading the exact durably stored bytes, not client metadata. Evidence:
        client-hash spoofing tests.
  - [ ] CAP.24.c Store byte length, media type, storage version, hash algorithm,
        digest, and immutable blob reference transactionally. Evidence:
        metadata/blob consistency tests.
  - [ ] CAP.24.d Recompute and compare hashes at every external import and
        quarantine mismatches before parsing or use. Evidence: import corruption
        tests.
  - [ ] CAP.24.e Recompute and compare hashes when promoting between quarantine,
        draft, approved, release, or storage tiers. Evidence: promotion
        corruption tests.
  - [ ] CAP.24.f Recompute and compare hashes immediately before publish/export
        package signing or transfer. Evidence: prepublication tamper tests.
  - [ ] CAP.24.g Verify stored bytes during retrieval according to an explicit
        read-time, sampled-scrub, or full-scrub policy and fail closed on
        mismatch. Evidence: retrieval/scrub fault tests.
  - [ ] CAP.24.h Record corruption findings, affected derivatives/releases,
        quarantine, recovery source, repair, and revalidation. Evidence:
        integrity incident workflow test.
- [ ] CAP.25 Signed provenance records preserve ingredients, transformations,
      tools/models, actors, policies, and validation status. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.25.a Assign a signed provenance record to the exact artifact bytes
        and immutable artifact identifier. Evidence: artifact-manifest binding
        tests.
  - [ ] CAP.25.b Enumerate each ingredient with identifier, hash, relationship,
        origin, rights/consent hints, and accessible or redacted status.
        Evidence: multi-ingredient fixtures.
  - [ ] CAP.25.c Record ordered capture, edit, transform, generation, merge,
        validation, approval, export, and publish actions with parameters.
        Evidence: action-history fixtures.
  - [ ] CAP.25.d Record application, library, plugin, tool, provider, model,
        template, policy, and environment versions used by each action.
        Evidence: tool/model provenance assertions.
  - [ ] CAP.25.e Record authenticated actor, delegated role, organization, and
        automation identity without exposing protected identities. Evidence:
        actor provenance and redaction tests.
  - [ ] CAP.25.f Record safety, rights, consent, quality, and release policy
        versions plus result references without presenting verdicts as facts
        beyond their scope. Evidence: policy linkage tests.
  - [ ] CAP.25.g Sign with governed keys, timestamp where required, preserve the
        certificate chain, and support rotation/revocation. Evidence:
        cryptographic lifecycle tests.
  - [ ] CAP.25.h Validate binding, signature, trust, revocation, ingredient
        availability, assertion support, and policy status independently.
        Evidence: provenance validation matrix.
- [ ] CAP.26 Soft-binding and external-ledger recovery are tested when embedded
      provenance is stripped by a delivery channel. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.26.a Identify delivery channels and transformations that preserve,
        modify, or strip each embedded provenance mechanism. Evidence:
        channel-transformation matrix.
  - [ ] CAP.26.b Define privacy-reviewed soft bindings appropriate to media
        type, such as robust fingerprint, watermark, perceptual hash, or lookup
        hint. Evidence: binding design record.
  - [ ] CAP.26.c Register external-ledger records by immutable
        artifact/provenance identity with authorized lookup and retention.
        Evidence: ledger persistence and access tests.
  - [ ] CAP.26.d Recover the correct record from an unmodified derivative after
        embedded metadata removal. Evidence: positive recovery fixtures.
  - [ ] CAP.26.e Test resize, crop, transcode, recompress, remix, screenshot,
        re-record, channel normalization, and metadata rewriting as applicable.
        Evidence: transformation robustness matrix.
  - [ ] CAP.26.f Measure false positive, false negative, ambiguity, collision,
        and insufficient-evidence outcomes without claiming certainty. Evidence:
        known-corpus evaluation.
  - [ ] CAP.26.g Handle missing, multiple, revoked, private, deleted, and
        integrity-invalid ledger matches safely. Evidence: recovery negative
        tests.
  - [ ] CAP.26.h Display recovered, partially recovered, unresolved, and
        untrusted status with the method and limitations. Evidence: accessible
        recovery UX tests.
- [ ] CAP.27 Secrets, protected source identities, biometric data, learner
      records, and unreleased assets never enter prompts or telemetry by
      default. Evidence: per-domain capability-matrix row linked to
      implementation and current proof.
  - [ ] CAP.27.a Classify credential material, tokens, keys, connection strings,
        and secret-bearing configuration at input and logging boundaries.
        Evidence: secret classification tests.
  - [ ] CAP.27.b Classify protected source names, contact details, documents,
        relationship graphs, and identifying metadata. Evidence:
        protected-source fixtures.
  - [ ] CAP.27.c Classify raw and derived face, voice, body, motion, health, and
        other biometric identifiers. Evidence: biometric classification
        fixtures.
  - [ ] CAP.27.d Classify learner identity, enrollment, activity, assessment,
        accommodation, integrity, and credential records. Evidence:
        learner-record fixtures.
  - [ ] CAP.27.e Classify unreleased media, scripts, builds, research, models,
        product plans, and embargoed artifacts. Evidence: unreleased-asset
        fixtures.
  - [ ] CAP.27.f Exclude classified values from prompt text, retrieval context,
        tool arguments, model metadata, traces, metrics, logs, profiles, and
        support exports by default. Evidence: boundary-by-boundary leakage
        tests.
  - [ ] CAP.27.g Permit exceptional processing only through an approved
        provider, purpose, minimization, region, retention, consent/legal basis,
        and explicit actor action. Evidence: exception-policy enforcement tests.
  - [ ] CAP.27.h Redact before emission, detect post-emission leakage, block or
        quarantine unsafe runs, and trigger incident handling. Evidence:
        canary-secret and sensitive-data tests.
  - [ ] CAP.27.i Verify provider no-training/no-retention controls and telemetry
        exporter filtering rather than relying on UI claims. Evidence:
        configuration and integration audit.

### 3.3 Production engineering capability

- [ ] CAP.28 Contracts are versioned, validated at every trust boundary, and
      backward compatibility is tested. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.28.a Inventory public, interservice, event, job, persistence,
        provider, desktop/bridge, import/export, and UI contracts. Evidence:
        per-domain contract register.
  - [ ] CAP.28.b Assign canonical ownership, namespace, identifier, semantic
        version, and source-of-truth representation to each contract. Evidence:
        ownership/version declarations.
  - [ ] CAP.28.c Define required, optional, nullable, default, invariant, enum,
        unknown-field, and extension semantics. Evidence: machine-readable
        schemas and contract notes.
  - [ ] CAP.28.d Validate untrusted input before domain use and validate output
        before crossing each external or asynchronous boundary. Evidence:
        boundary validation tests.
  - [ ] CAP.28.e Define version negotiation, unsupported-version failure,
        deprecation, compatibility window, and consumer migration behavior.
        Evidence: compatibility policy.
  - [ ] CAP.28.f Retain canonical valid, invalid, boundary, stale, unauthorized,
        and old/new-version fixtures. Evidence: versioned fixture corpus.
  - [ ] CAP.28.g Test current provider against supported consumers and current
        consumer against supported provider versions. Evidence: bidirectional
        contract test matrix.
  - [ ] CAP.28.h Detect schema/source/generated-client drift in CI. Evidence:
        passing drift gate and deliberate-mutation test.
- [ ] CAP.29 Persistence uses explicit queryable columns for operationally
      important fields instead of opaque payload blobs. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.29.a Inventory fields used for authorization, tenancy, lifecycle,
        uniqueness, ordering, filtering, sorting, joining, retention, audit,
        freshness, and operations. Evidence: operational-field register.
  - [ ] CAP.29.b Map each operational field to a typed column with unit, format,
        nullability, default, constraint, and owning schema version. Evidence:
        logical-to-physical mapping.
  - [ ] CAP.29.c Normalize relationships that require referential integrity or
        independent lifecycle rather than embedding them in payload blobs.
        Evidence: relationship design review.
  - [ ] CAP.29.d Retain flexible payloads only for genuinely nonoperational,
        versioned extension data with size and schema limits. Evidence: approved
        payload-field inventory.
  - [ ] CAP.29.e Add indexes and query plans for supported access patterns
        without indexing sensitive content indiscriminately. Evidence:
        production-shape query-plan report.
  - [ ] CAP.29.f Backfill explicit columns from existing payloads with
        malformed, missing, conflicting, and version-skew handling. Evidence:
        rehearsal and reconciliation report.
  - [ ] CAP.29.g Prevent writes that update a blob without keeping explicit
        authoritative columns consistent. Evidence: dual-write invariant tests.
- [ ] CAP.30 Every mutable table carries tenant, actor, created/updated,
      revision, tombstone, and concurrency metadata as applicable. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.30.a Record authoritative tenant and owning scope with foreign-key
        or equivalent isolation constraints. Evidence: cross-tenant write/read
        negative tests.
  - [ ] CAP.30.b Record creating actor, last-mutating actor, delegated actor,
        and automation/service identity as applicable. Evidence: actor
        attribution tests.
  - [ ] CAP.30.c Record server-created and server-updated timestamps with
        consistent precision and clock source. Evidence: timestamp invariant
        tests.
  - [ ] CAP.30.d Record monotonic revision or concurrency token and enforce it
        on conflicting mutations. Evidence: stale-write rejection tests.
  - [ ] CAP.30.e Define tombstone timestamp, actor, reason, retention,
        visibility, restoration, and purge linkage. Evidence: soft-delete
        lifecycle tests.
  - [ ] CAP.30.f Distinguish tables for immutable events/revisions, global
        reference data, ephemeral leases, and mutable tenant data with explicit
        N/A rationale. Evidence: table-by-table applicability review.
  - [ ] CAP.30.g Enforce metadata through shared persistence boundaries and
        block bypass by bulk/import/job paths. Evidence: alternate-write-path
        tests.
- [ ] CAP.31 Migrations have forward, rollback-or-compensating, backfill,
      compatibility-window, and production-size rehearsal evidence. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.31.a Define the forward schema/data change, prerequisites,
        transactional boundaries, locks, and expected duration. Evidence:
        reviewed forward plan.
  - [ ] CAP.31.b Define exact rollback when safe or an explicit compensating
        migration when rollback would lose accepted writes. Evidence:
        rollback/compensation plan.
  - [ ] CAP.31.c Implement resumable, idempotent, observable, and rate-limited
        backfill with checkpoint and quarantine behavior. Evidence: interruption
        and resume tests.
  - [ ] CAP.31.d Define expand/migrate/contract compatibility windows for old
        and new application, worker, event, and schema versions. Evidence:
        version coexistence matrix.
  - [ ] CAP.31.e Validate preconditions and postconditions, row counts, hashes,
        invariants, nulls, duplicates, orphans, and sampled semantic results.
        Evidence: reconciliation queries.
  - [ ] CAP.31.f Rehearse forward, concurrent traffic, backfill, rollback or
        compensation, and redeploy on production-size representative data.
        Evidence: timed rehearsal report.
  - [ ] CAP.31.g Define monitoring, abort thresholds, operator commands,
        ownership, communication, and evidence capture for production execution.
        Evidence: migration runbook.
- [ ] CAP.32 Retryable commands use idempotency keys and return the original
      result rather than creating duplicates. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.32.a Inventory retryable create, update, transition, job, publish,
        provider, webhook, import/export, and bulk commands. Evidence:
        idempotent-command register.
  - [ ] CAP.32.b Define idempotency-key scope by tenant, actor/client,
        operation, target, and compatibility version. Evidence: key-scope
        contract.
  - [ ] CAP.32.c Persist key, canonical request hash, state, authoritative
        result reference, error class, and expiry atomically with command
        acceptance. Evidence: persistence integration tests.
  - [ ] CAP.32.d Return the original success or still-running result for an
        identical request without repeating side effects. Evidence: concurrent
        duplicate-request tests.
  - [ ] CAP.32.e Reject reuse of a key with different parameters, target,
        identity, or contract version. Evidence: key-reuse negative tests.
  - [ ] CAP.32.f Define behavior for transient failure before acceptance,
        terminal failure after acceptance, timeout with unknown outcome, and
        expired records. Evidence: failure-state fixtures.
  - [ ] CAP.32.g Reconcile uncertain provider or downstream outcomes before
        retrying an irreversible side effect. Evidence: fault-injection tests.
- [ ] CAP.33 Multi-system writes use an outbox, inbox, or documented saga with
      replay and poison-message handling. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.33.a Inventory mutations that span a database, blob store, queue,
        search index, provider, ledger, notification, or another domain.
        Evidence: multi-system mutation register.
  - [ ] CAP.33.b Choose transactional outbox, idempotent inbox, orchestrated
        saga, choreographed saga, or explicit reconciliation per mutation.
        Evidence: pattern decision rows.
  - [ ] CAP.33.c Persist authoritative mutation and outbound intent atomically
        where a local transaction exists. Evidence: forced-rollback tests.
  - [ ] CAP.33.d Deduplicate inbound messages by tenant, producer, event or
        command identity, and version. Evidence: duplicate-delivery tests.
  - [ ] CAP.33.e Define step order, preconditions, timeouts, retries,
        compensation, irreversible steps, and point of no return. Evidence: saga
        state-machine tests.
  - [ ] CAP.33.f Preserve ordering or explicitly tolerate reordering,
        duplication, late delivery, and concurrent consumers. Evidence:
        delivery-order fault tests.
  - [ ] CAP.33.g Quarantine poison messages with error class, payload-safe
        diagnostics, retry history, ownership, and authorized replay. Evidence:
        dead-letter journey.
  - [ ] CAP.33.h Reconcile missing, stuck, duplicated, and partially compensated
        work against authoritative state. Evidence: reconciliation repair tests.
- [ ] CAP.34 Long-running work uses durable jobs with heartbeat, lease,
      cancellation, retry policy, checkpoint, and dead-letter semantics.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.34.a Define job type, versioned parameters, immutable input
        references, priority, queue, tenant, actor, cost estimate, and
        idempotency scope. Evidence: job contract.
  - [ ] CAP.34.b Persist queued, leased, running, waiting, cancelling,
        cancelled, retrying, failed, dead-lettered, and succeeded transitions.
        Evidence: job state-machine tests.
  - [ ] CAP.34.c Acquire exclusive time-bounded leases and renew heartbeats with
        worker identity and fencing token. Evidence: competing-worker and
        expired-lease tests.
  - [ ] CAP.34.d Define cooperative cancellation checkpoints, noncancellable
        regions, external cancellation, compensation, and terminal result.
        Evidence: stage-by-stage cancellation tests.
  - [ ] CAP.34.e Classify retryable, terminal, policy, authorization, invalid,
        provider, resource, timeout, and unknown errors. Evidence:
        error-to-retry table and tests.
  - [ ] CAP.34.f Persist progress and resumable checkpoints without reporting
        uncommitted work as complete. Evidence: crash/resume fault tests.
  - [ ] CAP.34.g Apply bounded exponential backoff, jitter, maximum attempts,
        age deadline, capacity, and spend limits. Evidence: deterministic
        retry-schedule tests.
  - [ ] CAP.34.h Dead-letter terminal/exhausted jobs with safe diagnostics,
        retained inputs, owner, remediation, and authorized replay or abandon.
        Evidence: dead-letter operations test.
  - [ ] CAP.34.i Reconcile orphan leases, silent workers, missing results, and
        side effects with unknown outcomes. Evidence: worker-loss repair test.
- [ ] CAP.35 APIs paginate deterministically and define stable sort and cursor
      semantics under concurrent mutation. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.35.a Define default and maximum page sizes plus server behavior for
        zero, negative, excessive, or malformed values. Evidence: limit boundary
        tests.
  - [ ] CAP.35.b Define an explicit stable default sort and permitted
        client-selected sorts with deterministic unique tie-breakers. Evidence:
        equal-key fixtures.
  - [ ] CAP.35.c Encode sort, filter, direction, position, schema/version, and
        authorization-safe snapshot context in an opaque integrity-protected
        cursor. Evidence: cursor contract tests.
  - [ ] CAP.35.d Reject a cursor reused with incompatible filters, sort,
        identity, tenant, API version, or expired snapshot. Evidence: cursor
        misuse negative tests.
  - [ ] CAP.35.e Define insertion, update, deletion, tombstone, and permission
        change semantics between page requests. Evidence: concurrent-mutation
        pagination tests.
  - [ ] CAP.35.f Return next/previous or continuation metadata consistently
        without expensive or disclosure-prone total counts by default. Evidence:
        response contract tests.
  - [ ] CAP.35.g Verify no duplicate or skipped records within the declared
        consistency model. Evidence: randomized pagination property tests.
- [ ] CAP.36 API rate, concurrency, payload-size, storage, compute, and spend
      limits are enforced per actor and tenant. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.36.a Define request-rate and burst budgets by actor, tenant,
        operation, client class, and trust tier. Evidence: rate policy.
  - [ ] CAP.36.b Define simultaneous request, job, stream, provider, render,
        bridge-session, and expensive-query limits. Evidence: concurrency
        policy.
  - [ ] CAP.36.c Define compressed and decompressed request, response, upload,
        archive-entry, field, and batch-size limits. Evidence: payload policy.
  - [ ] CAP.36.d Define object count, metadata, blob, revision, cache, log, and
        retained-output storage quotas. Evidence: storage policy.
  - [ ] CAP.36.e Define CPU, memory, GPU, wall-time, query complexity, token,
        frame/sample, and provider-use budgets. Evidence: compute policy.
  - [ ] CAP.36.f Define estimated, reserved, actual, daily/monthly, and
        exceptional spend limits with currency and attribution. Evidence: spend
        policy.
  - [ ] CAP.36.g Enforce limits at the earliest authoritative boundary and again
        before delayed or externally priced work. Evidence: bypass and
        reservation-race tests.
  - [ ] CAP.36.h Return stable quota category, current use where safe, reset or
        retry time, remediation, and correlation metadata. Evidence:
        limit-response fixtures.
  - [ ] CAP.36.i Prevent actors from evading tenant budgets and tenants from
        consuming another tenant's capacity. Evidence: identity/tenant abuse
        tests.
- [ ] CAP.37 Remote fetches defend against SSRF, DNS rebinding, redirect
      escapes, oversized content, decompression bombs, and malicious media.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.37.a Parse and canonicalize scheme, user info, hostname, port,
        Unicode/IDNA, IP literals, and path before policy evaluation. Evidence:
        URL parser adversarial fixtures.
  - [ ] CAP.37.b Allowlist schemes and ports and block loopback, link-local,
        private, reserved, multicast, metadata, unix/file, and internal service
        destinations. Evidence: address-range negative tests.
  - [ ] CAP.37.c Resolve through a controlled resolver, validate every returned
        address, pin the connection target, and revalidate on reconnect.
        Evidence: DNS rebinding tests.
  - [ ] CAP.37.d Disable redirects by default or validate every redirect hop,
        target, scheme, port, credential, and maximum count. Evidence:
        redirect-escape tests.
  - [ ] CAP.37.e Apply connect, TLS, header, idle, body, total, retry, and
        bandwidth limits without trusting `Content-Length`. Evidence: slow/large
        response tests.
  - [ ] CAP.37.f Bound compressed size, decompressed size, ratio, archive
        entries, nesting depth, dimensions, frames, duration, and parser
        resources. Evidence: decompression and media-bomb corpus.
  - [ ] CAP.37.g Sniff actual content type, quarantine mismatches, and parse
        untrusted media in isolated constrained workers. Evidence: polyglot and
        malformed-media tests.
  - [ ] CAP.37.h Strip outbound credentials and sensitive headers, restrict
        egress, and prevent response data from becoming privileged instructions.
        Evidence: credential-leak and injection tests.
  - [ ] CAP.37.i Log safe policy decisions and correlation metadata without
        leaking signed URLs, credentials, protected locations, or content.
        Evidence: telemetry redaction assertions.
- [ ] CAP.38 Uploads use quarantine, content sniffing, malware/moderation scans,
      checksum verification, retention, and orphan cleanup. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.38.a Create a tenant-bound upload intent with allowed types, maximum
        bytes, part count, expiry, actor, destination purpose, and idempotency
        key. Evidence: upload-intent contract tests.
  - [ ] CAP.38.b Write received bytes only to nonpublic quarantine and prevent
        downstream parsing, preview, indexing, or serving before promotion.
        Evidence: quarantine isolation tests.
  - [ ] CAP.38.c Enforce streaming byte/part limits and compute server-observed
        size and cryptographic checksum. Evidence: truncation/oversize/hash
        tests.
  - [ ] CAP.38.d Sniff actual type and structure, compare declared metadata, and
        reject polyglot, malformed, executable, or unsupported content.
        Evidence: adversarial type corpus.
  - [ ] CAP.38.e Run malware, content-safety/moderation, archive, and
        domain-specific validation with versioned results. Evidence: scanner
        result matrix.
  - [ ] CAP.38.f Promote only when every required scan is current and passing,
        binding the promoted blob to the scanned hash. Evidence: scan/promote
        race tests.
  - [ ] CAP.38.g Define quarantine, rejected, infected, appealed, expired, and
        retained-for-incident access and deletion policy. Evidence: retention
        lifecycle tests.
  - [ ] CAP.38.h Reconcile and remove abandoned multipart uploads, orphan
        quarantine blobs, stale intents, and failed promotions without deleting
        referenced evidence. Evidence: cleanup reconciliation tests.
- [ ] CAP.39 Caches include tenant, authorization, schema, and dependency
      versions in their keys and expose invalidation reasons. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.39.a Inventory browser, application, HTTP/CDN, query, object,
        evaluation, render, provider, and search caches. Evidence: cache
        register.
  - [ ] CAP.39.b Include tenant and owning scope in keys or prove the cached
        data is globally public and immutable. Evidence: cross-tenant cache
        tests.
  - [ ] CAP.39.c Include actor/role/policy/permission version or cache only data
        whose representation is authorization-independent. Evidence:
        permission-change tests.
  - [ ] CAP.39.d Include contract/schema, object revision, dependency,
        evaluator, model/corpus, policy/threshold, locale, and target versions
        as applicable. Evidence: stale-version cache tests.
  - [ ] CAP.39.e Define TTL, stale-while-revalidate, negative caching,
        unavailable/failure caching, and stampede control. Evidence: cache-state
        timing tests.
  - [ ] CAP.39.f Emit durable invalidation from authoritative mutations and
        support bounded tag/key eviction. Evidence: mutation-to-invalidation
        tests.
  - [ ] CAP.39.g Surface hit/miss, stored/served age, reason, dependency
        versions, invalidation source, and refresh outcome without sensitive key
        data. Evidence: cache diagnostic assertions.
  - [ ] CAP.39.h Reconcile cache divergence and prove permission revocation,
        deletion, rights/consent change, and emergency purge take effect within
        policy. Evidence: emergency-invalidation tests.
- [ ] CAP.40 Feature flags have owner, purpose, audience, default, expiry,
      rollback behavior, and removal task. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.40.a Assign a stable flag key, type, owning team/person, creation
        date, linked decision, and change authority. Evidence: flag registry.
  - [ ] CAP.40.b State the temporary purpose, hypothesis or migration, affected
        capability, success criteria, and non-goals. Evidence: reviewed purpose
        fields.
  - [ ] CAP.40.c Define eligible environments, tenants, roles, users, cohorts,
        devices, regions, and dependency conditions. Evidence: targeting
        contract and isolation tests.
  - [ ] CAP.40.d Define safe code/config default, missing-service behavior,
        evaluation-failure behavior, and new-tenant behavior. Evidence:
        default/failure tests.
  - [ ] CAP.40.e Set rollout start, review date, expiry, maximum lifetime, and
        alert before expiry. Evidence: clock-controlled expiry tests.
  - [ ] CAP.40.f Define rollback trigger, kill-switch authority, propagation
        budget, cached-evaluation invalidation, and state/data compatibility.
        Evidence: rollback rehearsal.
  - [ ] CAP.40.g Audit flag evaluations and changes with bounded cardinality and
        no sensitive targeting leakage. Evidence: telemetry/audit tests.
  - [ ] CAP.40.h Create and link the code, schema, data, test, documentation,
        and registry removal task at flag creation. Evidence: no flag lacks
        removal criteria.
  - [ ] CAP.40.i Block expired permanent-unknown flags and contradictory nested
        flag combinations in CI. Evidence: flag lifecycle lint tests.

### 3.4 Accessibility, localization, and resilient UX capability

- [ ] CAP.41 Every route passes automated WCAG 2.2 AA checks in each supported
      theme, density, viewport, and locale class. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.41.a Derive the reachable route and critical-state matrix from the
        authoritative route inventory. Evidence: no route/state is omitted or
        implicitly sampled.
  - [ ] CAP.41.b Run the configured rules in every supported light, dark,
        high-contrast, or branded theme. Evidence: theme-indexed scan results.
  - [ ] CAP.41.c Run each route in comfortable, compact, touch, and other
        supported density modes. Evidence: density-indexed scan results.
  - [ ] CAP.41.d Run at minimum supported narrow, medium, wide, zoomed/reflow,
        and orientation-dependent viewports. Evidence: viewport-indexed results.
  - [ ] CAP.41.e Run representative Latin, long-string, bidirectional, CJK or
        other supported locale/script classes. Evidence: locale-indexed results.
  - [ ] CAP.41.f Scan loaded, empty, validation-error, unavailable, conflict,
        dialog/drawer, and destructive-confirmation states where present.
        Evidence: state-indexed results.
  - [ ] CAP.41.g Triage every violation to a fix, explicit duplicate, tool false
        positive, or approved temporary waiver with owner and expiry. Evidence:
        zero unexplained findings.
  - [ ] CAP.41.h Keep the matrix in browser automation and fail releases on new
        unwaived AA violations. Evidence: deliberate-regression gate test.
- [ ] CAP.42 Keyboard-only users can complete every critical journey. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.42.a Enumerate critical create/edit, review, gate, bulk, recover,
        publish/release, search, and administrative journeys per workbench.
        Evidence: approved keyboard journey inventory.
  - [ ] CAP.42.b Reach every actionable control through logical focus order
        without pointer, touch, voice, or direct accessibility-tree invocation.
        Evidence: focus traversal tests.
  - [ ] CAP.42.c Operate menus, dialogs, tabs, trees, grids, lists, editors,
        sliders, splitters, canvases, graphs, and timelines with documented
        keys. Evidence: widget-pattern interaction tests.
  - [ ] CAP.42.d Provide keyboard equivalents for drag, lasso, pan, zoom, trim,
        resize, reorder, connect, scrub, and spatial placement. Evidence:
        nonpointer journey tests.
  - [ ] CAP.42.e Preserve visible focus, selection, scroll position, working
        context, and escape route throughout each journey. Evidence: step-level
        assertions.
  - [ ] CAP.42.f Avoid traps except intentional modal focus with reachable
        dismissal and correct focus return. Evidence: trap and restoration
        tests.
  - [ ] CAP.42.g Test supported browser/OS/editor/screen-reader shortcut
        collisions and international keyboard layouts. Evidence: collision
        matrix.
- [ ] CAP.43 Screen-reader announcements cover validation, save, job progress,
      gate changes, comments, conflict, publish, and destructive results.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.43.a Announce field and form validation at the affected control with
        error summary, count, and focus/navigation behavior. Evidence:
        validation announcement tests.
  - [ ] CAP.43.b Announce dirty, saving, saved, queued, retrying, failed,
        conflict, and recovered save states without excessive repetition.
        Evidence: save-state speech log.
  - [ ] CAP.43.c Announce job queued/running stage, meaningful progress,
        cancellation, retry, completion, partial result, and failure. Evidence:
        job speech log.
  - [ ] CAP.43.d Announce gate pending, running, stale, pass, warn, fail,
        unavailable, overridden, and recomputation changes. Evidence: gate
        speech log.
  - [ ] CAP.43.e Announce new comment, reply, mention, assignment, resolve,
        reopen, and anchor navigation in review context. Evidence: comment
        speech log.
  - [ ] CAP.43.f Announce edit conflict, affected content, comparison entry,
        resolution result, and preserved local work. Evidence: conflict speech
        log.
  - [ ] CAP.43.g Announce publish/release preparation, refusal, approval,
        dispatch, completion, partial delivery, correction, rollback, and
        failure. Evidence: release speech log.
  - [ ] CAP.43.h Announce destructive impact, confirmation requirements,
        progress, per-item result, recovery, and irreversible completion.
        Evidence: destructive-action speech log.
  - [ ] CAP.43.i Validate announcement priority, deduplication, interruption,
        language, accessible names, and real screen-reader behavior. Evidence:
        assistive-technology test matrix.
- [ ] CAP.44 Graphs, canvases, timelines, waveforms, 3D previews, and charts
      provide a semantically equivalent navigable data view. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.44.a Inventory every non-DOM visual surface and define the domain
        facts, relationships, states, and operations it communicates. Evidence:
        visual-to-semantic content map.
  - [ ] CAP.44.b Provide a structured table/tree/list/transcript view with the
        same entities, values, units, ordering, hierarchy, and status. Evidence:
        semantic-view component tests.
  - [ ] CAP.44.c Synchronize selection, focus, filters, time/range, zoomed
        context, and active object bidirectionally between visual and semantic
        views. Evidence: synchronization tests.
  - [ ] CAP.44.d Provide nonvisual create, edit, connect, reorder, position,
        trim, annotate, compare, and delete operations where the visual surface
        is editable. Evidence: equivalent-operation journeys.
  - [ ] CAP.44.e Describe color, shape, line, texture, spatial, animation,
        waveform, and depth encodings in text and expose exact raw values.
        Evidence: encoding legend and accessible descriptions.
  - [ ] CAP.44.f Preserve performance through pagination, virtualization, range
        selection, and on-demand detail without hiding total/position semantics.
        Evidence: large-dataset accessibility tests.
  - [ ] CAP.44.g Verify the semantic view supports the complete critical journey
        with keyboard and screen reader. Evidence: nonvisual end-to-end journey.
- [ ] CAP.45 Audio and video have captions, transcripts, visual alerts, and
      non-audio alternatives where content permits. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.45.a Inventory prerecorded, live, generated, uploaded, embedded,
        preview, monitoring, tutorial, and communication media. Evidence:
        media-accessibility register.
  - [ ] CAP.45.b Provide synchronized captions for dialogue and meaningful
        sound, with language, speaker, timing, correction, and quality review.
        Evidence: caption conformance tests.
  - [ ] CAP.45.c Provide navigable transcripts with speakers, timestamps,
        chapters/segments, search, download policy, and media synchronization.
        Evidence: transcript interaction tests.
  - [ ] CAP.45.d Provide audio description or an equivalent descriptive media
        alternative for essential visual information. Evidence:
        content-by-content applicability and proof.
  - [ ] CAP.45.e Convert alarms, clipping, cues, countdowns, stage signals,
        connection loss, and other audio-only status to visible and haptic
        alternatives where supported. Evidence: alert modality tests.
  - [ ] CAP.45.f Provide text, notation, data, waveform metrics, event lists, or
        controllable replay for domain information conveyed primarily by sound.
        Evidence: non-audio workflow tests.
  - [ ] CAP.45.g Expose caption/transcript availability, processing, failure,
        partial, language, and stale states without false completeness.
        Evidence: media-state UI tests.
- [ ] CAP.46 Focus is restored predictably after dialogs, drawers, route
      transitions, virtualization, and deleted items. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.46.a Capture the invoking control and restore focus to it after
        modal dialog completion or cancellation when it remains available.
        Evidence: dialog focus tests.
  - [ ] CAP.46.b Restore focus after nonmodal drawer/inspector dismissal without
        moving the user's workspace selection. Evidence: drawer focus tests.
  - [ ] CAP.46.c Place focus at the route's meaningful heading, restored
        selection, validation target, or requested deep-link anchor after
        navigation. Evidence: route transition tests.
  - [ ] CAP.46.d Restore focus to a virtualized item by stable identity and
        scroll it into view without relying on recycled DOM position. Evidence:
        virtualization focus tests.
  - [ ] CAP.46.e After deletion, move focus by documented rule to next item,
        previous item, parent, collection heading, or recovery action. Evidence:
        first/middle/last deletion tests.
  - [ ] CAP.46.f Handle disabled, permission-hidden, filtered, moved, or
        concurrently deleted restoration targets with a safe announced fallback.
        Evidence: unavailable-target tests.
  - [ ] CAP.46.g Prevent asynchronous loading, toasts, status updates, and
        background rerenders from stealing focus. Evidence: focus stability
        tests.
- [ ] CAP.47 Virtualized content preserves accessible row counts, position,
      selection, and keyboard navigation. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.47.a Expose total row/item count or an honest unknown/loading state
        independently from rendered DOM count. Evidence: collection semantic
        tests.
  - [ ] CAP.47.b Expose each item's stable position, set size, level, grouping,
        expansion, and relationship semantics. Evidence: screen-reader tree/grid
        logs.
  - [ ] CAP.47.c Preserve single, multiple, range, select-all-filtered, and
        offscreen selection by stable identity. Evidence: scroll/recycle
        selection tests.
  - [ ] CAP.47.d Implement arrow, page, home/end, type-ahead, group, and focus
        navigation according to the selected widget pattern. Evidence: keyboard
        navigation tests.
  - [ ] CAP.47.e Retain focused item and logical scroll anchor through prepend,
        append, resort, filter, resize, refresh, and concurrent mutation.
        Evidence: dynamic collection tests.
  - [ ] CAP.47.f Announce loading, loaded ranges, result-count changes, moved
        focus, and unavailable items without flooding live regions. Evidence:
        announcement tests.
  - [ ] CAP.47.g Test empty, one-row, huge, variable-height, nested, grouped,
        and permission-changing collections. Evidence: virtualization boundary
        matrix.
- [ ] CAP.48 Drag-and-drop always has non-drag controls and does not require
      fine motor precision. Evidence: per-domain capability-matrix row linked to
      implementation and current proof.
  - [ ] CAP.48.a Inventory reorder, move, upload, place, connect, trim, resize,
        pan, scrub, lasso, and domain-specific drag interactions. Evidence:
        per-domain drag inventory.
  - [ ] CAP.48.b Provide select-plus-explicit-action controls for moving between
        parents, groups, lanes, tracks, stages, or collections. Evidence:
        non-drag move journeys.
  - [ ] CAP.48.c Provide move-up/down/start/end and direct-position controls for
        ordering. Evidence: non-drag reorder tests.
  - [ ] CAP.48.d Provide numeric, stepper, preset, snapping, and reset controls
        for position, duration, trim, size, and value changes. Evidence:
        precision-independent operation tests.
  - [ ] CAP.48.e Provide source/target selectors and create/remove actions for
        graph, routing, dependency, or relationship connections. Evidence:
        non-drag connection tests.
  - [ ] CAP.48.f Meet target-size/spacing requirements, support cancellation,
        avoid path-dependent gestures, and tolerate coarse pointers and tremor.
        Evidence: motor accessibility tests.
  - [ ] CAP.48.g Keep drag and non-drag paths behaviorally equivalent for
        validation, authorization, impact preview, audit, undo, and result.
        Evidence: path-equivalence tests.
- [ ] CAP.49 Locale-aware formatting covers dates, times, time zones, numbers,
      units, currency, pluralization, sorting, and collation. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.49.a Format calendar dates with explicit locale, calendar system,
        granularity, and unambiguous machine value. Evidence: locale date
        fixtures.
  - [ ] CAP.49.b Format time, duration, relative time, time zone,
        daylight-saving transition, and ambiguous/nonexistent local time
        correctly. Evidence: clock and zone boundary tests.
  - [ ] CAP.49.c Format decimal, integer, percentage, scientific, compact, and
        significant-digit numbers without changing stored values. Evidence:
        numeric locale fixtures.
  - [ ] CAP.49.d Format domain units with conversion policy, displayed unit,
        precision, rounding, and source-unit preservation. Evidence: known-unit
        tests.
  - [ ] CAP.49.e Format currency with ISO identity, minor units, exchange-rate
        source/time when converted, and accounting behavior. Evidence: currency
        fixtures.
  - [ ] CAP.49.f Use locale plural categories, select/gender rules where needed,
        and parameterized messages without string concatenation. Evidence:
        pluralization tests.
  - [ ] CAP.49.g Apply locale-aware sort and collation with numeric, case,
        accent, punctuation, script, and stable tie-break behavior. Evidence:
        multilingual ordering fixtures.
  - [ ] CAP.49.h Keep canonical identifiers, hashes, code, paths, standards
        values, and protocol payloads locale-independent. Evidence:
        locale-switch invariant tests.
- [ ] CAP.50 Bidirectional text, vertical-script metadata, long translations,
      and font fallback are visual-regression tested where applicable. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.50.a Test full RTL layout mirroring, navigation order, panel
        placement, icons, directional controls, and embedded LTR technical
        content. Evidence: RTL route snapshots and interaction tests.
  - [ ] CAP.50.b Test mixed-direction titles, names, URLs, paths, numbers,
        timecodes, code, identifiers, and punctuation with isolation controls.
        Evidence: bidi adversarial fixtures.
  - [ ] CAP.50.c Test vertical writing-mode metadata and distinguish content
        direction from interface direction. Evidence: vertical-script fixtures.
  - [ ] CAP.50.d Expand translatable strings to representative long forms and
        test wrapping, reflow, control growth, tables, dialogs, toasts, and
        narrow layouts. Evidence: pseudo-localization snapshots.
  - [ ] CAP.50.e Test primary and fallback fonts across supported scripts,
        combining marks, emoji, symbols, notation, and specialist glyphs.
        Evidence: glyph coverage snapshots.
  - [ ] CAP.50.f Detect tofu, clipped marks, baseline shifts, broken ligatures,
        reordered text, overlap, truncation without disclosure, and hidden
        controls. Evidence: reviewed visual-diff results.
  - [ ] CAP.50.g Run the matrix in each supported theme, density, viewport, and
        zoom level. Evidence: cross-dimension visual regression report.
- [ ] CAP.51 Localizable content and non-localizable identifiers are separated
      in contracts and editors. Evidence: per-domain capability-matrix row
      linked to implementation and current proof.
  - [ ] CAP.51.a Classify each field as invariant identifier, source-authored
        content, localizable content, locale-neutral metadata, or derived
        display value. Evidence: field localization schema.
  - [ ] CAP.51.b Store stable IDs, keys, hashes, enum codes, standard
        vocabularies, paths, URIs, and protocol values independently from
        labels. Evidence: contract separation tests.
  - [ ] CAP.51.c Store localized values by canonical locale with source locale,
        fallback, translation status, translator, revision, and provenance.
        Evidence: localized-value contract tests.
  - [ ] CAP.51.d Prevent locale switching from rewriting identifiers,
        source-authored truth, references, search keys, or interoperability
        values. Evidence: locale-switch mutation tests.
  - [ ] CAP.51.e Present identifier and localizable label controls distinctly
        with appropriate validation, read-only state, and accessible help.
        Evidence: editor component tests.
  - [ ] CAP.51.f Define missing, fallback, stale, machine-suggested,
        human-reviewed, approved, and obsolete translation behavior. Evidence:
        translation lifecycle tests.
  - [ ] CAP.51.g Migrate legacy combined identifier/label fields without
        breaking external references or silently inventing translations.
        Evidence: migration reconciliation report.
- [ ] CAP.52 Offline, reconnect, tab suspension, expired auth, and mid-operation
      network loss have deterministic recovery. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.52.a Define which reads, edits, commands, jobs, reviews, approvals,
        publishes, and destructive actions are available, queued, or prohibited
        offline. Evidence: per-domain offline capability policy.
  - [ ] CAP.52.b Detect offline, captive, degraded, high-latency, and
        server-unavailable states without equating every request failure to
        offline. Evidence: connectivity classification tests.
  - [ ] CAP.52.c Persist allowed local work with user/tenant binding,
        encryption, schema/version, size quota, expiry, and support-safe export.
        Evidence: offline-store tests.
  - [ ] CAP.52.d Reauthenticate before replay after expired auth and recheck
        current tenant, permissions, policy, object revision, and dependencies.
        Evidence: expired-auth replay tests.
  - [ ] CAP.52.e Resume or reconcile work after tab suspension, process freeze,
        browser discard, desktop sleep, and mobile backgrounding. Evidence:
        lifecycle automation matrix.
  - [ ] CAP.52.f Handle network loss before acceptance, after acceptance with
        unknown result, during upload, during job, and during streamed output.
        Evidence: boundary-specific fault tests.
  - [ ] CAP.52.g Replay only idempotent queued work in dependency order and
        surface conflict, rejection, partial completion, and stale intent.
        Evidence: reconnect replay tests.
  - [ ] CAP.52.h Preserve recovery evidence and never claim success until the
        authoritative state is read back. Evidence: false-success negative
        tests.
- [ ] CAP.53 Deep links restore route, selected entity, tab, filter, inspector,
      and safe share context. Evidence: per-domain capability-matrix row linked
      to implementation and current proof.
  - [ ] CAP.53.a Define canonical versioned URL/path/query/state encoding for
        each restorable route context. Evidence: deep-link contract.
  - [ ] CAP.53.b Restore the authorized entity by stable identifier and handle
        missing, moved, deleted, retained, cross-tenant, or forbidden targets.
        Evidence: entity-resolution tests.
  - [ ] CAP.53.c Restore valid tab/subview and fall back predictably when a tab
        is removed, unsupported, or unauthorized. Evidence: tab compatibility
        tests.
  - [ ] CAP.53.d Restore typed filters, sort, grouping, pagination anchor, and
        saved-view references without accepting unsafe query expressions.
        Evidence: query-state round-trip tests.
  - [ ] CAP.53.e Restore inspector mode, selected subobject/anchor, compare
        target, and focus without opening hidden protected context. Evidence:
        inspector restoration tests.
  - [ ] CAP.53.f Separate shareable state from local-only drafts, secrets,
        protected identities, signed URLs, transient tokens, and oversized
        selection data. Evidence: URL leakage tests.
  - [ ] CAP.53.g Recheck authentication, tenancy, object/property authorization,
        lifecycle, and link expiry at resolution time. Evidence: deep-link
        security negative tests.
  - [ ] CAP.53.h Preserve a safe return location and provide intelligible
        recovery for stale, malformed, incompatible, or inaccessible links.
        Evidence: failure journey tests.
- [ ] CAP.54 Browser refresh and back/forward navigation do not discard unsaved
      work or mutate state unexpectedly. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.54.a Classify route state as canonical URL state, server-persisted
        state, recoverable local draft, ephemeral UI state, or nonrestorable
        secret state. Evidence: route-state ownership map.
  - [ ] CAP.54.b Flush, queue, or persist allowed unsaved work before reload and
        warn only when data loss truly remains possible. Evidence: refresh
        recovery tests.
  - [ ] CAP.54.c Restore local drafts only for the same authenticated user,
        tenant, object, base revision, and compatible schema. Evidence:
        draft-isolation tests.
  - [ ] CAP.54.d Make route rendering and history restoration read-only unless
        the user explicitly repeats a mutation. Evidence: no-mutation navigation
        assertions.
  - [ ] CAP.54.e Restore route, selection, filters, inspector, focus, and
        reasonable scroll position on Back and Forward. Evidence: history
        navigation tests.
  - [ ] CAP.54.f Handle POST/redirect, pending upload, running job, open dialog,
        in-progress composition, and conflict states without duplicate commands.
        Evidence: lifecycle boundary tests.
  - [ ] CAP.54.g Detect server changes, deletion, permission loss, expired auth,
        and stale local base on restoration. Evidence: stale-restoration tests.
  - [ ] CAP.54.h Test hard refresh, soft reload, crash recovery, tab duplicate,
        tab restore, and browser session restore. Evidence: browser lifecycle
        matrix.

### 3.5 Operability and release capability

- [ ] CAP.55 Every user mutation emits a correlated audit event and trace.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.55.a Inventory create, edit, delete, restore, transition, review,
        approval, override, bulk, import, publish, configuration, and
        administrative mutations. Evidence: mutation-to-audit register.
  - [ ] CAP.55.b Assign stable mutation, command/idempotency, request, trace,
        actor/session, and resulting audit event identifiers. Evidence:
        correlation contract tests.
  - [ ] CAP.55.c Record authenticated and delegated actor, tenant, target,
        authorized action, prior/new revision, timestamp, reason, result, and
        policy decision. Evidence: audit schema assertions.
  - [ ] CAP.55.d Link to content hashes, diffs, gate/provenance evidence, and
        side-effect references without duplicating protected content in audit.
        Evidence: audit redaction and linkage tests.
  - [ ] CAP.55.e Persist the audit event in the same transaction or guaranteed
        outbox as the accepted mutation. Evidence: mutation/audit atomicity
        tests.
  - [ ] CAP.55.f Propagate trace context across API, database, queue, worker,
        provider, desktop/bridge, webhook, and notification boundaries.
        Evidence: end-to-end trace continuity tests.
  - [ ] CAP.55.g Record refused and failed mutation attempts at a safe detail
        level without claiming an authoritative state change. Evidence: negative
        audit tests.
  - [ ] CAP.55.h Enforce append-only integrity, retention, access, legal hold,
        export, and tenant isolation for audit events. Evidence: audit lifecycle
        tests.
- [ ] CAP.56 Every job exposes queue delay, execution time, retries, resource
      use, provider/model, cost, outcome, and failure class. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.56.a Record accepted, eligible, queued, leased, started, stage,
        completed, and terminal timestamps with one clock/unit contract.
        Evidence: job timing assertions.
  - [ ] CAP.56.b Derive queue delay, scheduling delay, active execution,
        external wait, paused time, retry backoff, and total elapsed time
        separately. Evidence: known-timeline tests.
  - [ ] CAP.56.c Record attempt number, retry reason, policy, next-attempt time,
        worker, lease, checkpoint, and prior attempt linkage. Evidence:
        retry-history tests.
  - [ ] CAP.56.d Record bounded CPU, memory, GPU type/time/memory, storage,
        network, token, frame/sample, and provider usage as applicable.
        Evidence: resource instrumentation tests.
  - [ ] CAP.56.e Record provider, model/deployment, immutable version/digest,
        region, request reference, and provider operation/reference. Evidence:
        provider job provenance tests.
  - [ ] CAP.56.f Record estimated, reserved, actual, refunded, and unallocated
        cost with currency, rate-card version, and attribution. Evidence: cost
        reconciliation tests.
  - [ ] CAP.56.g Record succeeded, partial, cancelled, compensated, failed,
        dead-lettered, abandoned, and unknown outcomes without collapsing them.
        Evidence: terminal state tests.
  - [ ] CAP.56.h Classify input, authorization, policy, dependency, provider,
        timeout, capacity, resource, internal, cancellation, and unknown failure
        classes. Evidence: error classification tests.
  - [ ] CAP.56.i Expose support-safe job diagnostics and evidence links without
        content, secrets, signed URLs, protected identities, or unbounded
        labels. Evidence: telemetry privacy tests.
- [ ] CAP.57 Metrics have bounded cardinality and exclude content, PII,
      protected-source data, and secrets. Evidence: per-domain capability-matrix
      row linked to implementation and current proof.
  - [ ] CAP.57.a Inventory each metric name, type, unit, owner, purpose,
        aggregation, retention, and allowed dimensions. Evidence: metric
        registry.
  - [ ] CAP.57.b Set explicit cardinality budgets per instrument and service
        plus warning and hard-fail behavior. Evidence: budget policy and load
        tests.
  - [ ] CAP.57.c Allowlist bounded domain, operation, state, error-class,
        provider-class, model-family, region, environment, and cohort labels as
        required. Evidence: attribute schema tests.
  - [ ] CAP.57.d Prohibit raw user/tenant/object/job/request IDs, URLs,
        filenames, query text, prompts, content, stack traces, and exception
        messages as labels. Evidence: forbidden-label lint tests.
  - [ ] CAP.57.e Redact or exclude direct and quasi-identifying PII, learner
        records, protected-source identity, biometric values, rights details,
        credentials, and secrets. Evidence: canary-data leakage tests.
  - [ ] CAP.57.f Handle unknown/new enum values through bounded fallback
        categories without silently dropping the event. Evidence: unknown-value
        tests.
  - [ ] CAP.57.g Monitor series count, label churn, dropped samples, exporter
        rejection, cost, and schema violations. Evidence: metric pipeline
        observability tests.
- [ ] CAP.58 SLOs cover availability, latency, durability, queue age, publish
      correctness, and critical gate freshness. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.58.a Define user-visible service availability SLIs, qualifying
        requests, exclusions, target, window, and error budget. Evidence:
        approved availability SLO.
  - [ ] CAP.58.b Define latency SLIs and percentile targets for critical reads,
        mutations, searches, saves, jobs, reviews, and publish actions.
        Evidence: latency SLOs.
  - [ ] CAP.58.c Define acknowledged-write, revision, blob, event, audit, and
        restore durability/correctness SLIs. Evidence: durability SLOs.
  - [ ] CAP.58.d Define queue age, start delay, completion deadline, stuck job,
        and dead-letter SLOs by priority and job class. Evidence: queue SLOs.
  - [ ] CAP.58.e Define publish correctness for artifact bytes, metadata,
        audience/target, rights/consent, gates, provenance, delivery, and
        read-back. Evidence: publish SLO.
  - [ ] CAP.58.f Define maximum age and invalidation/recomputation time for
        safety, rights, consent, canon, quality, and release-critical gate
        evidence. Evidence: freshness SLOs.
  - [ ] CAP.58.g Assign owner, measurement source, dashboard, burn alerts,
        incident trigger, error-budget policy, and review cadence to each SLO.
        Evidence: operational SLO register.
  - [ ] CAP.58.h Validate SLIs against controlled success/failure events and
        prove missing telemetry cannot appear as success. Evidence: synthetic
        SLI tests.
- [ ] CAP.59 Alerts are actionable, deduplicated, routed by ownership, and
      linked to a tested runbook. Evidence: per-domain capability-matrix row
      linked to implementation and current proof.
  - [ ] CAP.59.a Define the user/business impact, triggering SLI or invariant,
        severity, threshold, window, and recovery condition for each alert.
        Evidence: alert catalog.
  - [ ] CAP.59.b Include affected domain/service/tenant-safe scope, environment,
        start time, current value, recent change, dashboard, logs/traces, and
        correlation data. Evidence: notification template tests.
  - [ ] CAP.59.c Group duplicate symptoms by incident key and suppress dependent
        alerts without hiding independent failures. Evidence: deduplication
        scenario tests.
  - [ ] CAP.59.d Route primary, backup, specialist, security/privacy/safety, and
        vendor escalation by service ownership and time. Evidence: routing
        tests.
  - [ ] CAP.59.e Define acknowledgement, paging, escalation, silence,
        maintenance, resolve, reopen, and handoff behavior. Evidence:
        alert-lifecycle tests.
  - [ ] CAP.59.f Link a versioned runbook with validation, mitigation,
        rollback/kill switch, recovery, evidence preservation, and escalation.
        Evidence: runbook schema review.
  - [ ] CAP.59.g Exercise alert firing, notification, ownership, runbook steps,
        recovery detection, and post-incident evidence in a game day. Evidence:
        timed rehearsal report.
  - [ ] CAP.59.h Review noisy, unactionable, stale-owner, missing-data, and
        never-fired alerts on a defined cadence. Evidence: alert hygiene report.
- [ ] CAP.60 Backup and restore prove both metadata and blob consistency,
      including provenance and audit records. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.60.a Inventory authoritative databases, blobs, manifests, search
        rebuild inputs, queues/outboxes, keys/config references, audit, and
        external-ledger data. Evidence: backup scope register.
  - [ ] CAP.60.b Define full/incremental cadence, point-in-time coverage,
        retention, region, immutability, encryption, key separation, and access.
        Evidence: backup policy.
  - [ ] CAP.60.c Capture a transactionally consistent recovery point or explicit
        ordering markers across metadata, blobs, events, audit, and provenance.
        Evidence: consistency design.
  - [ ] CAP.60.d Verify backup completeness, checksums, decryptability, schema
        version, catalog, and restore prerequisites automatically. Evidence:
        backup verification report.
  - [ ] CAP.60.e Restore into an isolated environment using documented
        credentials and infrastructure without depending on the failed primary.
        Evidence: independent restore rehearsal.
  - [ ] CAP.60.f Reconcile rows to blobs, revisions to content, provenance to
        ingredients, audit to mutations, outbox/inbox positions, tombstones,
        legal holds, and tenant counts. Evidence: post-restore integrity report.
  - [ ] CAP.60.g Rehash restored bytes and validate representative read, search,
        history, review, gate, export, and publish-preparation journeys.
        Evidence: restored-system verification.
  - [ ] CAP.60.h Measure achieved recovery point/time and record gaps, owners,
        remediation, and next rehearsal. Evidence: signed restore report.
- [ ] CAP.61 Disaster recovery proves RPO/RTO and safe replay of jobs, outbox
      events, and pending releases. Evidence: per-domain capability-matrix row
      linked to implementation and current proof.
  - [ ] CAP.61.a Define disaster scenarios, affected regions/services/data,
        dependency assumptions, authority, and per-capability RPO/RTO. Evidence:
        approved DR scope.
  - [ ] CAP.61.b Provision isolated recovery infrastructure, networking,
        secrets/keys, providers, storage, queues, observability, and access.
        Evidence: recovery-environment readiness check.
  - [ ] CAP.61.c Restore authoritative state to the selected recovery point and
        measure actual data loss and elapsed time. Evidence: timed recovery log.
  - [ ] CAP.61.d Fence old writers, leases, schedulers, webhooks, bridges, and
        provider callbacks before activating recovery writes. Evidence:
        split-brain prevention tests.
  - [ ] CAP.61.e Reconcile queued/running/retrying/unknown jobs before replay
        and preserve idempotency, checkpoints, external outcome, and cost state.
        Evidence: job replay report.
  - [ ] CAP.61.f Replay outbox/inbox/events in safe order with duplicate,
        out-of-order, poison, and consumer-version handling. Evidence: event
        replay report.
  - [ ] CAP.61.g Revalidate candidate inputs, gates, rights/consent, provenance,
        approvals, idempotency, and destinations before resuming pending
        releases. Evidence: pending-release reconciliation.
  - [ ] CAP.61.h Verify user journeys, integrations, observability, alerts,
        backups, and support procedures before failover completion. Evidence:
        recovery acceptance matrix.
  - [ ] CAP.61.i Rehearse failback or establish the recovery environment as the
        new primary with data reconciliation and rollback. Evidence: failback
        plan and result.
- [ ] CAP.62 Cost controls cover estimation, reservation, actual spend,
      variance, quotas, anomaly detection, and kill switch. Evidence: per-domain
      capability-matrix row linked to implementation and current proof.
  - [ ] CAP.62.a Define versioned unit prices for provider/model, tokens,
        GPU/CPU, storage, transfer, render frames/samples, licenses, and other
        billable resources. Evidence: rate-card registry.
  - [ ] CAP.62.b Estimate cost from validated inputs before acceptance and
        expose assumptions, currency, uncertainty, and expiry. Evidence:
        known-price estimate tests.
  - [ ] CAP.62.c Atomically reserve actor/project/tenant/provider budget for
        accepted work and handle concurrent reservations. Evidence:
        oversubscription race tests.
  - [ ] CAP.62.d Record actual metered usage and provider charges per attempt,
        job, derivative, retry, partial result, and cancellation. Evidence:
        usage attribution tests.
  - [ ] CAP.62.e Reconcile estimate, reservation, actual, refund/release, and
        invoice/exported cost with explicit variance reasons. Evidence: cost
        reconciliation report.
  - [ ] CAP.62.f Enforce per-operation, actor, project, tenant, day/month,
        provider, resource, and exceptional approval quotas. Evidence: quota
        boundary and isolation tests.
  - [ ] CAP.62.g Detect spend rate, variance, volume, provider, resource, retry,
        and unallocated-cost anomalies with bounded dimensions. Evidence:
        anomaly scenario tests.
  - [ ] CAP.62.h Provide scoped pause/kill controls with named authority,
        propagation target, in-flight policy, recovery, and audit. Evidence:
        kill-switch rehearsal.
  - [ ] CAP.62.i Prevent cost-control failure from becoming unbounded execution
        or fabricated zero cost. Evidence: unavailable-metering fail-closed
        tests.
- [ ] CAP.63 Release supports canary or tenant cohort rollout, rollback,
      compatibility monitoring, and data-migration observation. Evidence:
      per-domain capability-matrix row linked to implementation and current
      proof.
  - [ ] CAP.63.a Define immutable release candidate, artifacts, contracts,
        schemas, flags/config, migrations, dependency versions, and evidence.
        Evidence: release manifest.
  - [ ] CAP.63.b Define canary or cohort selection by environment, tenant, role,
        region, platform, data shape, and risk without leaking targeting.
        Evidence: rollout targeting policy.
  - [ ] CAP.63.c Define rollout stages, minimum observation, success/error/SLO
        thresholds, approval points, automatic pause, and abort. Evidence:
        staged rollout plan.
  - [ ] CAP.63.d Verify old/new API, event, job, schema, cache, client,
        desktop/bridge, provider, and artifact compatibility during coexistence.
        Evidence: compatibility dashboards and synthetic tests.
  - [ ] CAP.63.e Observe migration progress, lock/latency, backfill, validation,
        drift, old/new writer behavior, and contract completion. Evidence:
        migration telemetry.
  - [ ] CAP.63.f Define code/config/flag rollback, database rollback or
        compensation, queued-job policy, artifact withdrawal, and irreversible
        boundary. Evidence: rollback runbook.
  - [ ] CAP.63.g Rehearse rollback from each stage with realistic traffic and
        in-flight mutations/jobs. Evidence: stage-level rollback results.
  - [ ] CAP.63.h Promote only on fresh evidence and record actor, cohort,
        timestamps, observations, exceptions, and final state. Evidence: release
        progression audit.
  - [ ] CAP.63.i Remove temporary compatibility, dual-write/read, and rollout
        flags after the defined observation window. Evidence: closure checklist.
- [ ] CAP.64 A release evidence bundle links requirements, code, tests, scans,
      migrations, screenshots, performance results, runbooks, and approvals.
      Evidence: per-domain capability-matrix row linked to implementation and
      current proof.
  - [ ] CAP.64.a Pin release identifier, commit SHA, build inputs, dependencies,
        generated artifacts, configuration, target environment, and artifact
        hashes. Evidence: immutable release identity record.
  - [ ] CAP.64.b Link every in-scope requirement and ledger identifier to its
        implementation, disposition, or approved N/A evidence. Evidence:
        requirement traceability report.
  - [ ] CAP.64.c Link source diffs, review records, generated-code drift
        results, build outputs, and reproducibility or provenance references.
        Evidence: code/build evidence index.
  - [ ] CAP.64.d Link raw unit, property, contract, integration, end-to-end,
        mobile/desktop, evaluation, accessibility, resilience, and release test
        reports. Evidence: test evidence index.
  - [ ] CAP.64.e Link dependency, license, secret, static/dynamic security,
        authorization, privacy, safety, rights, consent, and provenance scans.
        Evidence: trust scan index.
  - [ ] CAP.64.f Link migration plans, rehearsals, compatibility windows,
        reconciliations, rollback/compensation results, and production
        observation queries. Evidence: migration evidence index.
  - [ ] CAP.64.g Link dated screenshots, recordings, network traces, console
        logs, accessibility outputs, and performance/load/cost/SLO results.
        Evidence: user/runtime evidence index.
  - [ ] CAP.64.h Link deployment, migration, rollback, incident, DR, support,
        provider, bridge/host, and kill-switch runbooks with rehearsal state.
        Evidence: operations evidence index.
  - [ ] CAP.64.i Link named technical, product, domain, security, privacy,
        safety, rights/legal, accessibility, operations, and human governance
        approvals without fabricating missing signoff. Evidence: approval
        register.
  - [ ] CAP.64.j Validate every link, hash, version, timestamp, owner, expiry,
        waiver, scope, and target before signing the bundle. Evidence:
        release-bundle integrity check.

---

## 4. Dependency and sequencing map

1. Complete BASE, STD, and Phase S architecture decisions before new workbench
   packages or routes.
2. Migrate Tara to the shared kit without behavioral regression.
3. Integrate existing standalone surfaces before creating replacements.
4. Build domain contracts and known-value gate logic before persistence.
5. Build persistence and service authorization before mutation UI.
6. Build read-only UI against live contracts before enabling mutations.
7. Build publish/release only after trust, provenance, and review gates.
8. Run focused verification throughout; run each expensive gate sequentially
   under the repository resource-safety policy.
9. Prepare engineering-readiness packets before human launch decisions.

- [x] DEP.1 Encode the phase dependency graph in a machine-readable file.
      Evidence: a cycle detector and unknown-ID validator pass. <!-- evidence:
      evidence/v1-workbenches/dependency-graph.json (23 nodes, 66 edges) +
      scripts/v1-workbenches/generate-dependency-graph.mjs; `--check` runs the
      cycle detector + unknown-ID validator and exits non-zero on failure →
      validation.passed=true. Command `pnpm inventory:v1-workbenches`; verify
      `pnpm verify:inventory:v1-workbenches`. -->
  - [x] DEP.1.a Define the graph schema for phase/task node, dependency edge,
        edge reason, blocking strength, evidence gate, owner, and source.
        Evidence: versioned graph schema.
        <!-- evidence: schema 'v1-workbenches-dependency-graph/1'; nodes carry id/kind/label; edges carry from/to/class/sourceRule/reason/blockingStrength/evidenceGate/owner (evidenceGate & owner are null slots pending DEP.2). -->
  - [x] DEP.1.b Encode every sequencing rule numbered above as explicit graph
        edges rather than prose-only constraints. Evidence: nine rules have
        machine-readable coverage.
        <!-- evidence: coverage.sequencingRulesCovered=[1..9], allNineRulesCovered=true; each edge records its sourceRule. -->
  - [x] DEP.1.c Encode shared-to-domain, domain-to-domain, host, migration,
        contract, infrastructure, evidence, and human-approval dependencies.
        Evidence: dependency-class coverage report.
        <!-- evidence: coverage.dependencyClasses covers all 8 classes; domain-to-domain edges derived from §5 X.1-X.5 cross-domain integrations. -->
  - [x] DEP.1.d Validate every node against an existing ledger identifier and
        every referenced artifact/environment against its registry. Evidence:
        zero unknown references.
        <!-- evidence: validation.unknownNodes=[] and danglingEdges=[]; phase nodes validated against `## Phase X` headings, meta/integration nodes against task-ID prefixes parsed live from the ledger. No node references an external artifact/environment, so that clause is vacuously clean. -->
  - [x] DEP.1.e Detect direct and transitive cycles and print the complete
        minimal cycle path. Evidence: cycle unit tests.
        <!-- evidence: findCycle() DFS returns the minimal a->b->c->a path; generate-dependency-graph.test.mjs asserts a positive 3-node cycle control + the real graph is acyclic (validation.cycle=null). -->
  - [x] DEP.1.f Detect redundant, contradictory, self, orphaned, and
        unsatisfiable dependencies. Evidence: graph integrity report.
        <!-- evidence: validation.{selfEdges,redundantEdges,contradictoryEdges,orphanNodes,danglingEdges} all empty; dangling-edge check covers unsatisfiable (a dependency on a non-existent node). -->
  - [x] DEP.1.g Generate stable critical-path and parallel-ready projections
        from the committed graph. Evidence: deterministic projection check.
        <!-- evidence: projections.criticalPath (8-stage build-order spine) + parallelReady (S, STAGE.contracts); determinism test asserts byte-stable output across runs. -->
- [ ] DEP.2 Assign an owner and reviewer to every phase. Evidence: no phase has
      an unowned critical path.
  - [ ] DEP.2.a Enumerate shared, domain, cross-domain, rollout, and final-gate
        phases requiring accountable ownership. Evidence: phase ownership rows.
  - [ ] DEP.2.b Assign one accountable delivery owner with team and escalation
        path to each phase. Evidence: zero missing owners.
  - [ ] DEP.2.c Assign an independent reviewer with the required domain,
        platform, safety, rights, security, accessibility, or operations
        expertise. Evidence: reviewer qualification rows.
  - [ ] DEP.2.d Prevent self-review where the phase contains high-impact,
        regulated, trust, release, or product-scope decisions. Evidence:
        separation-of-duties validation.
  - [ ] DEP.2.e Assign backup ownership for critical-path phases and record
        handoff/escalation timing. Evidence: critical-path coverage report.
  - [ ] DEP.2.f Validate that every critical-path node inherits or declares both
        owner and reviewer. Evidence: graph ownership check.
- [ ] DEP.3 Record cross-domain contract dependencies and compatibility windows.
      Evidence: producer/consumer matrix covers every integration.
  - [ ] DEP.3.a Inventory every synchronous API, asynchronous event, job,
        provider adapter, schema, blob/bundle, bridge, and embedded UI handoff
        across domains. Evidence: cross-domain integration register.
  - [ ] DEP.3.b Record producer, owner, contract identifier/version, transport,
        data classification, and availability/SLO for each integration.
        Evidence: producer-side matrix fields.
  - [ ] DEP.3.c Record every consumer, accepted versions, optional/required use,
        failure behavior, retry, and fallback. Evidence: consumer-side matrix
        fields.
  - [ ] DEP.3.d Define additive, breaking, deprecated, unsupported, and unknown
        version behavior. Evidence: compatibility policy per integration.
  - [ ] DEP.3.e Set coexistence start/end, producer-first or consumer-first
        ordering, feature/config flag, backfill, and removal conditions.
        Evidence: compatibility-window rows.
  - [ ] DEP.3.f Link provider and consumer contract, version-skew, replay, and
        failure tests. Evidence: no integration lacks bidirectional proof.
  - [ ] DEP.3.g Resolve producer/consumer owner conflicts and orphan consumers
        before scheduling implementation. Evidence: zero unresolved matrix rows.
- [ ] DEP.4 Record required infrastructure, credentials, fixtures, corpora,
      media, DCC versions, GPU capacity, and test tenants. Evidence: environment
      readiness checklist.
  - [ ] DEP.4.a Inventory databases, blob stores, queues, caches, search,
        telemetry, signing, model/provider, rendering, bridge, and delivery
        infrastructure by environment. Evidence: infrastructure rows.
  - [ ] DEP.4.b Inventory service accounts, OAuth clients, certificates, signing
        keys, provider secrets, DCC/engine licenses, and rotation owners without
        recording secret values. Evidence: credential-reference rows.
  - [ ] DEP.4.c Inventory valid, invalid, boundary, adversarial, version-skew,
        tenant-isolation, and migration fixtures with generation/source
        provenance. Evidence: fixture rows.
  - [ ] DEP.4.d Inventory licensed evaluation corpora, expected outputs,
        subgroup coverage, access policy, retention, and version. Evidence:
        corpus rows.
  - [ ] DEP.4.e Inventory source, generated, malformed, rights/consent,
        multilingual, accessibility, performance, and delivery media assets.
        Evidence: media fixture rows.
  - [ ] DEP.4.f Record supported DCC, engine, plugin, codec, browser, device,
        desktop runtime, and driver versions plus installation source. Evidence:
        target-version rows.
  - [ ] DEP.4.g Record GPU/CPU/memory/storage/network capacity, concurrency,
        reservation, quotas, cost, and fallback. Evidence: capacity rows.
  - [ ] DEP.4.h Provision isolated tenants, users, roles, cross-tenant collision
        data, and provider states for every integration and journey. Evidence:
        test-tenant rows.
  - [ ] DEP.4.i Validate availability, permission, version, quota, health, and
        cleanup for every readiness row. Evidence: zero unverified
        prerequisites.
- [ ] DEP.5 Define a critical-path release order with explicit parallel-safe
      slices. Evidence: the plan respects shared migrations and host ownership.
  - [ ] DEP.5.a Calculate the transitive critical path from the committed
        dependency graph with duration/confidence inputs. Evidence: generated
        critical-path projection.
  - [ ] DEP.5.b Identify architecture, product-boundary, contract, schema,
        security, infrastructure, and human-decision gates that block downstream
        starts. Evidence: blocking-gate list.
  - [ ] DEP.5.c Define the producer/consumer and expand/backfill/verify/contract
        order for every shared or cross-domain migration. Evidence:
        migration-sequencing rows.
  - [ ] DEP.5.d Define host-owner approval and deployment order for reused,
        proxied, redirected, embedded, or retired surfaces. Evidence:
        host-sequencing rows.
  - [ ] DEP.5.e Mark slices parallel-safe only when they share no incompatible
        contract, schema, data, host, environment, capacity, or approval
        mutation. Evidence: independence rationale per slice.
  - [ ] DEP.5.f Record integration, evidence-refresh, merge, and release
        synchronization points between parallel slices. Evidence: explicit join
        gates.
  - [ ] DEP.5.g Define replan behavior when an estimate, dependency, blocker,
        standard, provider, or product decision changes. Evidence: versioned
        schedule update procedure.
- [ ] DEP.6 Define stop-the-line conditions for security, provenance, consent,
      data loss, gate correctness, and cross-tenant leakage. Evidence: release
      policy links each condition to an owner and rollback.
  - [ ] DEP.6.a Define credential exposure, exploitable authorization bypass,
        remote code execution, unsafe fetch/upload, dependency compromise, and
        critical unpatched vulnerability stops. Evidence: security stop policy.
  - [ ] DEP.6.b Define invalid/missing signature, artifact hash mismatch, broken
        ingredient lineage, untrusted required provenance, and signing key
        compromise stops. Evidence: provenance stop policy.
  - [ ] DEP.6.c Define missing/revoked/expired/out-of-scope consent, protected
        identity exposure, and nonconsensual biometric/voice/body use stops.
        Evidence: consent stop policy.
  - [ ] DEP.6.d Define acknowledged-write loss, corruption, unrecoverable
        revision/blob mismatch, failed restore, migration reconciliation gap,
        and unsafe replay stops. Evidence: data-loss stop policy.
  - [ ] DEP.6.e Define fabricated, nondeterministic, stale-authorizing,
        miscalibrated, bypassable, or unavailable fail-open critical gate stops.
        Evidence: gate-correctness stop policy.
  - [ ] DEP.6.f Define any cross-tenant read, write, count, search, cache,
        export, event, telemetry, or provider-context disclosure as a stop.
        Evidence: tenancy stop policy.
  - [ ] DEP.6.g Assign incident commander, technical owner, specialist reviewer,
        communication path, evidence custodian, and restart authority by stop
        class. Evidence: named responsibility matrix.
  - [ ] DEP.6.h Define automatic pause/kill action, affected targets, in-flight
        behavior, rollback/containment, recovery proof, and approval to resume.
        Evidence: stop-and-resume runbooks.
  - [ ] DEP.6.i Rehearse at least one trigger per class and prove the release
        cannot proceed while the stop remains active. Evidence: game-day report.

---

## Phase S — Shared workbench platform

Phase S builds `@oshun/workbench-kit` only for genuinely domain-neutral
behavior. Domain rules remain in domain packages. Tara's current behavior and
tests are the extraction acceptance gate.

### S0 — Extraction audit and decisions

- [x] S0.1 Inventory Tara domain-library exports consumed by web, BFF, tests,
      migrations, and content publishing. Evidence: import graph snapshot. <!--
      evidence: evidence/v1-workbenches/tara-import-graph.json — generator
      scripts/v1-workbenches/generate-tara-import-graph.mjs (deterministic,
      git-grep driven, --check drift gate), 14 known-value tests, wired into
      `pnpm inventory:v1-workbenches` / `verify:inventory:v1-workbenches` + CI.
      -->
  - [x] S0.1.a Enumerate Tara public entry points and exported symbols from
        package metadata and source. Evidence: export-level inventory.
        <!-- evidence: .exportInventory — 209 symbols resolved from the src/index.ts
                                                                                                                                                                                                                                                                        barrel (star + named re-exports followed), each with origin module id and
                                                                                                                                                                                                                                                                        declaration kind (const/function/class/type/interface/enum). -->
  - [x] S0.1.b Map web application imports to exact exports and consuming
        routes/components. Evidence: web consumer edges.
        <!-- evidence: .consumersByArea.web = [] — no web surface imports the
                                                                                                                                                                                                                                                                        domain library directly; apps/oshun/web/.../tara-workbench reaches Tara
                                                                                                                                                                                                                                                                        through the BFF over HTTP, not via @oshun/tara-workbench. Honest zero. -->
  - [x] S0.1.c Map BFF imports to handlers, middleware, stores, and runtime
        paths. Evidence: BFF consumer edges.
        <!-- evidence: .consumersByArea.bff — 7 production BFF files (routes,
                                                                                                                                                                                                                                                                        server/app wiring, tara-workbench/{evidence,plane-categories,review-verdict},
                                                                                                                                                                                                                                                                        agentic bundle publisher), each edge lists exact imported symbols. -->
  - [x] S0.1.d Map unit/integration/browser test, fixture, and builder imports
        without treating test-only use as production reuse. Evidence: test
        consumer edges.
        <!-- evidence: .consumerEdges[].isTest — 2 test edges segregated
                                                                                                                                                                                                                                                                        (routes + bundle-publisher specs); excluded from every production-reuse
                                                                                                                                                                                                                                                                        count and from the unused-export computation. -->
  - [x] S0.1.e Map migration, schema, code-generation, and persistence
        registration imports. Evidence: data-path consumer edges.
        <!-- evidence: .consumersByArea.dataPath = [] — no data-path import edge.
                                                                                                                                                                                                                                                                        libs/oshun/persistence contract-persistence-registry.ts references Tara
                                                                                                                                                                                                                                                                        only in a comment (no import); classifier + comment-stripping confirm. -->
  - [x] S0.1.f Map content build, validation, bundle, and publishing imports.
        Evidence: publishing consumer edges.
        <!-- evidence: .consumersByArea.publishing — tara-bundle-publisher.ts
                                                                                                                                                                                                                                                                        (assembleMeditationBundle + MeditationBundle* types), the one production
                                                                                                                                                                                                                                                                        publishing consumer. -->
  - [x] S0.1.g Flag unused exports, deep imports, circular imports, and exports
        with only one real consumer. Evidence: reviewed import graph findings.
        <!-- evidence: .findings — 151 unused exports (validated: sampled 12,
                                                                                                                                                                                                                                                                        all 0 non-lib/non-test hits), 2 test-only exports, 56 single-consumer
                                                                                                                                                                                                                                                                        exports (all @oshun/bff), 0 deep imports, 0 intra-package cycles. -->
- [x] S0.2 Inventory Tara web components and classify domain-neutral shell
      behavior separately from meditation-specific behavior. Evidence: component
      extraction matrix. <!-- evidence: inventory (a,b) =
      tara-component-inventory.json; classification (c-f) =
      tara-component-extraction-matrix.md + component-extraction-matrix.json,
      the full extraction matrix (29 components → 4 neutral / 13 Tara-owned / 9
      split / 2 reject / 1 shared-shell; 13 extractable contracts two-archetype
      validated). Named architecture ratification of the matrix stays a human
      step (see S0 signoff); the classification is coherence-gated in CI
      (generate-component-extraction-matrix.mjs --check, 11 tests). -->
      <!-- adr: docs/adr/ADR-S1-workbench-kit-package-ownership.md carries the ownership
                                                                                                                                                                                                                                                                      + dependency direction this matrix's extraction targets must respect. -->
  - [x] S0.2.a Enumerate route, layout, workspace, inspector, editor, review,
        gate, publish, history, and utility components. Evidence: source-linked
        component rows. <!-- evidence: .components — 29 rows (28
        StudioTaraWorkbench\* owned + shared ShellLayout), each source-linked
        (.file) and role-tagged across the full taxonomy, route-linked to the
        page(s) that render it (multi-component pages captured).
        generate-tara-component-inventory.mjs, --check gate, tests. -->
  - [x] S0.2.b Record each component's props, state, context, hooks, data
        requests, mutation paths, styles, tokens, and tests. Evidence:
        dependency columns are complete. <!-- evidence: per-row columns
        props{hasProps,propNames,propsType}, localStateHooks, contextHooks,
        hooks[], dataRequests[] (GET), mutationPaths[] (POST/PUT/PATCH/DELETE,
        BASE-const resolved), inlineStyleBlocks, designTokens[] (var(--…)),
        hasTest/testFile. 24 read + 27 mutation endpoints; 25 owned components
        have no co-located test (surfaced coverage gap). -->
  - [x] S0.2.c Identify behavior whose contract and interaction semantics are
        independently usable by at least one non-Tara archetype. Evidence:
        domain-neutral rationale per candidate. <!-- evidence:
        evidence/v1-workbenches/tara-component-extraction-matrix.md +
        component-extraction-matrix.json .buckets.neutralCandidate (4
        zero-signal) plus the 9 mixed-split neutral contracts = 13 extractable
        contracts, each with a per-component domain-neutral rationale grounded
        in its real routes. generator generate-component-extraction-matrix.mjs
        (--check gate), 11 tests. -->
  - [x] S0.2.d Identify meditation vocabulary, stages, content blocks, gates,
        scores, routes, providers, copy, and policy that must remain Tara-owned.
        Evidence: Tara-specific rationale per retained component.
        <!-- evidence: .buckets.taraOwned (13) + the 2 mixed-reject rows, each with a
                                                                                                                                                                                                                                                                        Tara-specific rationale citing its meditation-vocabulary terms (invitational/
                                                                                                                                                                                                                                                                        breath/grounding/spark/cadence/seasonal/plane/steering/meditation). Coherence-
                                                                                                                                                                                                                                                                        checked: every highMeditationSignalComponent is Tara-owned. -->
  - [x] S0.2.e Split mixed components at an explicit seam or reject extraction
        when the seam would leak Tara semantics. Evidence: split/reject decision
        per mixed row.
        <!-- evidence: .buckets.mixedSplit (9, each with a named neutral contract +
                                                                                                                                                                                                                                                                        seam) and .buckets.mixedReject (2, residue too thin). Seam = the interaction
                                                                                                                                                                                                                                                                        contract over a domain-supplied adapter; Tara keeps routes/copy. Every mixed
                                                                                                                                                                                                                                                                        row carries a split|reject verb (test-enforced). -->
  - [x] S0.2.f Validate proposed neutral components against both an authoring
        and an operations-console consumer. Evidence: two-archetype usage
        sketches.
        <!-- evidence: .archetypeSketches — all 13 extractable neutral contracts have
                                                                                                                                                                                                                                                                        a concrete authoring-console AND operations-console usage sketch (e.g.
                                                                                                                                                                                                                                                                        CommentThread: draft comments vs incident-timeline comments). Test asserts
                                                                                                                                                                                                                                                                        both archetypes present for every contract. EvidenceGroundingPanel/SourceLibrary
                                                                                                                                                                                                                                                                        name Veritas as the near-at-hand real 2nd consumer (S0.11 gate). -->
- [x] S0.3 Inventory Tara BFF handlers, middleware, stores, and provider seams.
      Evidence: route-to-capability matrix.
      <!-- evidence: evidence/v1-workbenches/tara-bff-matrix.json — generator
                                                                                                                                                                                                                                                                      scripts/v1-workbenches/generate-tara-bff-matrix.mjs (string/comment/template-
                                                                                                                                                                                                                                                                      aware balanced-paren route scanner), 14 known-value tests, wired into
                                                                                                                                                                                                                                                                      inventory/verify:inventory:v1-workbenches + CI. 70 routes fully covered. -->
  - [x] S0.3.a Enumerate all mounted and source-defined Tara BFF routes with
        method, path, handler, middleware chain, and reachability. Evidence:
        effective route rows.
        <!-- evidence: .routes — 70 rows (GET 28 / POST 36 / PATCH 5 / PUT 1), each
                                                                                                                                                                                                                                                                        with resolved path, guard wrapper (all `guarded`), and middleware chain
                                                                                                                                                                                                                                                                        ([abuseProtection, authProtection]). Reachability = the guarded auth+scope+
                                                                                                                                                                                                                                                                        store gate (absent store ⇒ 503 not_configured); 0 ungated routes. -->
  - [x] S0.3.b Trace each handler to command/query, domain service, store,
        provider, job, blob, and external side effect. Evidence:
        handler-dependency rows.
        <!-- evidence: per-route storeMethods[] (47 distinct), domainCalls[]
                                                                                                                                                                                                                                                                        (@oshun/tara-workbench symbols actually invoked), auditEvents[] (32
                                                                                                                                                                                                                                                                        distinct), externalEffects[] (publish-manifest/model-provider/http-fetch/
                                                                                                                                                                                                                                                                        blob). -->
  - [x] S0.3.c Record actor resolution, object/property/function authorization,
        tenancy, validation, idempotency, concurrency, rate/size limits, and
        audit per route. Evidence: boundary-control rows.
        <!-- evidence: per-route guard (actor+tenancy+scope), middleware
                                                                                                                                                                                                                                                                        (auth + abuse/rate/size), permissions[] + dynamicPermissions[] +
                                                                                                                                                                                                                                                                        hasPermissionCheck (function authz, literal AND dynamic e.g.
                                                                                                                                                                                                                                                                        stage.requiredPermission), validationSchemas[], usesIdempotency,
                                                                                                                                                                                                                                                                        concurrencyControls[] (revision-version/supersede-in-place/409-conflict),
                                                                                                                                                                                                                                                                        auditEvents[]. Surfaces 7 auth-only mutations (guarded but no
                                                                                                                                                                                                                                                                        function-permission, e.g. script/compile binds `_actor`). -->
  - [x] S0.3.d Classify stores as durable production, in-memory production,
        fixture/demo, cache, or adapter and record lifecycle semantics.
        Evidence: store classification rows.
        <!-- evidence: .stores — TaraWorkbenchStore = durable-production, backed by
                                                                                                                                                                                                                                                                        @oshun/persistence ContractPersistenceService (durable-before-ack writes,
                                                                                                                                                                                                                                                                        typed read client); new Map() usages are local read-aggregation, not the
                                                                                                                                                                                                                                                                        backend. -->
  - [x] S0.3.e Classify provider seams as configured, not configured, fallback,
        fixture, or accidental mock with error/cancellation/cost behavior.
        Evidence: provider classification rows.
        <!-- evidence: .providers — TaraWorkbenchModelProvider =
                                                                                                                                                                                                                                                                        configured-or-fail-closed: a null LLM provider makes every capability
                                                                                                                                                                                                                                                                        (diverge/extractClaims/assistScript) throw TaraWorkbenchNotConfiguredError
                                                                                                                                                                                                                                                                        (honest fail-loud seam; no fabrication, fixture, or accidental mock). -->
  - [x] S0.3.f Map each route to the smallest shared or Tara-owned capability
        and its current tests. Evidence: zero unmapped reachable routes.
        <!-- evidence: every route carries a capability id (tara-workbench.<segs>.<verb>)
                                                                                                                                                                                                                                                                        — zero unmapped reachable routes — plus a `tested` flag from the spec's
                                                                                                                                                                                                                                                                        inject() paths. 59/70 tested; the 11 untested routes are surfaced honestly
                                                                                                                                                                                                                                                                        in coverageGaps.untestedRoutes (a coverage gap, not an unmapped route). -->
- [x] S0.4 Inventory `@oshun/studio-authoring` overlap. Evidence: duplication
      report identifies reuse, consolidation, and intentionally separate logic.
      <!-- evidence: inventory + duplication/cycle checks (a,f) =
                                                                                                                                                                                                                                                                      tara-studio-authoring-overlap.json; the reuse/consolidate/separate SELECTION
                                                                                                                                                                                                                                                                      (b-e) = studio-authoring-overlap-selection.json + tara-studio-authoring-overlap-selection.md
                                                                                                                                                                                                                                                                      (0 exact dups; 4 reuse / 0 consolidate / 5 separate over the 9 SA concern
                                                                                                                                                                                                                                                                      categories; DECISIVE FINDING: the two packages are already correctly factored —
                                                                                                                                                                                                                                                                      no consolidation debt). Named architecture ratification stays a human step (S0
                                                                                                                                                                                                                                                                      signoff); the selection is coherence-gated in CI (11 tests). -->
  - [x] S0.4.a Enumerate public and internally consumed `studio-authoring`
        contracts, actor/role logic, components, hooks, stores, utilities, and
        tests. Evidence: source-linked inventory.
        <!-- evidence: .studioAuthoringInventory — 315 exports across 9 barrel
                                                                                                                                                                                                                                                                        categories, each source-linked (.module) + kind-tagged; surfaceBuckets show
                                                                                                                                                                                                                                                                        actor-role=36, contracts-domain-logic=279, components/hooks/stores=0 (pure
                                                                                                                                                                                                                                                                        logic library — Tara's 28 UI components can NOT consolidate into it without
                                                                                                                                                                                                                                                                        a new UI layer), tests=6. generate-tara-studio-authoring-overlap.mjs, tests. -->
  - [x] S0.4.b Match exact and near-equivalent Tara capabilities by input,
        output, lifecycle, policy, audience, and failure semantics. Evidence:
        candidate overlap pairs. <!-- evidence:
        studio-authoring-overlap-selection.json .overlap — recomputed from the
        two committed inventories: 0 exact-name duplicates + 117/209 Tara
        exports sharing a CONCERN token
        (source/manifest/gate/block/review/asset/
        transition/stage/publish/concept/decision/divergence) with an SA symbol
        = concern-level candidate pairs, NOT impl duplicates. generator
        generate-studio-authoring-overlap-selection.mjs (--check gate), 11
        tests. -->
  - [x] S0.4.c Select direct reuse where ownership and behavior already match.
        Evidence: reuse rows with consumer migration path.
        <!-- evidence: .selection.reuse (4) = versioning/collaboration/asset-metadata/
                                                                                                                                                                                                                                                                        localization-workflow — neutral concerns Tara should consume from the lower
                                                                                                                                                                                                                                                                        layer; each .dispositions[].migrationPath gives the consumer migration
                                                                                                                                                                                                                                                                        (e.g. reference SA versioning contracts, drop Tara-local version helpers). -->
  - [x] S0.4.d Select consolidation where two implementations should move to one
        authoritative package. Evidence: target owner and removal sequence.
        <!-- evidence: .selection.consolidate = [] — NONE. With 0 exact duplicates + a
                                                                                                                                                                                                                                                                        clean one-way acyclic layer boundary (Tara → studio-authoring; SA does not
                                                                                                                                                                                                                                                                        import Tara), there is no consolidation debt: no two live implementations to
                                                                                                                                                                                                                                                                        collapse. A real selection outcome (empty + justified), coherence-gated
                                                                                                                                                                                                                                                                        against the 0-duplicate fact, not a deferral. -->
  - [x] S0.4.e Retain separate logic when audience, domain semantics, trust,
        lifecycle, dependency direction, or release cadence differs. Evidence:
        reviewed separation rationale.
      <!-- evidence: .selection.separate (5) = creator-roles/editorial-lifecycle/
                      taxonomy-curation/authoring-blocks/templates — meditation-specialized
                      (invitational language, seasonal registry, program cadence, plane taxonomy,
                      review-ladder grades); differ by domain semantics + audience. Each row carries
                      the retained Tara counterpart module + rationale. -->
    <!-- doc: evidence/v1-workbenches/tara-studio-authoring-overlap-selection.md -->
  - [x] S0.4.f Check that the final package graph has no duplicate exported
        contract or dependency cycle. Evidence: boundary and duplication checks.
        <!-- evidence: .dependencyChecks — 0 duplicate exported contract names
                                                                                                                                                                                                                                                                        across studio-authoring (315) and tara-workbench (209); dependency direction
                                                                                                                                                                                                                                                                        is acyclic (Tara → studio-authoring; studio-authoring does NOT import Tara),
                                                                                                                                                                                                                                                                        cycle=false. -->

- [ ] S0.5 Inventory shared contract packages and generated persistence
      registration. Evidence: authoritative owner is named for every shared
      type.
      <!-- partial: inventory + lineage + consumer graph + integrity (a-d) done for
                                                                                                                                                                                                                                                                      the Tara contract slice in evidence/v1-workbenches/tara-contract-inventory.json.
                                                                                                                                                                                                                                                                      Owner assignment (e) is a human decision and per-type owners are not yet named,
                                                                                                                                                                                                                                                                      so the parent stays unchecked. -->
  - [x] S0.5.a Enumerate shared schemas, TypeScript/Python/Rust types, protobuf,
        OpenAPI, events, generated clients, database models, and registry rows.
        Evidence: contract artifact inventory. <!-- evidence: .contractSchemas
        (98 Zod schemas, 44 enums) + .inferredTypes (98 z.infer types) +
        .persistenceRows (22 objectContract entities) + driftGuardSpecs (4), all
        source-linked. Scoped to the Tara contract slice libs/contracts/src/tara
        (the shared types the workbench-kit extraction touches).
        generate-tara-contract-inventory.mjs, --check gate, tests. -->
  - [x] S0.5.b Trace each generated artifact to its source schema and generation
        command. Evidence: source-to-generated lineage.
        <!-- evidence: .lineage — each of the 22 persistence rows resolves to its
                                                                                                                                                                                                                                                                        source Zod schema (sourceFound=true for ALL — 0 source-less rows) and the
                                                                                                                                                                                                                                                                        generation command (generate-contract-persistence-schema.ts → Prisma). -->
  - [x] S0.5.c Trace each shared type to all producers, consumers, persisted
        tables, events, and public docs. Evidence: type consumer graph.
        <!-- evidence: .consumers — 14 real importers of @oshun/contracts/tara
                                                                                                                                                                                                                                                                        categorized (bff=6, domain-lib=5, web=3); producer = libs/contracts/src/tara,
                                                                                                                                                                                                                                                                        persisted tables = the 22 objectContract rows. -->
  - [x] S0.5.d Identify duplicate, forked, unregistered, source-less, and
        generated-but-hand-edited types. Evidence: contract integrity findings.
        <!-- evidence: .findings — 0 source-less registry rows (healthy lineage);
                                                                                                                                                                                                                                                                        76 unregistered schemas surfaced (mostly composed sub-schemas/enums, noted);
                                                                                                                                                                                                                                                                        the contracts↔domain-lib direction is recorded (contracts is the lower layer,
                                                                                                                                                                                                                                                                        the domain lib CONSUMES it — the enum "mirror" is reuse, not a fork).
                                                                                                                                                                                                                                                                        generated-but-hand-edited detection needs a live generation (see S0.5.f). -->
  - [ ] S0.5.e Assign one authoritative owner, source, namespace, version
        policy, compatibility window, and deprecation path per type. Evidence:
        zero ownerless shared types.
        <!-- human: owner/version-policy assignment is a governance decision. -->
  - [ ] S0.5.f Validate persistence/model registration from a clean generation
        and real database metadata. Evidence: generation drift and registration
        checks. <!-- runtime: needs a running database + Prisma generation
        (generate-contract-persistence-schema.ts / generate:v3-schema --check);
        not run on this 16GB Mac without the dev DB up. -->

- [ ] S0.6 Record ADR-S1 for package ownership and dependency direction.
      Evidence: no proposed dependency cycle.
      <!-- partial: ADR-S1 recorded (Status: Proposed) at
                                                                                                                                                                                                                                                                      docs/adr/ADR-S1-workbench-kit-package-ownership.md with the context + graph
                                                                                                                                                                                                                                                                      snapshot (a) and options trade-off (b). The current graph is proven acyclic
                                                                                                                                                                                                                                                                      (S0.4). Parent stays unchecked: ownership assignment (c), the chosen
                                                                                                                                                                                                                                                                      direction's rules/validation (d,e), and owner approval (f) are open human
                                                                                                                                                                                                                                                                      decisions. -->
  - [x] S0.6.a Define the decision questions and current package/consumer graph.
        Evidence: ADR context and graph snapshot.
        <!-- evidence: ADR-S1 §"Context and Problem Statement" (3 decision
                                                                                                                                                                                                                                                                        questions) + §"Current package and consumer graph" (snapshot table from the
                                                                                                                                                                                                                                                                        7 committed evidence artifacts; direction proven acyclic). -->
  - [x] S0.6.b Compare retain-current, extend `studio-authoring`, create
        `workbench-kit`, and other viable ownership options. Evidence: option
        trade-off table.
        <!-- evidence: ADR-S1 §"Considered Options" — 4-option trade-off table
                                                                                                                                                                                                                                                                        (retain / extend studio-authoring / new workbench-kit / staged hybrid),
                                                                                                                                                                                                                                                                        each row grounded in the evidence (studio-authoring has no UI; S0.11 shows
                                                                                                                                                                                                                                                                        0/209 exports clear the reuse bar). -->
  - [x] S0.6.c Assign ownership for domain-neutral contracts, UI, server
        boundaries, persistence helpers, testing, and domain plugins. Evidence:
        responsibility map.
        <!-- evidence: workbench-kit-boundary.json .responsibilityMap + ADR-S1
                                                                                                                                                                                                                                                                        Decision Outcome — concern→layer: contracts→@oshun/contracts, neutral logic
                                                                                                                                                                                                                                                                        →@oshun/studio-authoring, neutral UI shell/primitives + server/testing
                                                                                                                                                                                                                                                                        helpers→@oshun/workbench-kit (proposed, S0.11-gated), domain semantics +
                                                                                                                                                                                                                                                                        plugins→domain packages, transport→BFF, composition→apps. Named-team owners
                                                                                                                                                                                                                                                                        are S0.6.f (human). generate-workbench-kit-boundary.mjs + 5 tests. -->
  - [x] S0.6.d Specify allowed and forbidden dependencies among applications,
        shared kit, studio-authoring, domain libraries, contracts, BFF, and
        adapters. Evidence: normative dependency rules.
        <!-- evidence: .dependencyRules — 6 layers (contracts<shared-logic<
                                                                                                                                                                                                                                                                        workbench-kit<domain<bff<app); dependencies may point ONLY downward (15
                                                                                                                                                                                                                                                                        allowed edges); upward + same-layer edges forbidden (21), as Nx-tag-style
                                                                                                                                                                                                                                                                        rules. -->
  - [x] S0.6.e Validate the chosen direction with a cycle simulation and Nx/tag
        rule prototype. Evidence: passing boundary proof. <!-- evidence:
        .cycleSimulation — the proposed layered graph is ACYCLIC, cross-checked
        against the current observed graph proven acyclic in S0.4
        (tara→studio-authoring→contracts). boundaryProofPasses=true; a
        deliberately inserted upward edge is caught as a cycle (regression
        test). --check gates it. -->
  - [ ] S0.6.f Obtain architecture and affected package-owner approval.
        Evidence: ADR approval record.
        <!-- human: named architecture + owner sign-off on ADR-S1 (now records the
                                                                                                                                                                                                                                                                        staged-hybrid direction as agent-adopted PENDING this named ratification). -->

- [x] S0.7 Record ADR-S2 for generic parameters and extension points. Evidence:
      examples cover content-authoring and operations-console archetypes. <!--
      evidence: docs/adr/ADR-S2-generic-parameters-and-extension-points.md +
      evidence/v1-workbenches/workbench-kit-extension-model.json + the compiling
      reference evidence/v1-workbenches/workbench-kit-extension-model/ (both
      archetypes as COMPILING plugins). generator
      generate-workbench-kit-extension-model.mjs (--check gate + tsc --noEmit
      gate in verify), 9 tests. Named architecture ratification stays a human
      step. -->
  - [x] S0.7.a Inventory variability in identity, stage, block, score, gate,
        artifact, preview, inspector, import, publish, and route semantics.
        Evidence: variability table.
        <!-- evidence: .variability — all 11 dimensions tabled, each with a mechanism +
                                                                                                                                                                                                                                                                    rationale; 6/11 ground to an S0.2-extracted neutral contract. -->
  - [x] S0.7.b Define which variability belongs in generic type parameters,
        discriminated interfaces, plugin capabilities, composition slots, or
        domain-owned adapters. Evidence: extension mechanism mapping.
        <!-- evidence: .variability[].mechanism — each dimension mapped to exactly one of
                                                                                                                                                                                                                                                                    the 5 mechanisms (2 generic-type-param, 1 discriminated-interface, 1 plugin-
                                                                                                                                                                                                                                                                    capability, 3 composition-slot, 4 domain-adapter); all 5 mechanisms used. -->
  - [x] S0.7.c Define invariants the shared kit owns and behavior plugins may
        not override. Evidence: normative core contract.
        <!-- evidence: .invariants (6) = authorization/audit/idempotency/honest-capability/
                                                                                                                                                                                                                                                                    no-escape-hatch/deletion-threshold, each sourced to S0.3/S0.8/S0.11;
                                                                                                                                                                                                                                                                    encoded in kit-contract.ts enforceInvariants() + a closed MutationEnvelope. -->
  - [x] S0.7.d Define optional capability discovery and honest unsupported/not-
        configured behavior. Evidence: capability negotiation examples.
        <!-- evidence: .capabilityModel — capabilities()/supports() discovery + typed
                                                                                                                                                                                                                                                                    CapabilityResult<T> ({configured:false,reason}); the ops plugin honestly
                                                                                                                                                                                                                                                                    declares canPublish=false (index.ts), proving negotiation is real. -->
  - [x] S0.7.e Model one Tara-like content-authoring plugin without leaking
        meditation types into the shared API. Evidence: compiling example. <!--
        evidence: workbench-kit-extension-model/plugin-content-authoring.ts —
        compiles under strict tsc --noEmit; leak check enforces 0
        meditation/spark/plane/ cadence/invitational tokens in the CODE
        (comments-stripped). -->
  - [x] S0.7.f Model one Aja/Bellona-like operations plugin with jobs, queues,
        resources, and host targets. Evidence: compiling example.
        <!-- evidence: workbench-kit-extension-model/plugin-operations.ts — compiles under
                                                                                                                                                                                                                                                                    strict tsc; ProcessingJob models host target + resourceUnits + queueDepth;
                                                                                                                                                                                                                                                                    disjoint entity/stage/artifact types from the authoring plugin. -->
  - [x] S0.7.g Reject generic parameters or slots that have only one concrete
        use or permit unsafe arbitrary escape hatches. Evidence: abstraction
        challenge review.
      <!-- evidence: .rejections (3) = single-use domain generic (TMeditation*),
                      arbitrary escape hatch (extraData:any/raw:unknown bypassing invariants),
                      kit-level score() computing a domain number. -->
    <!-- doc: evidence/v1-workbenches/workbench-kit-extension-model/ (compiling reference) -->

- [x] S0.8 Record ADR-S3 for event, audit, idempotency, and transaction
      boundaries. Evidence: failure scenarios are diagrammed.
      <!-- evidence: docs/adr/ADR-S3-event-audit-idempotency-transaction-boundaries.md +
                                                                                                                                                                                                                                                                      event-audit-boundaries.json. a=mutation-effect-matrix, b/c=transaction-
                                                                                                                                                                                                                                                                      boundaries (34 local-ACID + 8 outbox, 0 saga + identity contract), d/e=
                                                                                                                                                                                                                                                                      delivery/audit semantics, f=7 mermaid failure diagrams (all coherence-gated).
                                                                                                                                                                                                                                                                      Named architecture ratification stays a human step. -->
  - [x] S0.8.a Inventory shared commands and mutations that produce persistence,
        event, job, blob, provider, publish, notification, or audit effects.
        Evidence: mutation-effect matrix.
        <!-- evidence: .mutations — all 42 Tara BFF mutations classified across the
                                                                                                                                                                                                                                                                        8 effect types (persistenceWrite 32, event/audit 34, provider 4, publish 3,
                                                                                                                                                                                                                                                                        blob 2, notification 2, job 0). crossBoundaryMutations (8) isolates the ones
                                                                                                                                                                                                                                                                        touching an external boundary (the S0.8 outbox/saga candidates); every write
                                                                                                                                                                                                                                                                        mutation audits (templated event names handled). generate-tara-mutation-
                                                                                                                                                                                                                                                                        effect-matrix.mjs + 6 tests, wired into inventory/verify. -->
  - [x] S0.8.b Define authoritative transaction boundaries and when outbox,
        inbox, or saga behavior is required. Evidence: boundary decision table.
        <!-- evidence: tara-transaction-boundaries.json .boundaryDecisionTable —
                                                                                                                                                                                                                                                                        the standard transactional-outbox rule applied to the S0.8.a effect matrix:
                                                                                                                                                                                                                                                                        34 local ACID transactions (persistence+audit only) + 8 transactional-outbox
                                                                                                                                                                                                                                                                        mutations (persistence then provider/publish/blob/notification), 0 sagas
                                                                                                                                                                                                                                                                        (no external effect must be atomic with persistence). Per-route pattern +
                                                                                                                                                                                                                                                                        rationale. generate-tara-transaction-boundaries.mjs + 5 tests. -->
  - [x] S0.8.c Define command, idempotency, event, trace, audit, job, and
        external-operation identity and correlation rules. Evidence: identity
        contract.
        <!-- evidence: .identityContract — command id (randomUUID/server id;
                                                                                                                                                                                                                                                                        idempotency-capable routes carry a randomUUID command id), event id (audit
                                                                                                                                                                                                                                                                        event name + target id per write), correlation (per-request id through the
                                                                                                                                                                                                                                                                        guarded handler + audit), external-operation id (outbox row correlates the
                                                                                                                                                                                                                                                                        durable write with its async dispatch). Derived per-mutation from the matrix. -->
  - [x] S0.8.d Define event ordering, delivery, duplication, replay,
        compatibility, and poison handling. Evidence: delivery semantics. <!--
        evidence: event-audit-boundaries.json .deliverySemantics (6) —
        at-least-once, per-aggregate ordering, dedup-by-externalOperationId,
        durable replay, versioned compatibility, dead-letter poison. Over the 8
        outbox mutations (S0.8.b). generate-event-audit-boundaries.mjs (--check
        gate), 8 tests. -->
  - [x] S0.8.e Define audit atomicity, refusal/failure capture, immutability,
        redaction, retention, and evidence linkage. Evidence: audit semantics.
        <!-- evidence: .auditSemantics (6) — audit atomic-in-tx with the write (S0.8.b
                                                                                                                                                                                                                                                                local-ACID), refusals/failures captured, append-only immutable, redact-at-
                                                                                                                                                                                                                                                                write, RetentionPolicy-governed, evidence-linked actor→command→effect (S0.8.c). -->
  - [x] S0.8.f Diagram failure before commit, after commit/before dispatch,
        duplicate delivery, worker loss, external success/local timeout,
        compensation failure, and replay. Evidence: reviewed failure diagrams.
      <!-- evidence: .failureScenarios (7) + docs/adr/ADR-S3-*.md mermaid sequence
                      diagrams for each. compensation-failure is NOT APPLICABLE (sagaRequired=0 —
                      coherence-checked); the only durability seam is after-commit/before-dispatch,
                      closed by the durable outbox; dedup-by-externalOperationId makes 3/5/7 safe. -->
    <!-- doc: docs/adr/ADR-S3-event-audit-idempotency-transaction-boundaries.md -->

- [x] S0.9 Record ADR-S4 for UI composition and theming. Evidence: existing
      Oshun, Isis, Yemaya, Euterpe, and Bellona design systems are reconciled.
      <!-- evidence: docs/adr/ADR-S4-ui-composition-and-theming.md +
                                                                                                                                                                                                                                                                      workbench-kit-theme-contract.json + the workbench-kit-theme/ prototype.
                                                                                                                                                                                                                                                                      a=design-system-comparison, b=ui-composition-boundary, c=9-category token
                                                                                                                                                                                                                                                                      contract (6 mapped/3 introduced), d=scoped isolation prototype, e=4 archetype
                                                                                                                                                                                                                                                                      compositions (no clone), f=6-condition a11y matrix. All coherence-gated.
                                                                                                                                                                                                                                                                      Named architecture ratification stays a human step. -->
  - [x] S0.9.a Inventory shells, navigation, layout primitives, tokens, themes,
        typography, icons, component libraries, overlays, and interaction
        patterns in all five systems. Evidence: design-system comparison.
        <!-- evidence: .systems — per-system token defs/uses, css/inline/styled
                                                                                                                                                                                                                                                                        approach, shell/layout components, theme signals, interaction primitives
                                                                                                                                                                                                                                                                        for all 5 (oshun/isis/yemaya/euterpe/bellona). Key finding: the 5 do NOT
                                                                                                                                                                                                                                                                        share a styling approach — token OWNERS oshun(600)/yemaya(67), token
                                                                                                                                                                                                                                                                        CONSUMER isis(87 uses, 0 local defs), non-token euterpe(inline)/bellona —
                                                                                                                                                                                                                                                                        so a uniform shared token contract (S0.9.c) is not adoptable as-is.
                                                                                                                                                                                                                                                                        generate-design-system-comparison.mjs + 7 tests, wired into inventory/verify. -->
  - [x] S0.9.b Define shared shell/primitive responsibilities versus
        host/domain-owned composition, vocabulary, information architecture, and
        specialist visualization. Evidence: ownership boundary.
        <!-- ui-composition-boundary.json: 10 UI concerns assigned kit-owned (5:
                                                                                                                                                                                                                                                                        neutral app shell/nav, layout primitives, interaction primitives, the token
                                                                                                                                                                                                                                                                        CONTRACT/role-names, and the theme-injection seam) vs host/domain-owned (5:
                                                                                                                                                                                                                                                                        domain vocabulary, information architecture, domain composition, specialist
                                                                                                                                                                                                                                                                        visualization e.g. DAW/3D/mocap, and the token VALUES). Derived from the
                                                                                                                                                                                                                                                                        S0.6 layering fact (kit = neutral UI layer) + the S0.9.a divergence
                                                                                                                                                                                                                                                                        (oshun/yemaya own tokens, isis consumes, euterpe/bellona non-token — so
                                                                                                                                                                                                                                                                        token VALUES cannot be shared and stay host-owned; only the naming CONTRACT
                                                                                                                                                                                                                                                                        is neutral). generate-ui-composition-boundary.mjs + 6 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. S0.9.c token contract (creative), S0.9.d isolation
                                                                                                                                                                                                                                                                        prototype (code), S0.9.f a11y matrix (runtime) remain unchecked. -->
  - [x] S0.9.c Define token contracts for color, typography, spacing, density,
        elevation, motion, focus, semantic states, and data visualization.
        Evidence: token mapping. <!-- evidence:
        workbench-kit-theme-contract.json .tokenContract — 9 categories of
        --wk-_ role names; 6 MAPPED to real oshun --l-_ tokens
        (color/typography/ elevation/motion/semantic-states/data-viz), 3
        CONTRACT-INTRODUCES (spacing/ density/focus, which oshun does not
        tokenize). generate-workbench-kit-theme- contract.mjs (--check gate), 9
        tests. -->
  - [x] S0.9.d Define host theme injection without global CSS leakage, runtime
        class collisions, duplicated providers, or inaccessible overrides.
        Evidence: theming isolation prototype. <!-- evidence: prototype
        workbench-kit-theme/tokens.css — all tokens scoped to
        [data-workbench-theme] (0 --wk-_ in a bare :root block, checked), --wk-_
        names not classes (no collision), single scoped provider (nesting
        re-scopes via the cascade), reduced-motion + forced-colors handled at
        the contract level so a host override cannot defeat them. .isolation (4
        props) validated against the CSS. -->
  - [x] S0.9.e Reconcile Oshun calm authoring, Isis operator, Yemaya/Euterpe
        creative production, and Bellona control-room density without forcing
        one visual clone. Evidence: archetype compositions. <!-- evidence:
        prototype workbench-kit-theme/archetypes.css — 4 archetypes
        (calm-authoring/operator/creative-production/control-room) consume the
        SAME contract with divergent density/motion/contrast values (≥2 distinct
        --wk-density, checked) — reconciled, NOT a visual clone. -->
  - [x] S0.9.f Validate themes, densities, zoom, reduced motion, and forced
        colors with shared components. Evidence: cross-host visual/accessibility
        matrix.
      <!-- evidence: .a11yMatrix — 6 conditions (light/dark theme, compact/comfortable
                      density, zoom, reduced-motion, forced-colors) each mapped to a contract mechanism
                      ASSERTED PRESENT in the prototype CSS (data-density/rem/prefers-reduced-motion/
                      forced-colors). This validates the a11y CONTRACT coverage; live per-pixel cross-host
                      screenshots are the runtime layer (noted honestly in the finding). -->
    <!-- doc: docs/adr/ADR-S4-ui-composition-and-theming.md + workbench-kit-theme/ prototype -->

- [x] S0.10 Record ADR-S5 for compatibility and incremental migration. Evidence:
      Tara can migrate without a flag day.
      <!-- evidence: docs/adr/ADR-S5-compatibility-and-incremental-migration.md +
                                                                                                                                                                                                                                                                      migration-plan.json. a=migration-surface register, b=additive coexistence
                                                                                                                                                                                                                                                                      seams, c=13-slice ordered list (S0.2 contracts), d=slice transition contract
                                                                                                                                                                                                                                                                      (no dual-write), e=mixed-version journey (routes/store/deep-links unchanged),
                                                                                                                                                                                                                                                                      f=milestone-gated retirement (S0.11). Tara migrates WITHOUT a flag day; all
                                                                                                                                                                                                                                                                      coherence-gated. Named architecture ratification stays a human step. -->
  - [x] S0.10.a Inventory Tara public imports, routes, state stores, contracts,
        persisted data, events, generated artifacts, and current consumers.
        Evidence: migration surface register.
        <!-- evidence: .register — consolidates all 8 named dimensions from the six
                                                                                                                                                                                                                                                                        committed inventories (publicImports 209, routes 70, durableStores 1,
                                                                                                                                                                                                                                                                        contracts 98, persistedEntities 22, auditEvents 32, generatedArtifactRows 22,
                                                                                                                                                                                                                                                                        domainLibConsumers 1), each with source provenance; migrationRiskFlags surface
                                                                                                                                                                                                                                                                        151 unused exports + 11 untested routes + 25 untested UI components.
                                                                                                                                                                                                                                                                        generate-tara-migration-surface.mjs + 7 tests, wired into inventory/verify. -->
  - [x] S0.10.b Define additive compatibility seams, adapters, aliases, and
        version negotiation needed while old and new packages coexist. Evidence:
        coexistence architecture. <!-- evidence: migration-plan.json
        .coexistenceSeams (4) — adapter (S0.7 domain adapters), alias (re-export
        kit under Tara's names), version-negotiation (S0.7.d capability
        discovery), no-breaking-change (Tara keeps all 70 routes).
        generate-migration-plan.mjs (--check gate), 9 tests. -->
  - [x] S0.10.c Slice migration by independently verifiable capability rather
        than package-wide replacement. Evidence: ordered migration slice list.
        <!-- evidence: .slices — the 13 S0.2 neutral contracts as a gapless 1..13
                                                                                                                                                                                                                                                        total order by risk: tier1 EvidenceGroundingPanel/SourceLibrary (Veritas 2nd
                                                                                                                                                                                                                                                        consumer clears S0.11 first), tier2 the 0-signal neutral contracts, tier3 the
                                                                                                                                                                                                                                                        mixed-split ones needing an adapter. Capability-sliced, not package-wide. -->
  - [x] S0.10.d Define per-slice enablement, dual-read/write avoidance or
        reconciliation, rollback, evidence, and removal conditions. Evidence:
        slice transition contract.
        <!-- evidence: .sliceTransitionContract — lifecycle enable→verify→coexist→remove
                                                                                                                                                                                                                                                        (flag-gated, rollback = keep Tara inline default, evidence = slice tests pass,
                                                                                                                                                                                                                                                        removal = S0.11 ≥2 consumers). NO dual-write: UI extraction over the SAME Tara
                                                                                                                                                                                                                                                        store (S0.8 keeps the store Tara-owned) — nothing to reconcile. -->
  - [x] S0.10.e Define route/state/deep-link compatibility and preserve current
        Tara behavior for partially migrated users. Evidence: mixed-version
        journey plan.
        <!-- evidence: .mixedVersionJourney — 70 routes + 1 durable store + the
                                                                                                                                                                                                                                                        /studio/tara-workbench/* deep-link namespace UNCHANGED; the migration swaps a
                                                                                                                                                                                                                                                        component implementation, not a route/store/URL, so a partially-migrated user
                                                                                                                                                                                                                                                        sees identical behavior (the S0.2 seam is the interaction contract). -->
  - [x] S0.10.f Define the final contract period, legacy consumer migration, and
        old implementation deletion gate. Evidence: dated retirement plan.
      <!-- evidence: .retirementPlan — milestone-gated T0..T3 (T0=first Veritas 2nd
                      consumer clears S0.11), contract period = per-slice until S0.11, single legacy
                      consumer @oshun/bff re-points, deletion gate = S0.11 (≥2 consumers + passing
                      tests). Absolute calendar dates are a named-ratification input (honest scope). -->
    <!-- doc: docs/adr/ADR-S5-compatibility-and-incremental-migration.md -->

- [ ] S0.11 Define a deletion threshold: no shared abstraction ships with only
      one real consumer unless the reuse is already proven. Evidence: each
      exported module lists at least two consumers or an approved exception.
      <!-- partial: the threshold, definition, report, eligibility check, and CI
                                                                                                                                                                                                                                                                      regression (a,b,c,f) are implemented in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/deletion-threshold.json + deletion-threshold-gate.mjs.
                                                                                                                                                                                                                                                                      The parent stays unchecked because d (exception records) and e (delete/return
                                                                                                                                                                                                                                                                      one-consumer abstractions) are human decisions not yet executed — no shared
                                                                                                                                                                                                                                                                      kit has shipped, so 0/209 candidates currently clear the bar. -->
  - [x] S0.11.a Define a real consumer as a production capability with distinct
        domain ownership or archetype, excluding tests, demos, fixtures,
        examples, aliases, and reexports. Evidence: consumer definition.
        <!-- evidence: .summary.realConsumerDefinition (REAL_CONSUMER_DEFINITION) —
                                                                                                                                                                                                                                                                        production import by a DISTINCT consumer project (domain/archetype); tests,
                                                                                                                                                                                                                                                                        fixtures, demos, aliases, re-exports excluded; identity = importing package.
                                                                                                                                                                                                                                                                        Implemented in countRealConsumers/realConsumersByExport, unit-tested. -->
  - [x] S0.11.b Count consumers per proposed exported module and symbol from the
        authoritative import/capability graph. Evidence: export-to-consumer
        report.
        <!-- evidence: .report — per exported symbol, its distinct real consumer
                                                                                                                                                                                                                                                                        projects + realConsumerCount, computed from tara-import-graph.json (the
                                                                                                                                                                                                                                                                        authoritative import graph, S0.1). -->
  - [x] S0.11.c Require two implemented consumers before extraction where
        behavior is not already domain-neutral and stable. Evidence: extraction
        eligibility check.
        <!-- evidence: .report[].eligibility (threshold=2): eligible / single-consumer
                                                                                                                                                                                                                                                                        / zero-consumer. Result: 0/209 Tara exports eligible (56 single, 153 zero) —
                                                                                                                                                                                                                                                                        none extractable as-is without a second-domain consumer or an exception. -->
  - [ ] S0.11.d For exceptions, record imminent second consumer, proven prior
        reuse, cost of delay, abstraction boundary, owner, and expiry. Evidence:
        approved exception record.
        <!-- human: an approved, time-boxed exception record per exception. -->
  - [ ] S0.11.e Delete or return one-consumer abstractions to the owning domain
        before shared-kit release. Evidence: zero unexplained one-consumer
        exports.
        <!-- human/impl: acts on S0.11.c results at shared-kit release; no kit
                                                                                                                                                                                                                                                                        shipped yet, so nothing to delete/return. -->
  - [x] S0.11.f Recheck consumer counts on every shared export addition/removal
        in CI. Evidence: deliberate one-consumer regression test. <!-- evidence:
        deletion-threshold-gate.mjs --check runs in
        verify:inventory:v1-workbenches (CI) — drift + enforcement (exit 1 once
        a package is in SHARED_KIT_PACKAGES); deletion-threshold-gate.test.mjs
        has a deliberate single-consumer + test-only-excluded regression case.
        -->
- [ ] S0.12 Ratify the shared capability matrix with domain owners. Evidence:
      review records list accepted, rejected, and domain-owned rows.
  - [x] S0.12.a Prepare per-domain rows with capability semantics, proposed
        shared boundary, extension point, consumer, dependency, and evidence.
        Evidence: seven review packets.
        <!-- shared-capability-review-packets.json: SEVEN packets, one per owner
        S0.12.c names, each carrying all 13 neutral contracts from the S0.2
        extraction matrix — 91 rows. Every row carries the six things this cell
        asks for and nothing it cannot know: semantics verbatim from the
        extraction matrix, the kit layer and the edges it may and may not use
        from the S0.6 boundary proof, the ADR-S2 variability dimension mapped to
        that contract, the consumer, the invariants adoption inherits, and
        source-linked evidence.
        THE COLUMN THAT MAKES A PACKET WORTH AN OWNER'S TIME is the consumer, and
        it is COMPUTED: does this domain already have something of that name?
        52 rows ask a domain to REPLACE OR ALIGN something it has and 39 to ADOPT
        something new, and the two are different decisions with different costs —
        a matrix that did not separate them would ask seven owners the same
        question about materially different work. PipelineFunnel is the only
        contract all seven domains already have, which makes it the strongest
        extraction candidate and the one most likely to mean seven different
        things. AgentAutonomyBudget is the only one NO domain has, so it is
        Tara-only today and cannot clear the S0.11 two-consumer bar on this
        matrix alone. 42 of 91 rows carry no ADR-S2 variability dimension at all
        and ask the owner to accept the contract as-is or name the variation they
        need.
        ALL 91 DECISIONS ARE PENDING with a null decider, and `--check` exits
        non-zero if that ever stops being true. This cell PREPARES the review;
        S0.12.b-g are named owner signoffs, and an agent that filled them in
        would be forging the exact artifact the cell exists to produce.
        generate-shared-capability-review-packets.mjs + 24 tests, wired into
        inventory/verify + CI. -->
        <!-- The one judgment is the recognition VOCABULARY, so it is published
        per contract with its reason and is disputable by the owner reading it.
        Two of its terms were removed after the first run reported overlap that
        was not there, and both are now regression tests: `library` matched every
        symbol in `@isis/3d-asset-library` and reported a licence-assignment
        function as a catalogue browser — a fact about a DIRECTORY NAME, fixed by
        matching the symbol rather than the package-qualified behaviour string;
        and the bare verb `assign` fired on assigning a licence, a colour or a
        bitrate, none of which is reviewer assignment. Prisma MIGRATIONS are
        excluded beside the schema for the same reason: a migration is the
        history of a table and the schema is its current truth, so counting both
        inflates a domain's apparent overlap in proportion to how long it has
        existed. Terms match WORDS, not substrings — `gate` does not fire on
        `aggregate`, which is tested — and each row publishes its match count by
        capability kind and whether the matches are concentrated in one or two
        projects or scattered, so a weak match is visible as one.
        notDerivableFromSource says the thing that matters: a vocabulary finds
        capabilities CALLED the same, and whether they ARE the same is the
        judgment the review exists to make. An `adopt` row is an offer, not a
        gap. -->
  - [ ] S0.12.b Obtain Tara owner review as the extraction source and behavioral
        regression authority. Evidence: signed Tara comments.
  - [ ] S0.12.c Obtain Isis, Metis, Yemaya, Veritas, Euterpe, Aja, and Bellona
        owner decisions for their candidate rows. Evidence: seven named domain
        review records.
  - [ ] S0.12.d Record accepted shared capabilities with named consumers and
        invariants. Evidence: accepted row register.
  - [ ] S0.12.e Record rejected capabilities with rationale and any retirement
        or redesign action. Evidence: rejected row register.
  - [ ] S0.12.f Record domain-owned capabilities with authoritative package and
        explicitly forbidden shared semantics. Evidence: domain-owned row
        register.
  - [ ] S0.12.g Resolve conflicting owner decisions through product/architecture
        governance rather than averaging incompatible requirements. Evidence:
        zero unresolved matrix rows.

### S1 — Package, contracts, and extension model

- [x] S1.1 Scaffold `libs/oshun/workbench-kit` using repository ESM, Nx,
      TypeScript, lint, and Vitest conventions. Evidence: targeted lint,
      typecheck, test, and build pass.
      <!-- libs/oshun/workbench-kit, mirroring its sibling @oshun/studio-authoring
      (the other half of ADR-S1's layering) rather than inventing a shape. All
      five targets pass on this branch: lint, typecheck, test (23), build, and
      a `smoke` target that consumes the BUILT package. -->
  - [x] S1.1.a Create the package manifest, project configuration, source/test
        roots, README, ownership metadata, and initial public entry point.
        Evidence: scaffold file review.
        <!-- package.json / project.json / src / README with the ownership table
        (layer, scope, owning ADRs, ledger cells, and S0.6.f approval recorded as
        PENDING rather than implied). The initial entry point is the SIX
        kit-owned invariants from ADR-S2, as a runtime-checked closed mutation
        envelope rather than a document a plugin is trusted to have read — the
        load-bearing one being no-escape-hatch, since every other invariant is
        defeated by a single `raw`/`any` field, so the envelope refuses UNKNOWN
        keys as well as missing ones. That is the part of the kit that does not
        wait on S0.12: the export map (S1.2) is drawn from the RATIFIED
        capability matrix and all 91 of its rows are still pending, so shipping a
        capability now would prejudge the decision the review exists to make. The
        rules are restated in source (a library must not read an audit artifact
        at runtime) and a test asserts they still equal the artifact verbatim,
        so the restating cannot drift. -->
  - [x] S1.1.b Configure ESM and TypeScript module resolution, project
        references, declaration output, source maps, and repository aliases.
        Evidence: emitted declaration and import inspection.
        <!-- type: module; moduleResolution bundler; tsconfig.json references
        lib+spec; tsconfig.lib.json emits declaration + declarationMap; the alias
        `@oshun/workbench-kit` is in tsconfig.base.json. Emitted output inspected:
        index.js/.d.ts/.js.map/.d.ts.map + invariants.*. -->
  - [x] S1.1.c Configure Nx tags, named inputs, cacheable
        lint/typecheck/test/build targets, and affected-file inputs. Evidence:
        project target inspection.
        <!-- tags scope:oshun / layer:workbench-kit / type:lib — the layer tag is
        what makes ADR-S1's direction enforceable by a boundary check instead of
        by review. Targets: build (@nx/js:tsc with declared outputs, so it
        caches), lint, test, typecheck, plus `smoke` dependsOn build. -->
  - [x] S1.1.d Inherit repository lint/format rules without blanket local
        disables. Evidence: lint rejects a deliberate invalid fixture.
        <!-- No local eslint config and no file-level disables: the package
        inherits the root flat config. Proven both ways — the real source lints
        clean, and a deliberate invalid fixture (`any` params + an unused local)
        was rejected with 1 error and 2 warnings before being deleted. A lint
        that passes everything is indistinguishable from a lint that is not
        running. -->
  - [x] S1.1.e Configure Vitest environment, setup, coverage boundaries, and
        test-only entry points. Evidence: sample unit and coverage run.
        <!-- vitest.config.ts: node environment, root-pinned, include
        src/**/*.{test,spec}.ts, v8 coverage. passWithNoTests is FALSE in the Nx
        target, so an empty suite fails rather than reporting green. 23 tests
        cover every invariant branch, including the ones that make the closed
        envelope worth having: a truthy value where a boolean verdict belongs, an
        allowed decision that names no grant, a refusal with no reason,
        expectedRevision undefined vs explicit null, and the same consumer twice
        counting as one. -->
  - [x] S1.1.f Configure deterministic build output and validate consumption
        from an external ESM fixture. Evidence: packed-consumer smoke test.
        <!-- tools/packed-consumer-smoke.mjs, wired as its own Nx target that
        dependsOn build. THIS CAUGHT A REAL DEFECT the other four targets could
        not: the manifest declared `main`/`types`/`exports` as `./src/index.ts`
        (copied from the sibling, where nothing exercises them), so the built
        package was one Node could not import at all. Lint, typecheck, test and
        build were all green over it. The entries now name the emitted output and
        the manifest records why; in-workspace consumers resolve through the
        tsconfig path alias and never read those fields. The smoke test imports
        the emitted entry, checks every declared export is there, and then
        EXERCISES it — a valid envelope accepted and an escape-hatch field
        refused — because an emitted module that enforced nothing would pass an
        export check. -->
  - [x] S1.1.g Run the four targeted targets sequentially on the current branch.
        Evidence: archived passing command output.
        <!-- lint PASS, typecheck PASS, test PASS (23), build PASS, run one after
        another via `npx nx <target> @oshun/workbench-kit`; smoke PASS on top.
        Archived as a repeatable TARGET rather than a transcript: a pasted log
        proves a machine once, and `pnpm nx smoke @oshun/workbench-kit` proves it
        every time. -->
        <!-- CORRECTION (S1.4): the `typecheck` PASS recorded above and in S1.1.b
        was VACUOUS. `tsc --noEmit` against a solution-style tsconfig with empty
        `files`/`include` compiles nothing and exits 0 — the target was green over
        a deliberately injected `const x: number = 'nope'`. Fixed in project.json
        to `tsc --noEmit -p tsconfig.lib.json && tsc --noEmit -p
        tsconfig.spec.json`, and verified to FAIL on an injected error in src and
        again in a spec. The four targets do now all check something; three of
        them always did. -->
- [x] S1.2 Add the package export map without deep-import escape hatches.
      Evidence: public API test rejects unexported imports.
      <!-- evidence/v1-workbenches/workbench-kit-export-map.json, generated and
      gated by scripts/v1-workbenches/generate-workbench-kit-export-map.mjs
      (--check wired into verify:inventory:v1-workbenches + CI, 31 tests). The
      escape hatch this guards against is not mainly `./src/*` in a manifest —
      it is the slower one where the kit grows an entry point nobody ratified,
      because editing `exports` is easier than getting seven owners to agree. So
      the approved set is DERIVED and the manifest is checked against it. -->
  - [x] S1.2.a Enumerate approved root and feature subpath entry points from the
        ratified shared capability matrix. Evidence: public API list.
        <!-- .publicApi — three entry points, each naming what authorizes it.
        A capability subpath exists only when the S0.12 matrix has ACCEPTED that
        contract for >=2 domains (S0.11: one domain accepting a shared contract
        is a domain abstraction with extra steps). All 91 rows are pending, so
        the capability-derived set is EMPTY and the map contains only what a
        kit-owned ADR decision authorizes: the ADR-S2 invariants, the invariants
        subpath, and the test corpus. The gate fails the moment a row is
        accepted with no entry point, OR an entry point exists with nothing
        authorizing it — both directions, because a map that only caught the
        second would let ratified capabilities go unshipped silently. -->
  - [x] S1.2.b Map each entry point to ESM implementation and types without
        wildcard or source-directory exports. Evidence: manifest review.
        <!-- checkManifest: exports must EQUAL the derived set (both
        directions), no `*` key, no bare-string target (which resolves no
        types), `import` ends .js and `types` ends .d.ts, and no target that is
        TypeScript source or reaches into a src/ directory. That last check
        turned up a real defect: `.d.ts` ends with `.ts`, so the first version
        flagged every conforming entry point — the one-character difference is
        now its own test. The build had to change with it: @nx/js:tsc emitted
        under dist/.../src/, so a manifest could only name `./src/index.js`;
        project.json now sets the executor's rootDir and output is flat. -->
  - [x] S1.2.c Separate browser, React, server, contract, and test-only entry
        points so incompatible runtimes cannot import one another. Evidence:
        runtime boundary tests.
        <!-- RUNTIME_CLASSES: a six-class lattice (contract/neutral/browser/
        react/server/test) with an explicit may-import set, forbidden module
        specifiers and forbidden ambient globals, enforced over the REAL module
        graph via the TypeScript AST — transitively, so a violation two hops
        from the entry point is still the entry point's. The two edges that
        matter are browser-may-not-reach-server and its mirror. Today's kit
        occupies two classes (contract, test), so the lattice is proven on
        deliberate violating fixtures as well: a browser entry reaching
        node:fs, a server entry touching `document`, a contract entry importing
        react, and a browser->server edge, each fed a MIXED graph so a gate that
        rejects everything is distinguishable from one that rejects the right
        thing. The DOM-global check is a documented approximation (free
        identifiers, not scope resolution) and both its directions — flagged
        bare `document`, unflagged `ctx.document`/local/parameter/object-key —
        are tested. -->
  - [x] S1.2.d Reject internal source paths and undeclared subpaths in workspace
        and packed-package consumer fixtures. Evidence: negative import tests.
        <!-- BOTH resolution modes, because they are different mechanisms.
        Packed: tools/packed-consumer-smoke.mjs builds a real install (a temp
        dir whose node_modules/@oshun/workbench-kit symlinks to dist) and
        requires ERR_PACKAGE_PATH_NOT_EXPORTED for ./src/index.js,
        ./invariants.js, ./testing.js, ./package.json and a never-existed
        subpath — Node's exports resolution answers, not this script. Proven by
        mutation: adding `./*` to the built manifest made all five resolve.
        Workspace: an in-workspace consumer NEVER READS package.json at all, so
        a sealed manifest plus one `@oshun/workbench-kit/*` alias is a package
        that is closed to outsiders and wide open to the seven domains that use
        it — checkAliases + public-api.spec assert the alias table has no
        wildcard and equals the approved specifiers exactly. Also proven by
        mutation: adding the wildcard alias to tsconfig.base.json failed the
        gate and two spec cases. The sibling @oshun/studio-authoring ships
        exactly that alias, which is why absence is asserted, not assumed. -->
  - [x] S1.2.e Import every declared subpath from both workspace and built
        package modes. Evidence: positive public API matrix.
        <!-- Workspace: public-api.spec imports every subpath from the artifact
        (not a hand-written list) through the tsconfig aliases, which
        vitest.config.ts derives from tsconfig.base.json rather than duplicating
        — a duplicated alias table would let the spec pass against aliases that
        do not exist, which is the one thing it is for. Packed: the smoke test
        imports all three from the symlinked install. Both then assert the root
        and the /invariants subpath are the SAME module instance, because two
        copies is how `instanceof KitInvariantError` starts failing for one
        consumer and not another with no message that explains why. Ordering
        gotcha found live: Vite alias matching is first-prefix-wins, so a
        root-first table rewrote `/invariants` to `.../index.ts/invariants`;
        the aliases are sorted longest-first. -->
  - [x] S1.2.f Scan production bundles for test builders, fixtures, server code,
        and unintended side effects. Evidence: bundle inspection.
        <!-- Over the EMITTED dist, not source. (1) No production entry point's
        emitted graph may reach the test-only module — this is the check that
        stops an actor-decision forger shipping in a bundle, and it is why
        `testing` is a separate entry point rather than part of the root. (2) No
        production module may do work at import time, which is what
        `sideEffects: false` promises the bundler that will drop it;
        deliberately stricter than the ECMAScript notion (any top-level call,
        including one that only computes a constant), stated as such, and the
        test-only entry is exempt because it is never in a production bundle to
        be dropped from. Both proven by mutation on the real package: making
        index.ts re-export a builder produced the leak violation from two
        checks, and adding one computed const to invariants.ts failed the scan.
        That second run reported the same defect twice — invariants is reached
        from both production entry points — so the scan now dedupes by module;
        one defect reported twice reads as two defects. -->
        <!-- Server code in a production bundle is covered by the S1.2.c lattice
        rather than a second scan: `server` is a runtime class, so a production
        browser/react entry that reached one would fail forbidden-class-edge and
        forbidden-node-builtin. No server entry point exists today. -->
- [x] S1.3 Add path mapping and dependency tags. Evidence: Nx boundary checks
      pass.
      <!-- evidence/v1-workbenches/workbench-kit-dependency-constraints.json +
      generate-workbench-kit-dependency-constraints.mjs (--check in
      verify:inventory:v1-workbenches + CI, 20 tests), plus two live Nx targets:
      `alias-resolution` (tsc) and `boundary-fixtures` (the real ESLint).
      S0.6 DECIDED the layering; the gap between decided and enforced is where
      layering dies, so this cell measures that gap instead of asserting it
      closed. HEADLINE: of the six S0.6 layers, `layer:bff` has ZERO carrier
      projects, so a constraint naming it passes every build without ever
      matching an import — the report says `unenforceable-no-carriers` for those
      two edges rather than counting them enforced. `layer:shared-logic` had
      zero too until @oshun/studio-authoring was tagged as the layer S0.6 names
      it. -->
  - [x] S1.3.a Add canonical TypeScript path aliases for approved package entry
        points only. Evidence: resolution tests.
        <!-- tools/alias-resolution.mjs, its own Nx target. The S1.2 spec matrix
        runs under Vitest, which resolves through VITE's alias plugin — a
        different resolver from the `tsc` every consumer actually uses, and the
        two disagree in ways that matter (Vite takes the longest prefix,
        TypeScript matches `paths` by specificity then checks the file exists).
        So this asks TypeScript, from a consumer position, both ways: every
        approved specifier resolves to the exact file its alias names AND a
        generated consumer program importing all three typechecks clean; every
        deep specifier (`/src/...`, an emitted-file path, the pre-S1.2 layout, a
        plausible internal module) does NOT resolve. Proven by mutation —
        re-adding `@oshun/workbench-kit/*` made two of the six resolve straight
        into src. The probe config EXTENDS tsconfig.base.json so the alias table
        under test is the repository's, not a copy. -->
  - [x] S1.3.b Add package scope, type, domain, and layer tags consistent with
        the repository taxonomy. Evidence: project metadata review.
        <!-- scope:oshun / layer:workbench-kit / type:lib / domain:neutral. The
        domain tag is the one worth arguing: the kit is domain-NEUTRAL, and
        saying so as a tag is what lets the claim be checked rather than
        believed. The gate publishes a census over all 3502 tracked
        project.json files: layer:contracts 19, layer:domain 1790, layer:app 11,
        layer:shared-logic 1, layer:workbench-kit 1, layer:bff 0. A layer with
        no carriers is reported `enforceable: false` — the failure mode this
        cell exists to surface. What is NOT derivable is where the other 40-odd
        legacy layer tags (layer:service 229, layer:infra 124, layer:studio…)
        belong in the six-layer model; that is an architecture decision, not a
        lookup, and the summary says so. -->
  - [x] S1.3.c Define allowed edges from hosts, BFFs, domain packages,
        studio-authoring, contracts, shared kit, and adapters. Evidence:
        dependency constraint table.
        <!-- All 15 S0.6 allowed edges replayed against the depConstraints
        eslint.config.js actually exports — IMPORTED, not regexed, because a flat
        config builds its rules in JavaScript and a regex would agree with a
        comment while disagreeing with the code. THIS FOUND A REAL BLOCKER:
        `layer:domain -> layer:workbench-kit` and `-> layer:shared-logic` were
        both REFUSED, so a domain package could not import the kit at all — the
        layering was decided and unusable, which is precisely what S1.3.f means
        by resolving violations in proposed consumers. Both are now in the
        layer:domain allow-list and enforced. -->
  - [x] S1.3.d Forbid app-internal imports, domain-to-host reverse edges,
        browser-to-server imports, server-to-React imports, and private deep
        imports. Evidence: boundary rules.
        <!-- All 21 S0.6 forbidden edges replayed the same way. The kit's own
        rules are a PAIR — allow-list (contracts, shared-logic) plus deny-list
        (domain, bff, app) — because a single allow-list is defeated by the one
        edit anybody would make. Neither names its own layer, so the same-layer
        edge S0.6 also forbids is refused: two peer kits importing each other
        are one kit with a seam nobody can cut. Browser-to-server and
        server-to-React are NOT expressible as Nx tags (tags are per PROJECT and
        these are per MODULE within one package) — they are the S1.2.c runtime
        lattice, enforced over the module graph, and this cell does not
        double-count them. Private deep imports were already refused by Nx's
        relative-import rule, which the fixtures confirm rather than claim.
        THREE PRE-EXISTING DIVERGENCES are reported, never folded into
        "enforced": layer:app/contracts/domain each permit their own layer,
        which S0.6 forbids. Closing that is a 1820-project migration, not a cell
        in S1.3, so the gate names and counts them on every run and holds only
        the layers Phase S introduced to the full bar. -->
  - [x] S1.3.e Add deliberate allowed and forbidden consumer fixtures for each
        dependency class. Evidence: rule tests reject only forbidden edges.
        <!-- tools/boundary-fixtures.mjs writes five real TypeScript files into
        the package, runs the repository's real ESLint over them, and deletes
        them in a `finally` with a preflight sweep and a .gitignore entry for a
        killed run. BOTH halves are asserted: 2 permitted edges must be ACCEPTED
        and 3 forbidden ones REJECTED, because a rule that also refuses the
        permitted edges is a rule nobody can adopt. Proven by mutation: removing
        the new constraints made two of the three forbidden edges pass.
        THE CHECK CAUGHT ITS OWN VACUITY. Excluding the fixture directory from
        tsconfig.lib.json looked tidier and silently defeated everything:
        typescript-eslint's project service could not find the files, every rule
        was replaced by a parsing error, and the boundary rule never ran — all
        three forbidden fixtures started "passing". The tool now fails on a
        parsing error naming that exact cause, and the tsconfig comment records
        why the directory stays included. -->
  - [x] S1.3.f Resolve all violations in proposed consumers without disabling
        the constraint. Evidence: clean targeted boundary check.
        <!-- The two real violations were `layer:domain -> layer:workbench-kit`
        and `-> layer:shared-logic` (S1.3.c). Resolved by adding the two layers
        to the layer:domain allow-list — the edge S0.6 ratified — rather than by
        disabling the rule, adding an eslint-disable, or allow-listing the
        package name. `nx lint` is clean on @oshun/workbench-kit,
        @oshun/studio-authoring and @oshun/domain-tara (the retagged and
        newly-permitted projects), and the gate reports 0 contradictions with 22
        edges enforced. -->
      <!-- Still open by design: 11 of the 36 S0.6 edges are
      `unenforceable-no-carriers` because layer:bff has no tagged project. That
      is a fact about the repository, not a gap in this cell, and it is
      published on every run rather than rounded up. -->
- [x] S1.4 Define `WorkbenchId`, `TenantId`, `ActorRef`, `ArtifactRef`,
      `RevisionRef`, and `StableAnchor` branded types. Evidence: invalid
      cross-assignment fails type tests.
      <!-- libs/oshun/workbench-kit/src/identity.ts + identity.spec.ts (82
      tests), published as the `./identity` entry point (S1.4 is what authorizes
      it in the S1.2 derived export map) and re-exported from the root.
      THREE DECISIONS shape the module and each is stated in source: parsing is
      STRICT and normalising is EXPLICIT; there is NO unsafe cast; parsers return
      RESULTS rather than throwing (unlike ./invariants, because a malformed id
      at a boundary is expected input while an unauthorised mutation is a bug).
      FOUND AND FIXED A REAL DEFECT ON THE WAY IN: the `typecheck` target ran
      `tsc --noEmit` against a solution-style tsconfig with empty files/include,
      so it compiled ZERO files and was green over a deliberate `const x: number
      = 'nope'`. S1.1.b/g recorded it as PASS; it passed because it did nothing.
      Now `tsc --noEmit -p tsconfig.lib.json && -p tsconfig.spec.json`, proven
      to fail on an injected error in src AND in a spec. S1.4.f depends on that
      being true. -->
  - [x] S1.4.a Define canonical string format, namespace, parser, serializer,
        equality, and validation for `WorkbenchId` and `TenantId`. Evidence:
        valid/invalid/round-trip fixtures.
        <!-- `<namespace>:<segment>`, segment 1-63 lowercase alphanumerics with
        internal hyphens, over seven namespaces (tenant/workbench/actor/artifact/
        entity/revision/block). LOWERCASE-ONLY IS THE LOAD-BEARING CHOICE: it is
        what lets equality be byte equality instead of a case-folding function
        whose locale behaviour seven codebases would each get subtly wrong. So
        `parseTenantId('tenant:ACME')` FAILS with its own code
        (`not-canonical-case`) rather than folding — a parser that quietly
        lowercased would mean two strings both "are" the tenant and neither is
        the one in storage. `normalizeIdCandidate` does the folding, reports
        every change it made, and is opted into at an input boundary; an HTTP
        form may use it, a stored id must not, and the report is what lets the
        two callers differ. `formatId` deliberately returns `string` (widening),
        because a value going into a URL or a column typed as branded invites
        skipping the parse on the way back. 10 rejection codes, each tested. -->
  - [x] S1.4.b Define `ActorRef` for human, delegated, service, system, and
        redacted identities without using display data as identity. Evidence:
        actor variant tests.
        <!-- Five variants, closed-shape parser. "Without using display data as
        identity" is enforced TWO ways rather than asserted: structurally, since
        no email-shaped string can satisfy `actor:<segment>`, so the prohibition
        needs no denylist of address formats; and by the closed shape, which
        refuses unknown keys and gives `name`/`email`/`avatarUrl`/... their own
        code (`display-data-in-identity`) with the reason — a name changes, an
        email is reassigned, and either used as identity turns an audit trail
        into a guess. `delegated` carries BOTH actorId and onBehalfOf because an
        audit row with one cannot answer either "who did this" or "whose
        authority was used"; `authorityOf` returns the PRINCIPAL for it, which is
        the question every authorization check is really asking. A delegation to
        oneself is refused: it records an authority chain of length zero while
        claiming one exists. `redacted` exists so an erasure leaves the audit row
        intact — a nullable actorId cannot distinguish "erased" from "never
        recorded" — and `authorityOf` returns null for it, not the surrogate,
        because the surrogate identifies the ROW. -->
  - [x] S1.4.c Define `ArtifactRef` with artifact identity, immutable byte
        version/hash, media/profile, and resolution semantics. Evidence:
        artifact reference tests.
        <!-- Pinned and floating are SEPARATE TYPES, not one type with a nullable
        digest, so "did we pin this?" is a question the compiler answers. That is
        the resolution semantics: `resolutionContract` reports
        deterministic/cacheable/signable, and a floating ref is none of the three
        — signing one commits to nothing and caching one caches a lie. Media is
        REQUIRED on a pinned ref (the bytes are fixed, so what they are is
        knowable) and nullable on a floating one. The digest carries its
        algorithm rather than assuming sha256, so adding a second is a widening
        instead of a silent reinterpretation of old rows. Uppercase hex is
        rejected for the same reason uppercase ids are. -->
  - [x] S1.4.d Define `RevisionRef` with entity/workbench scope, revision ID,
        schema version, and optional parent/base. Evidence: revision reference
        tests.
        <!-- tenant + workbench + entityId + revisionId + schemaVersion, and
        `parent`/`base` as two fields because they answer different questions and
        diverge exactly when it matters: after a rebase, parent is where the
        content came from and base is what the change was expressed against. A
        three-way merge needs both; a model with one silently picks a side. Both
        must be a revision id or explicitly NULL — undefined is refused, because
        it makes "first revision" and "we did not record it" the same value. A
        self-PARENT is refused (a lineage cycle) while a self-BASE is ALLOWED (an
        empty diff, a real thing to express); the asymmetry is deliberate and
        tested. Wrong-kind ids are caught in all four slots. -->
  - [x] S1.4.e Define `StableAnchor` variants for entity, field, block, range,
        temporal, graph, canvas, and hierarchy targets with revision semantics.
        Evidence: anchor variant tests.
        <!-- All eight, each carrying the `RevisionRef` it was TAKEN AT — that is
        the revision semantics, and without it an offset into a document nobody
        can date is a number, not an anchor. It is what makes `AnchorResolution`
        expressible: resolved / rebased-with-confidence / orphaned with CAP.11.e's
        five causes kept distinct, because the product answers differ (deleted =
        the comment is history; permission-hidden = this reader may not know it
        exists; ambiguous = a human could rescue it but nothing may move it
        automatically). Real domain content rather than a shape: FRAME RATES ARE
        RATIONALS, since 29.97 is 30000/1001 and comparing floats is how a note
        lands one frame off after a JSON round trip — and unreduced rates are
        refused so equality is field equality. Ranges are half-open over CODE
        POINTS, not UTF-16 code units, which can split a surrogate pair into a
        range that cannot render; `start === end` is a caret, not an error. A
        fractional frame or sample position is refused (rounding it silently
        moves the anchor) while a fractional seconds position is fine. A port
        anchor MUST name its owning node, because port ids are only unique within
        a node; a node anchor must NOT carry ownerId, or somebody will read it as
        meaningful. Paths are ARRAYS so a segment containing a dot cannot split
        the path. -->
  - [x] S1.4.f Prohibit unchecked string casts and cross-kind ID assignment at
        compile time and parsing boundaries. Evidence: type-negative and runtime
        tests.
        <!-- COMPILE TIME: a `declare const ... : unique symbol` that is never
        defined and never exported, so a consumer cannot name it and therefore
        cannot construct a branded value. Seven `@ts-expect-error` blocks are the
        assertions — an UNUSED `@ts-expect-error` is itself a TypeScript error,
        so a brand that stops working fails `nx typecheck` on the line that
        expected it to work. PROVEN BY MUTATION: making the brand optional broke
        one directive, removing it entirely broke three. `idEquals` uses
        `NoInfer` on its second parameter so comparing a WorkbenchId with a
        TenantId is an error rather than inferring the union and comparing
        anyway. PARSING BOUNDARY: `wrong-namespace` is its own code — a tenant id
        used as a workbench id would address a different object with the same
        segment — and there is no exported unsafe cast at all, because an escape
        hatch is used exactly once and then everywhere.
        S1.2.f ALSO CHANGED THE CODE rather than being worked around: the strict
        import-time-work scan flagged three module-level `new Set([...])`
        constructions, so they are frozen arrays and the module is genuinely
        inert at import — which is what `sideEffects: false` already promised. -->
- [x] S1.5 Define versioned envelopes for commands, queries, events, job
      updates, and capability probes. Evidence: golden wire fixtures.
      <!-- libs/oshun/workbench-kit/src/envelopes.ts, published as `./envelopes`.
      Evidence is evidence/v1-workbenches/workbench-kit-envelope-goldens.json —
      57 cases, 22 that must parse and 35 that must be refused with a NAMED
      CODE — generated by generate-workbench-kit-envelope-goldens.mjs (11
      structural tests) and consumed by envelopes.spec.ts (77 tests) running the
      real parsers. The corpus is committed JSON rather than built by the code
      under test, because a test that constructs an envelope with the same
      helpers the parser uses proves the two agree with EACH OTHER and says
      nothing about the bytes another producer sent.
      THE CORPUS EARNED ITS KEEP ON THE FIRST RUN, finding three real defects:
      the extension-key pattern rejected `isis.retryHint` (it forced kebab-case
      on the name segment, so every realistic extension would have been
      refused); a succeeded outcome with no output field at all reported
      "ArtifactRef must be an object", sending the reader to the wrong field; and
      `older-compatible` had no coverage at all. MUTATION-TESTED three ways —
      dropping the deadline invariant, the namespace check, and the
      terminal-checkpoint rule each fails exactly the one case that claims to
      cover it. -->
  - [x] S1.5.a Define common envelope ID, kind, schema version, timestamp,
        actor/tenant/workbench scope, correlation, trace, and extension fields.
        Evidence: common wire schema.
        <!-- Ten common fields, closed shape, ONE open field. `extensions` is the
        whole compatibility story: every other field is refused if undeclared
        (the same no-escape-hatch rule ./invariants.ts enforces on mutations),
        which would normally make forward compatibility impossible — a producer
        on a newer schema adds a field, an older reader rejects it, and the
        rollout is a flag day. Keys are `<vendor>.<name>`, the kit never reads
        them, and a key that SHADOWS a declared field is refused because
        `x.actor` reads as authoritative in a log and is validated by nobody.
        That combination is what lets versionCompatibility() tolerate a NEWER
        envelope: if it parses under the current shape the producer only added
        extensions, so nothing this reader needed was dropped; if it does not,
        the addition was a real field, and honouring a field you do not
        implement is worse than refusing the message. No hand-maintained version
        ceiling to bump per producer release.
        correlationId AND traceId are both present and are not the same thing: a
        correlation id is a BUSINESS transaction that survives retries and
        appears in the audit record; a trace id is an OBSERVABILITY span tree
        that is sampled and may be absent. Using the trace for correlation means
        "show me everything in this publish" silently returns a subset whenever
        tracing is off, so traceId is nullable and correlationId is not. -->
  - [x] S1.5.b Define command envelope for idempotency, expected revision,
        deadline, authorization intent, and typed payload. Evidence: command
        golden fixtures.
        <!-- 17 golden command cases. `authorizationIntent` is the grant the
        caller CLAIMS to need — an intent, not a verdict; the verdict is
        ./invariants.ts's ActorDecision. Keeping them apart is what makes
        commandAuthorizationMismatch() possible: a command asking for
        workbench:read that was authorised under workbench:publish is privilege
        confusion, and one combined field cannot notice it. A deadline at or
        before occurredAt is refused as a CLOCK FAULT rather than accepted as an
        expired command, because treating it as a timeout hides the fault.
        expectedRevision must be a revision or explicitly null — undefined is
        the ambiguity that makes a lost update silent. -->
  - [x] S1.5.c Define query envelope for typed filters, pagination, consistency,
        locale, field projection, and payload. Evidence: query golden fixtures.
        <!-- Cursor and offset pagination are separate SHAPES, so a query
        carrying both is structurally impossible rather than caught by a check —
        a shape with both has no defined meaning and whichever the server reads
        first becomes the answer. `bounded-staleness` REQUIRES its
        maxStalenessSeconds, because without the number it is "eventual" with a
        name that sounds like a guarantee. pageSize is bounded at 200, since an
        unbounded page size is how one read becomes a full table scan behind an
        innocent-looking query. locale is REQUIRED and BCP-47: "the server's
        locale" is nobody's locale and produces dates no reader asked for.
        projection is [] for the default rather than omitted, so there is no
        third state. -->
  - [x] S1.5.d Define event envelope for producer, subject, sequence, causation,
        correlation, delivery compatibility, and typed payload. Evidence: event
        golden fixtures.
        <!-- `producer` is separate from `actor` because a retry by a different
        component has the same actor. `delivery` is carried rather than assumed
        so a consumer knows whether it must deduplicate instead of guessing and
        being wrong once. dedupeKey() is (subject, sequence), NOT envelopeId: a
        redelivery may legitimately carry a fresh envelope id while a genuine
        second event never reuses a sequence for one subject — tested with a
        redelivery that must produce the same key. Self-causation is refused
        because a causation walk over it would not terminate; causationId must be
        an id or explicitly null, since undefined cannot distinguish
        "externally triggered" from "we lost the chain". -->
  - [x] S1.5.e Define job update envelope for job/attempt/stage identity,
        progress, checkpoint, usage/cost, output/error, and timestamp. Evidence:
        job-update golden fixtures.
        <!-- 12 golden cases. COST IS INTEGER MINOR UNITS with an explicit
        ISO-4217 currency, because a float cost accumulates error over a million
        updates and then disagrees with the invoice. progress is 0..1 or
        explicitly null (indeterminate) — a percentage and a fraction in one
        field is how a bar shows 4500%. A TERMINAL outcome may not carry a
        checkpoint: a resumable-looking terminal update is how a finished job
        gets run twice. A `succeeded` outcome must name its output (a success
        with nothing to show cannot be told apart from a job that silently
        produced nothing) and may not report progress below 1. A `failed`
        outcome must state `retryable`, because a caller cannot infer that from
        an error code and guessing wrong either loses work or retries forever.
        isProgressRegression() deliberately returns false across ATTEMPTS (a
        retry legitimately starts over) and for a null (a producer saying it no
        longer knows). -->
  - [x] S1.5.f Define capability probe envelope for capability/version,
        dependency status, state, freshness, limitations, and safe remediation.
        Evidence: probe golden fixtures.
        <!-- "SAFE remediation" is made STRUCTURAL rather than asserted: a closed
        seven-action enum plus an https docs URL and a requiresOperator flag,
        with no prose field at all. Free text cannot be made safe — the first
        operator to paste a working curl command puts a bearer token in a probe
        response that gets logged — and the golden corpus includes a docsUrl
        carrying `api_key=sk-live-...` that must be refused. Two consistency
        rules make the answer worth reading: a `configured` capability may not
        offer remediation (if something needs fixing the state is degraded), and
        a non-configured one may not offer `none` ("broken, and there is nothing
        you can do" is almost never true and is never actionable).
        freshnessSeconds 0 means never-cache, which a nullable field could not
        say; isProbeFresh() also refuses a clock that went backwards, because
        that is a fault rather than freshness. CAPABILITY_STATES is ONE constant
        shared with S1.6 so the probe and the state record cannot drift into
        disagreeing about what states exist. -->
  - [x] S1.5.g Test malformed, unknown-kind, older/newer compatible,
        unsupported-version, unauthorized, and oversized envelopes. Evidence:
        negative/compatibility fixtures.
        <!-- All seven classes declared; SIX covered by 35 cases and
        `older-compatible` reported UNREACHABLE with its reason:
        MIN_SUPPORTED_SCHEMA_VERSION === CURRENT_SCHEMA_VERSION, so version 1 has
        no predecessor and a fixture claiming to cover it would have to invent a
        version this build does not support. That exemption EXPIRES on its own —
        the generator stops declaring it unreachable the moment the two constants
        diverge, and the uncovered-class gate then fails. Oversized is checked on
        the TEXT before parsing, because a 4 MB envelope should be refused before
        it becomes 4 MB of objects; the 256 KiB limit is the smallest payload cap
        among the transports these envelopes cross, so fitting here means fitting
        everywhere. The fixture describes the oversized case (fillerBytes) rather
        than committing 256 KiB of JSON, because a fixture nobody can open is a
        fixture nobody reviews.
        THE S1.3 ALIAS CHECK CAUGHT A CONTRACT VIOLATION HERE: the byte count
        used `TextEncoder`, an ambient global, in a module whose declared runtime
        class is `contract` — importable from every runtime, depending on nothing
        that is not. A consumer compiling with `types: []` had no declaration for
        it. Replaced with utf8ByteLength(), eight lines, cross-checked against
        Node's TextEncoder in the spec including a 4-byte emoji that a naive
        charCodeAt walk would count as 6. -->
- [x] S1.6 Define discriminated `configured`, `not_configured`, `degraded`,
      `unavailable`, and `failed` capability states. Evidence: exhaustive switch
      tests.
      <!-- libs/oshun/workbench-kit/src/capability-state.ts + 54 tests,
      published as `./capability-state`. ./invariants.ts gave honest-capability a
      minimal TWO-state shape so the rule could ship with the scaffold; this is
      the full vocabulary, and the reason it needs five is that the three in the
      middle are the ones a caller gets wrong. The state NAMES and the
      Remediation shape are IMPORTED from ./envelopes.js rather than redeclared —
      a transport module owning a domain vocabulary looks backwards and is
      deliberate, because S1.5 shipped the probe envelope first and two lists of
      five strings in one package would eventually disagree about what states
      exist. -->
  - [x] S1.6.a Define common capability identity, checked-at time, freshness,
        dependency, owner, limitation, remediation, and safe-detail fields.
        Evidence: common state schema.
        <!-- Nine common fields on every state. `safeDetail` is not a naming
        convention: parseSafeDetail() runs the same credential scan over the
        detail, every limitation, every dependency detail and the remediation
        URL, because the failure mode is convenience rather than malice —
        somebody pastes the request that reproduced the problem, headers
        included, into a field a dashboard renders and a log aggregator retains.
        `owner.contactPath` must be an https rota or runbook URL and NOT an email
        address, for the same reason S1.4.b keeps display data out of identity:
        an address goes stale and names a person rather than a responsibility.
        One consistency rule is stated once and applies to all five: only
        `configured` may offer remediation `none`. -->
  - [x] S1.6.b Define `configured` only when required configuration,
        dependencies, permissions, and supported versions are verified.
        Evidence: configured-state tests.
        <!-- `configured` carries four VERIFICATION RECORDS, not a boolean, and
        the parser refuses the state unless all four subjects are covered. The
        four fail differently and independently — credentials present but the
        account lacks the scope, every dependency up but the provider is a major
        version ahead, config complete but the key is for the wrong tenant — so a
        single `verified: true` cannot say which one was checked, which means it
        cannot say which one was SKIPPED. Each of the four has its own rejection
        test. A duplicate subject is refused (it hides which check the second
        entry reported), and so is a `configured` state with a dependency that is
        not itself configured: a capability is not more available than the things
        it needs. -->
  - [x] S1.6.c Define `not_configured` with missing configuration category and
        authorized resolution path but no secret values. Evidence:
        not-configured fixtures.
        <!-- MissingConfiguration is `{category, settingName}` and has NOWHERE to
        put a value — not "we redact it", which is the version of that promise
        that does not survive contact with a hurry. The setting NAME is carried
        because `ISIS_PROVIDER_KEY` is exactly what an operator needs to hear.
        `resolutionAuthority` (self-service / tenant-admin / platform-operator)
        makes the answer actionable by the person reading it rather than by
        somebody they have to go and find, and it is cross-checked against
        remediation.requiresOperator: a platform-operator resolution that claims
        no operator is required is telling a self-service user to fix something
        they cannot reach. An empty `missing` list is refused — "not configured,
        and we cannot say what is missing" leaves the reader no move. -->
  - [x] S1.6.d Define `degraded` with affected and retained behavior, cause,
        limitations, recheck/expiry, and recovery. Evidence: degraded fixtures.
        <!-- BOTH halves are required and non-empty, and that is the whole
        distinction between degraded and its neighbours: nothing affected is
        `configured`, nothing RETAINED is `unavailable`. A caller told only what
        broke disables a surface that is still half usable; a caller told only
        that it works hits the part that does not. A behaviour listed in both is
        refused, since a reader of either list alone would get the opposite
        answer. Automatic recovery with no recheckAfterSeconds is refused —
        nothing would ever look again, so the degradation is permanent in
        practice — while MANUAL recovery with no recheck is fine, because a human
        is the trigger. An expiresAt at or before checkedAt is refused: a
        degradation that expired before it was observed cannot be acted on. -->
  - [x] S1.6.e Define `unavailable` with transient/terminal/unknown dependency
        state, retry/circuit metadata, and no fabricated fallback. Evidence:
        unavailable fixtures.
        <!-- `unknown` is a real disposition and not a cop-out: a timeout says
        nothing about whether the far side is restarting or gone, and forcing a
        guess produces retry policy built on invented information. FIVE coherence
        rules, each with its own test: terminal-but-retryable (a client retries a
        permanent failure until its own budget runs out and reports the wrong
        cause), transient-but-not-retryable (work discarded although the state
        says it would have succeeded shortly), retryable-with-no-delay (a client
        with no backoff is the thing keeping the dependency down), its inverse,
        and an open circuit with no cooldown — a busy loop wearing a circuit
        breaker.
        "No fabricated fallback" is enforced by NAMING the ten keys such a value
        would arrive under, checked before the generic unknown-field pass. A
        generic message is the one somebody works around; this refusal says that
        a stale answer presented as a live one IS the fabricated success the
        honest-capability invariant exists to prevent. -->
  - [x] S1.6.f Define `failed` for completed probe/evaluation failure with
        stable error class, correlation, safe detail, and remediation. Evidence:
        failed fixtures.
        <!-- `failed` means an evaluation COMPLETED and returned "this is
        broken"; `unavailable` means no answer was obtained. The two have
        opposite retry semantics, so collapsing them means retrying a permanent
        failure forever or giving up on a blip — and the shapes make the
        confusion impossible: `failed` has no retry metadata and `unavailable` has
        no failureClass, both tested. failureClass is a closed seven-value enum
        because a free-text class changes when somebody improves the wording and
        every dashboard grouped on it silently splits into two series; the
        message lives in safeDetail, where it is allowed to change. correlationId
        is REQUIRED: a completed verdict nobody can find the trace of is
        indistinguishable from a guess. -->
  - [x] S1.6.g Require exhaustive handling and prohibit boolean/truthy collapse
        in typed and UI consumers. Evidence: compile-time negative and consumer
        tests.
        <!-- matchCapabilityState() is TOTAL: the handler record requires all five
        keys, so adding a sixth state breaks every consumer at compile time
        instead of falling into somebody's `default:` and being treated as
        whichever neighbour that branch already handled. Four `@ts-expect-error`
        blocks are the assertions (an unused one is itself an error) — a partial
        handler record, reading a variant field off the union, assigning a state
        NAME where the union belongs, and a handler typed for the wrong variant.
        PROVEN BY MUTATION: making one handler optional broke the directive AND
        the implementation.
        THE BOOLEAN PROHIBITION IS ENFORCED, NOT DOCUMENTED. usability() is
        THREE-valued (full / reduced / none) because a boolean cannot represent
        `degraded` without rounding it to a neighbour — round up and a caller uses
        the broken half, round down and a caller disables a surface that still
        works. And a test checks by REFLECTION that the module exports no
        is/has/can predicate over the union except isStateFresh (which is about
        the answer's AGE, not its meaning). Both mutations caught: collapsing
        usability to two values, and adding an `isUsable` boolean. -->
- [x] S1.7 Define a typed workbench capability registry. Evidence: duplicate
      capability IDs and missing version metadata fail validation.
      <!-- libs/oshun/workbench-kit/src/registry.ts + registry.spec.ts (50 tests),
      published as `./registry`. Evidence is
      evidence/v1-workbenches/workbench-kit-registry-fixtures.json — a realistic
      SIX-capability registry across five domains plus 30 cases (17 descriptor
      rejections, 10 registry-level checks, 3 merge cases), generated by
      generate-workbench-kit-registry-fixtures.mjs (12 structural tests) and run
      through the real parser/validator/merge/projections by the kit spec. The
      two the cell names explicitly both have fixtures: `registry/duplicate-id`
      and `descriptor/missing-version`. MUTATION-TESTED four ways — last-write-
      wins merge, cycles following optional edges, a projection dropping a
      field, and the duplicate-id check removed — each failing exactly the case
      that claims to cover it. -->
  - [x] S1.7.a Define stable capability ID, version, owner, domain, audience,
        host, runtime, lifecycle, and description. Evidence: registry schema.
        <!-- All ten, none optional. The version is a STRICT semver with no build
        metadata and no leading zeroes: semver ignores build metadata in
        precedence, so two entries could differ in a field no comparison can
        see, and `1.02.0`/`1.2.0` would be two spellings of one version.
        compareSemanticVersions implements real precedence including the rule
        people get backwards — a numeric prerelease identifier ranks BELOW an
        alphanumeric one — tested over an eight-version ladder.
        The lifecycle rule is bidirectional: a deprecated or retired capability
        MUST name its successor (a deprecation with no replacement is an
        instruction to stop with no instruction to start, which is why nobody
        migrates), and a live one must NOT (it reads as deprecated to anyone
        scanning the registry). -->
  - [x] S1.7.b Define required/optional dependencies, provided contracts,
        configuration references, provider/resource needs, and health probes.
        Evidence: dependency metadata tests.
        <!-- A dependency listed as both required and optional is refused: a
        caller reading either list alone gets the opposite answer about whether
        it can run without it. Self-dependency is refused (the walk would not
        terminate). Every configuration reference must state `required`, because
        an optional setting treated as required makes a working deployment look
        broken. A probe interval below 5s is refused — a health check costing
        more than the information it produces is how the check becomes the load —
        and a duplicate probe name is refused because a runtime would run it
        twice and report two answers to one question. -->
  - [x] S1.7.c Define supported commands, queries, jobs, routes, blocks, gates,
        import/export, and publisher features by typed references. Evidence:
        capability metadata tests.
        <!-- Eight kinds as `<kind>:<dotted.name>` rather than eight arrays of
        bare strings. The kind travels WITH the name so a consumer holding one
        reference in isolation still knows which of eight registries to look it
        up in; a registry full of `"run.publish"` cannot say. Round-trip tested,
        duplicates refused, and the sample registry exercises at least six of the
        eight kinds so the parser is not proven correct on `command:` alone. -->
  - [x] S1.7.d Define authorization scope, data classification, trust controls,
        observability, SLO, cost, and fixture policy. Evidence: operational
        metadata tests.
        <!-- Four rules that make the operational metadata load-bearing rather
        than decorative. (1) An SLO availability target of exactly 1 is REFUSED:
        a 100% target leaves no error budget, so every incident is a breach and
        the number stops informing any decision. (2) At least one metric is
        required, because an unmeasured capability cannot have its SLO evaluated,
        which makes the SLO decorative. (3) `restricted` data requires
        synthetic-only fixtures and `personal` data forbids a production copy —
        otherwise the capability's own test suite holds the data its
        classification forbids copying, and anonymisation is a claim about
        re-identification risk that nothing in this registry can verify. (4)
        Admin-only scopes with requiresElevation false is refused: one of the two
        is wrong and the permissive reading is the one that ships. Cost is
        integer minor units, since a float estimate accumulates error the moment
        anyone multiplies it by a volume. -->
  - [x] S1.7.e Enforce unique IDs/aliases, semantic versions, known owners,
        known contracts, declared dependencies, and acyclic graph. Evidence:
        registry negative tests.
        <!-- Nine violation kinds, each with a fixture, and `--check` fails if a
        kind's bucket empties. The graph checks run over a real six-node registry
        rather than a two-node toy. THE MOST INSTRUCTIVE CASE IS A NEGATIVE OF A
        NEGATIVE: a cycle through an OPTIONAL dependency must NOT be reported,
        because an absent optional dependency satisfies the loop — without that
        case, a validator that reported every kind on every input would pass all
        nine positive cases. Cycle detection is iterative with an explicit stack
        (a plugin-supplied registry should produce a violation, not a stack
        overflow) and reports one cycle once however many nodes it is entered
        from. `retired-still-required` is called out separately: retiring
        something its dependents still need is the failure retirement exists to
        prevent, and the registry says so before the deploy does. An empty
        knownOwners list SKIPS the check rather than failing everything — an
        empty register means "we do not have that list", not "nothing is
        valid". -->
  - [x] S1.7.f Merge/discover shared and domain plugin entries deterministically
        and reject collisions rather than last-write-wins. Evidence:
        multi-plugin registry tests.
        <!-- A collision is an ERROR naming EVERY source that claimed the id, and
        the failed result carries no `entries` at all — so there is no winner for
        a caller to reach for by accident. Last-write-wins is the obvious
        implementation and it is how a domain plugin silently replaces a shared
        capability, in an order that depends on plugin discovery. THE PAIR THAT
        MATTERS: two sources claiming one id, and ONE source claiming an id
        twice. Both are collisions, because deduplicating the second would be
        last-write-wins wearing a different hat. Determinism is asserted by
        merging the same sources in reverse order and comparing bytes. -->
  - [x] S1.7.g Generate runtime probe and human/API reference projections from
        the same registry. Evidence: source-to-output drift test.
        <!-- Both projections read the same entries. The drift test does NOT
        compare against a committed copy of the output, because a copy is a
        second artifact to maintain and it drifts in the other direction —
        instead every projection row is traced back to the registry field it came
        from, which catches an invented value and a stale one, and is the
        property a copy would only approximate. Byte-stable across calls and
        independent of input order. The two projections deliberately DISAGREE on
        one thing: a RETIRED capability is dropped from the probe list (a runtime
        should not execute it) but kept in the reference (a page that forgets
        retired entries cannot tell anyone what replaced them) — two different
        questions, tested as such. -->
- [x] S1.8 Define a route descriptor with host, audience, scope, navigation,
      deep-link, and feature-flag metadata. Evidence: route integrity tests.
      <!-- libs/oshun/workbench-kit/src/routes.ts + routes.spec.ts (53 tests),
      published as `./routes`. Evidence is
      evidence/v1-workbenches/workbench-kit-route-fixtures.json — a six-route set
      across three hosts plus 37 cases (24 descriptor rejections, 10 set-level
      checks, 3 projection cases), generated by
      generate-workbench-kit-route-fixtures.mjs (13 structural tests).
      A route is the one artifact three audiences read: a router mounts it, a
      menu renders it, and a share link resolves it. Those three disagree
      constantly — a page in the menu the router never mounted, a link that leaks
      a tenant's data, a flag that hides the button but not the URL — so all
      three are DERIVED from one declaration. MUTATION-TESTED four ways: dropping
      the scope cross-check, dropping the method from the effective path,
      dropping a route from navigation without recording it, and dropping the
      share-safety check. -->
  - [x] S1.8.a Define canonical route ID, path/pattern, method or UI kind,
        owning capability, contract version, and lifecycle. Evidence: route
        descriptor schema.
        <!-- "method OR ui kind" is a DISCRIMINATED UNION, so a descriptor
        carrying a GET and a dialog is unrepresentable rather than caught — a
        single optional-both shape leaves every consumer deciding which half to
        believe, and both directions are fixtured. A trailing slash is refused
        because `/runs` and `/runs/` would be two spellings of one route and the
        duplicate-path check could not see the collision. A parameter used twice
        in one path is refused: the two positions can disagree and the router
        keeps one. A deprecated or retired route MUST name a redirect, or every
        link and bookmark to it becomes a 404 with no way to find the
        replacement. -->
  - [x] S1.8.b Define canonical host, allowed host aliases, audience, role,
        tenant/workspace scope, and data classification. Evidence: host/audience
        validation tests.
        <!-- THE LOAD-BEARING RULE OF THE WHOLE CELL: a tenant- or
        workspace-scoped route must say WHERE the scope comes from, and the two
        answers are cross-checked against the path. `path-parameter` requires the
        path to actually contain `:tenantId`; `session` requires it NOT to.
        A route that takes a tenant from both is one where the two can disagree
        and whichever the handler reads first becomes the answer — which is a
        tenant-isolation bug that no amount of handler review finds, because each
        half looks correct alone. All four directions have fixtures (missing
        binding, platform route with a binding, and the two mismatches).
        hostAliases repeating the canonical host is refused: the alias list is
        what a route is ALSO reachable from, and including the canonical one
        makes the duplicate-path check see two. -->
  - [x] S1.8.c Define navigation placement, label/icon keys, order, parent,
        breadcrumb, discoverability, and external/embedded behavior. Evidence:
        navigation metadata tests.
        <!-- labelKey and iconKey are i18n KEYS, validated as dotted keys — a
        literal label in a route descriptor is a string no translator will ever
        see, and the descriptor is exactly where one gets typed because it is the
        one place the words are convenient to write. A `hidden` route declaring a
        menu placement is refused: a menu slot for something the menu must not
        show is a contradiction one of the two consumers resolves wrongly. An API
        route carrying navigation is refused — a menu entry that resolves to a
        JSON endpoint is a link nobody can follow. Self-parenting is refused (the
        breadcrumb walk would not terminate). -->
  - [x] S1.8.d Define deep-link entity, subview, filter, inspector, anchor,
        expiry, and safe-share state. Evidence: deep-link descriptor tests.
        <!-- SHARE SAFETY IS CHECKED AGAINST THE DATA CLASSIFICATION: a publicly
        shareable link into a confidential, restricted or personal route is
        refused. A descriptor is precisely where somebody sets `public` for
        convenience, months after the classification was decided somewhere else,
        and the two would never be read together by a human. The expiry rule runs
        both ways: a `never-share` link may NOT carry one (there is nothing to
        expire, and the number reads as permission to share it for that long) and
        a shareable one MUST (a URL that keeps working after the person it was
        sent to has left). The entity parameter must appear in the path, or a
        link built from it resolves to the route's default entity rather than the
        one that was shared. Anchor kinds reuse the S1.4 vocabulary rather than
        inventing a second one. -->
  - [x] S1.8.e Define feature/config flag identity, default, targeting,
        dependency, expiry, and unavailable behavior. Evidence: flag metadata
        tests.
        <!-- A FLAG IS TEMPORARY UNLESS SOMEBODY ARGUES OTHERWISE: exactly one of
        `expiresAt` and `permanentReason`, never both and never neither. Neither
        is the flag nobody removes because nobody remembers whether it is still
        load-bearing; both is a contradiction. Both failure modes are fixtured,
        and the shipped route set contains one of each so the rule is exercised in
        both directions rather than only refused.
        `fail-open` is REFUSED on a confidential/restricted/personal route: an
        outage in the flag service would grant access the flag exists to
        withhold. Every flag must state its default, because a route whose
        default nobody wrote down behaves differently the first time the flag
        service is unreachable. Self-dependency is refused. -->
  - [x] S1.8.f Validate unique IDs and effective paths, known host/capability,
        authorized audience/scope, navigable parents, and no redirect cycles.
        Evidence: route integrity negative tests.
        <!-- Nine violation kinds, each fixtured, `--check` failing on an empty
        bucket. The effective path is HOST + METHOD + PATH, and both halves of
        that matter: a host alias is enough to collide two routes that look
        distinct in isolation, and the same path with two different methods is
        two handlers rather than a collision. THAT SECOND CASE IS THE
        NEGATIVE-OF-A-NEGATIVE — without it, a check that reported every path
        twice would pass both collision fixtures.
        `unnavigable-parent` is separate from `unknown-parent` because they fail
        differently: a breadcrumb through a route that appears in no menu is a
        link to a page the user cannot get back to. `audience-exceeds-capability`
        catches a route widening the audience of the thing it exposes; the
        capability audiences come from the S1.7 registry rather than being
        inferred, since inferring them would make any route correct by
        construction. An empty capability register SKIPS the check rather than
        failing everything. -->
  - [x] S1.8.g Generate route inventory and host navigation projections from the
        descriptor without silently mounting unauthorized source pages.
        Evidence: generated route snapshot and drift test.
        <!-- The navigation projection RETURNS ITS EXCLUSIONS with a reason each
        (no-navigation-placement / different-host / audience-not-permitted /
        not-discoverable / retired), and the test that matters is
        navigationAccountsForEveryRoute: every route is either in the tree or in
        the exclusion list, never in neither and never in both. A projection that
        quietly returns a shorter list is indistinguishable from one that never
        knew about the missing routes, and "without silently mounting
        unauthorized source pages" needs that honesty in BOTH directions.
        Three projection cases across two audiences and two hosts, so the filter
        is proven to filter rather than to return everything. The inventory and
        the menu deliberately disagree on a search-only route: a menu shows what
        is browsable, an inventory records what is mounted. Both projections are
        byte-stable and independent of input order. -->
      <!-- Note for S1.9+: `hostNavigation` promotes a child whose parent was
      excluded to a root rather than dropping it, so an audience that may see a
      leaf but not its parent still reaches the leaf. That is a deliberate choice
      and the alternative (hiding the subtree) is defensible; it is called out
      here because it is the kind of decision a later cell may need to revisit
      once real menus exist. -->
- [x] S1.9 Define domain plugin interfaces for stage models, gate evaluators,
      structured blocks, previews, inspectors, imports, and publishers.
      Evidence: minimal A and B archetype test plugins compile.
      <!-- libs/oshun/workbench-kit/src/plugins.ts, published as
      `@oshun/workbench-kit/plugins` (S1.2's derived export map authorizes it
      from this cell), plus the two archetypes under src/archetypes/ and
      evidence/v1-workbenches/workbench-kit-plugin-fixtures.json (73 cases, 45
      violation kinds, generator + 12 structural tests wired into
      verify:inventory:v1-workbenches). 491 tests in the kit, all seven Nx
      targets green.
      THE LINE THE WHOLE MODULE IS DRAWN AGAINST: a FORMAT is a spelling rule (a
      stage id is lowercase-kebab so it survives a URL and a JSON key); a
      VOCABULARY is a decision about the domain's model (a stage is called
      `review`). The kit enforces the first and refuses to hold an opinion about
      the second. That is not provable by a sentence, so the evidence is two
      archetypes with DISJOINT stage vocabularies that both validate, and a
      `--check` that fails the moment they share a name.
      Four rules carry the module: a declaration and a contribution must agree
      in BOTH directions; a measurement is not a verdict; nobody verifies their
      own work; absence is a value, never an exception. -->
  - [x] S1.9.a Define plugin identity, supported kit/contract versions,
        capability declarations, dependencies, configuration, and lifecycle.
        Evidence: base plugin contract.
        <!-- `PluginDescriptor` + `parsePluginDescriptor`, 18 committed
        rejections across 8 error codes. The descriptor is DATA and the
        contributions are code, split deliberately: a host decides whether to
        LOAD a plugin before it can run any of it, and a system that must
        execute a plugin to learn whether it is admissible has already given it
        the machine. Kit support is a HALF-OPEN semantic range (>=min, <below) —
        an inclusive maximum of 1.4.0 excludes 1.4.1, so every honestly pinned
        plugin breaks on a patch release and the pressure becomes to stop
        pinning. The wire schema range is a separate INTEGER type, because
        modelling it as semver would let a plugin declare `^1.2` support for a
        thing that has no minor version. `declares` is a discriminated
        implemented/degraded/unsupported and is closed PER VARIANT: `retains` on
        an unsupported row would describe behaviour the row just denied, and a
        reader believes the more generous half. -->
  - [x] S1.9.b Define typed stage model and transition-guard registration with
        no shared assumption about domain stage names. Evidence: stage plugin
        fixtures.
        <!-- `StageModel<TStage extends string>` / `TransitionGuard` /
        `validateStageModel`, 10 fixtures. The validator never compares a stage
        against a vocabulary — only against the model's own stages — and the
        proof is `clean/two-plugins-with-disjoint-stage-vocabularies` plus the
        archetypes. `GuardOutcome` has THREE members: an `indeterminate` guard
        whose dependency is unreachable must not answer `allow`, and
        `transitionDecision` reports the first block AND the first
        indeterminate separately, because a caller that learns only "blocked"
        cannot tell whether to show a remediation or page whoever owns the
        unreachable guard. Reachability is computed rather than asserted: an
        unreachable stage is either dead or the transition that reaches it was
        never written, and both are worth saying out loud. -->
  - [x] S1.9.c Define gate/evaluator input, raw result, policy verdict,
        provenance, staleness, remediation, and failure interfaces. Evidence:
        gate plugin fixtures.
        <!-- `GateInput` / `GateMeasurement` / `GateProvenance` / `GateFailure` /
        `PolicyDecision` / `GateVerdict` + `gateVerdict`, 7 verdict fixtures and
        2 registration fixtures. THE SEPARATION IS THE CELL: the evaluator
        MEASURES and the policy DECIDES, so one measurement can be read against
        two tenants' thresholds — and staleness becomes expressible at all,
        because a stale measurement is a real number that must not become a
        pass. The kit resolves absence, failure and staleness BEFORE the policy
        is consulted, and `NonPassingVerdict` excludes `pass` and `warn` by
        type, so a domain cannot write a policy mapping "the gate did not run"
        to "the gate passed". `decidedBy` names which of the two decided, so a
        green gate is always traceable to the thing that greened it — and the
        assertion that matters in the spec is not the verdict but that the
        policy's `decide` was NOT CALLED on the three kit-decided paths. The
        freshness window is half-open and a future timestamp is not fresh: a
        clock skew read as extra validity extends every cached pass by the
        skew. -->
  - [x] S1.9.d Define block schema, validator, editor, semantic renderer,
        migration, diff, anchor, import/export, and accessibility interfaces.
        Evidence: block plugin fixtures.
        <!-- `BlockContribution` (validate / editor / toSemanticNodes /
        migrationSteps / diff / anchors / interchange / accessibility) +
        `validateBlockContribution`, 9 fixtures. The renderer returns SEMANTICS
        rather than markup — a contract-class module cannot bind a view library,
        and a block returning markup makes every non-visual consumer (screen
        reader, export, plain-text digest, diff) parse HTML to recover meaning
        the block already had. The editor is a real contract minus the
        component: surface, typed commands with per-command scope and a
        `reversible` flag, and a REQUIRED read-only fallback, because the
        alternative a missing one invites is showing the editor and refusing on
        save, which teaches people their work can vanish. `applyEdit` is pure
        and re-validates, since an editor is the one place a block is built from
        untyped input. The migration chain must be CONTIGUOUS from its lowest
        version to the current one: a gap means every document at that version
        is unreadable and nothing reports it until one shows up. `diff` and
        `anchors` take the revision they are taken AT rather than manufacturing
        one — an anchor without it is an offset into a document nobody can
        date. -->
  - [x] S1.9.e Define preview and inspector contributions with typed selection,
        authorization, loading/state, nonvisual alternative, and host isolation.
        Evidence: projection plugin fixtures.
        <!-- `ProjectionContribution` + `SelectionContract` +
        `ContributionAuthorization` + `ProjectionState` +
        `NonvisualAlternative` + `HostIsolation` +
        `validateProjectionContribution`, 6 fixtures. `ready` is the ONLY state
        carrying a model, and `empty` and `unauthorized` are deliberately
        different members: collapsing them is how a permission defect renders as
        an empty state for months, because the panel looks the same and nobody
        files anything. The non-visual alternative is REQUIRED rather than a
        courtesy — a projection is often the only place a piece of data is shown
        at all, and if its only rendering is visual the data has no API, no
        export and no screen-reader path. Isolation rules with teeth: an iframe
        with no permitted origin can be framed by anything that can then post to
        it, `*` is the absence of the check written as a value, and an
        in-process contribution over confidential/restricted/personal data may
        not reach the network, because in-process means it carries the host's
        credentials off-origin. -->
  - [x] S1.9.f Define importer plan/validate/execute/reconcile and publisher
        plan/execute/verify/compensate interfaces. Evidence: interchange plugin
        fixtures.
        <!-- `Importer` / `PublisherRuntime` / `PublisherMetadata` +
        `executionMayProceed` + `verificationIsIndependent`, 4 fixtures and the
        archetype behaviour tests. Approval is bound to a PLAN DIGEST, so the
        bug this exists for cannot happen: validate approves plan A, the source
        changes, execute runs plan B under A's approval, and both halves look
        correct in review because neither can see the other's input. The
        archetype test runs exactly that and asserts 0 applied.
        `VerificationSource` has three members and the executor's own report is
        not one of them — "the thing that did the work says it worked" is the
        most common shape of a fabricated success in a publishing pipeline, and
        it is the one that survives review because the code reporting it is the
        code that would have failed; a matching verification with no evidence is
        refused for the same reason. Reversibility is a discriminated PAIR:
        reversible implies a compensation, irreversible implies a stated reason,
        and neither can be omitted quietly. -->
  - [x] S1.9.g Compile and runtime-validate one content-authoring and one
        operations-console plugin with different types and unsupported
        capabilities. Evidence: A/B archetype tests.
        <!-- src/archetypes/story-desk.archetype.ts (A, content authoring) and
        incident-console.archetype.ts (B, operations). COMPILE is proven by the
        `typecheck` target: the archetypes are excluded from tsconfig.lib.json
        and included in tsconfig.spec.json, so `tsc --noEmit -p
        tsconfig.spec.json` compiles them while nothing reaches dist — an
        archetype in dist is a fixture shipped to every consumer. (They stay
        inside a tsconfig, which is the `src/__boundary-fixtures__/` lesson:
        excluding files from every project takes them out of typescript-eslint's
        project service and replaces every rule with a parse error.)
        RUNTIME-VALIDATE is `validatePlugins([A, B]) === []` plus `admitPlugin`
        returning three DIFFERENT answers — activated, refused (kit version) and
        not-configured (missing setting) — because a host that collapsed the
        first two would show "not configured" for a plugin built against a kit
        that no longer exists.
        DIFFERENT TYPES, made checkable: disjoint stage vocabularies, different
        extension-point SETS (B contributes no block and no inspector), and
        opposite publisher reversibility with two different verification
        sources. UNSUPPORTED CAPABILITIES: A declares image generation
        unsupported (no model access by policy) and the archive degraded (with
        what it retains); B declares structured authoring unsupported, because
        an incident record is a fixed schema written by detection systems and an
        editor there would offer a surface no responder may use mid-incident.
        Both are cross-checked against the fixture's recorded claims, so
        converging the two archetypes fails a test rather than quietly weakening
        the evidence.
        The archetypes are real logic over injected PORTS, not sketches: the
        attribution gate parses the draft and cross-references its footnotes,
        the importer parses markdown, the migrations transform real documents,
        and the publisher verifies by reading the target back through the same
        port it wrote through. A test supplies in-memory ports — that is the
        dependency boundary, and the only place a double belongs. -->
- [x] S1.10 Define stable error codes and safe public error envelopes. Evidence:
      internal causes are logged but secrets and stack traces are absent from
      client payloads.
      <!-- libs/oshun/workbench-kit/src/errors.ts, published as
      `@oshun/workbench-kit/errors`, plus
      evidence/v1-workbenches/workbench-kit-error-fixtures.json (63 cases, 21
      violation kinds, 11 leak classes, the 28-code transport table) and a
      generator + 12 structural tests wired into
      verify:inventory:v1-workbenches. 571 tests in the kit.
      THE HEADLINE EVIDENCE, as one test: a cause holding a stack frame, an
      INSERT statement, a JWT and another tenant's id produces an envelope that
      repeats NONE of them, while an authorized operator gets all of it back
      through the correlation id alone.
      The design decision the whole module rests on: an error crosses the
      boundary TWICE, and the safe crossing is CONSTRUCTED, never derived by
      deleting fields from the rich one. Deletion is a blocklist, and a
      blocklist is wrong the first time somebody adds a field — the leak then
      arrives with a feature that had nothing to do with errors, and nothing
      fails. -->
  - [x] S1.10.a Define namespaces and stable codes for validation,
        authentication, authorization, tenancy, conflict, state, dependency,
        provider, job, policy, quota, and internal errors. Evidence: error code
        registry.
        <!-- `ERROR_REGISTRY`: 28 codes across all twelve namespaces, SHIPPED by
        the kit rather than left to each host — the whole value of a stable code
        is that a client written against one workbench handles the same failure
        the same way in another, and a per-domain registry is a per-domain
        vocabulary with extra ceremony. The namespace set is CLOSED because a
        namespace is what an old client falls back to when it meets a code it
        has never seen; if the set can grow, the fallback has nothing to fall
        back to. `validateErrorRegistry` reports 21 kinds and the shipped
        registry is one of the corpus's clean cases, so a rule that became too
        strict fails here rather than being discovered by a domain. -->
  - [x] S1.10.b Define envelope fields for code, safe message key/parameters,
        affected field/reference, retryability, remediation, correlation, and
        optional details. Evidence: public error schema.
        <!-- `PublicErrorEnvelope` + `publicError`. Parameters are SCALARS only
        (an object carries whatever its author put on it, and a scanner can read
        a string but cannot know what a nested field MEANS) and only under keys
        the CODE declared: an open parameter bag is the leak that arrives
        without a code change, when a handler passes the object it already had
        and a field added to that object six months later goes out with it. The
        message is an i18n KEY, never text — a literal is both untranslatable
        and a string a client will end up matching on. Two retry rules run in
        opposite directions: a non-retryable code may not carry a retry-after,
        and a code that says "retry after backoff" MUST, because otherwise every
        client picks its own delay and the ones that pick badly are
        indistinguishable from an attack. -->
  - [x] S1.10.c Map domain/internal/provider/database/queue/bridge exceptions to
        public codes without string matching fragile messages. Evidence: mapper
        unit tests.
        <!-- `mapCause` + `DEFAULT_CAUSE_MAPPINGS` (SQLSTATEs, provider error
        codes, domain error names) + `validateMappings`. The guarantee is
        structural: `mapCause` has NO message parameter, so there is nothing to
        match on. `validateMappings` then refuses a classifier that reads as
        prose — a space, a trailing full stop, over 64 characters, a capitalised
        sentence — because message-matching does not arrive as a decision, it
        arrives as one mapping somebody added under pressure with the provider's
        sentence pasted in. An unmapped cause falls back to `internal.unexpected`
        rather than improvising a code: an unmapped provider failure that
        inherited the provider's own retry semantics is a retry storm nobody
        chose. Five cause cases including two that must NOT match — a classifier
        colliding across kinds, and a source-agnostic rule that must still
        match a source it never named. -->
  - [x] S1.10.d Define HTTP/RPC/job/event status and retry semantics per code.
        Evidence: transport mapping table.
        <!-- `transportTable()` over the registry, and the committed table in
        the fixture is a hand-MIRRORED claim the spec asserts equality against.
        Deriving it would make it agree by construction and prove nothing;
        mirroring means a change to any code's status or retry semantics has to
        be made twice, in two files, by somebody who noticed. Four cross-checks
        run inside the registry validator, each catching a pair that reads as
        reasonable in isolation: backoff advice on a status that will never
        succeed, "change your request" on a server fault, a worker retrying what
        the code says cannot be retried, and a job parked for an operator
        without saying an operator has to act. The rpc column is checked against
        the http column too, so the same failure cannot mean two things
        depending on which transport a client used. -->
  - [x] S1.10.e Redact secrets, tokens, signed URLs, stack traces, SQL,
        protected identities, content, prompts, and cross-tenant object detail.
        Evidence: canary-data negative tests.
        <!-- `scanEnvelope` over 11 leak classes, with one canary each, and
        `publicError` REFUSES rather than strips: a caller whose parameter
        silently disappeared ships a message with a hole in it and learns
        nothing. The corpus's sharpest half is the five LOOKALIKES — a field
        path containing "select", a short `sk-2026`, prose containing the word
        "at", a plain docs URL, and the addressed tenant's OWN id — all of which
        must pass, or the scanner is not detecting secrets, it is refusing to
        emit anything.
        DOCUMENTED APPROXIMATION, stated rather than hidden: a prompt is prose,
        and so is a document and a comment, so no pattern distinguishes one from
        an error message somebody wrote. Prompts (and SQL, stacks, tokens) are
        therefore ALSO refused by the NAME of the field carrying them, and the
        limit is that a prompt under an undeclared key would not be caught by
        shape. What catches that case is the S1.10.b rule that a parameter key
        must be declared by the code — the two checks cover each other and
        neither is asked to do the other's job.
        Cross-tenant detection fails CLOSED: with no addressed tenant given,
        every tenant id in the payload is foreign, because "we do not know who
        this is for" is not a reason to send them somebody else's id. -->
  - [x] S1.10.f Correlate the safe envelope to access-controlled internal cause,
        trace, log, and audit context. Evidence: support lookup integration
        test.
        <!-- `lookupCause` + `envelopeMatchesCause`, and the integration test is
        the cell's headline: build an envelope from a cause carrying a stack
        frame, an INSERT statement, a JWT and `tenant:globex`, assert the
        serialized envelope contains none of them, then resolve the correlation
        id and assert an authorized operator gets every one back.
        Four outcomes, and two of the distinctions are the point. `expired` is
        separate from `not-found` — "we deleted it on this date, because that is
        the retention we committed to" and "we have no record of this" are
        different answers, and collapsing them turns every retention deletion
        into a suspected data-loss incident. And an unauthorized caller is TOLD
        SO rather than told not-found: the usual advice is the opposite, and the
        argument for this direction is recorded in notDerivableFromSource — the
        correlation id came from the customer, who already knows the error
        happened, so hiding existence protects nothing and costs an hour.
        The lookup RETURNS the audit row rather than writing it: a function that
        both reads and audits in one place is one where the audit can be skipped
        by calling the read directly. `envelopeMatchesCause` checks quoting with
        a sliding 24-character window rather than whole-string equality, because
        the interesting failure is an envelope that quotes PART of the cause — a
        clause pasted into a parameter because it read well — and a whole-string
        check never sees it. -->
  - [x] S1.10.g Version and deprecate codes without reusing semantics. Evidence:
        compatibility and unknown-code consumer tests.
        <!-- `resolveCode` returns five statuses and every one has a case.
        `retired` is deliberately distinct from `unknown`: "this used to mean
        something else" and "I have never heard of this" are different answers
        and only the first tells a maintainer what happened. A retired code may
        never be re-registered (`retired-code-reregistered`), because an old
        client still has the old meaning compiled in and would handle the new
        failure as the old one, silently and correctly-looking.
        The unknown-code path is the compatibility claim: a code from a NEWER
        registry still parses into a namespace, so an old client degrades to
        that namespace's retry semantics rather than crashing or — worse —
        treating an unrecognised error as a success. The per-namespace fallbacks
        are deliberately the cautious answer in each case and are recorded as a
        judgement rather than a derivation. A deprecation must name a live
        replacement with the SAME retryability, since a consumer that migrates
        on the strength of the pointer would otherwise silently change its retry
        behaviour, and a deprecated code stays emittable so the consumers the
        deprecation exists to give time to keep working. -->
- [x] S1.11 Define schema-version negotiation and supported-version discovery.
      Evidence: backward-compatible and unsupported-version contract tests.
      <!-- libs/oshun/workbench-kit/src/negotiation.ts, published as
      `@oshun/workbench-kit/negotiation`, plus
      evidence/v1-workbenches/workbench-kit-negotiation-fixtures.json (34 cases,
      19 violation kinds, the 6-combination skew matrix) and a generator + 10
      structural tests wired into verify:inventory:v1-workbenches. 615 tests in
      the kit.
      THE ASYMMETRY THE MODULE IS BUILT AROUND: negotiation needs a live
      counterparty. HTTP and the desktop bridge have one; an event, a job and a
      bundle do not — the reader may not exist yet and may not exist for months.
      So only two of the six transports may declare themselves negotiable and
      the other four have to be conservative instead. This is the thing a single
      "we negotiate versions" story gets wrong: it is written for
      request/response and then applied to a queue, where there is nobody to
      negotiate with. -->
  - [x] S1.11.a Define how clients request or declare contract versions for
        HTTP, events, jobs, bundles, desktop/bridge, and generated clients.
        Evidence: per-transport version carrier rules.
        <!-- `VERSION_CARRIERS`: one carrier per transport, each naming the
        mechanism (header / envelope field / manifest field / handshake /
        compiled constant), whether a message with no version is refused,
        whether the receiver may answer with a different one, and an EXAMPLE —
        a carrier rule with no example is one every team implements slightly
        differently, and the differences only show up across a boundary.
        `validateCarriers` refuses a second carrier for one transport (two
        places a version can be written are two places it can disagree, and the
        handler reads whichever it reads first), a transport with none, and an
        asynchronous transport claiming to negotiate. The generated-client
        carrier is required and NOT negotiable on purpose: a generated client
        cannot decide at runtime to speak a version it has no types for. -->
  - [x] S1.11.b Define server default selection, minimum/maximum and exact
        supported versions, compatible ranges, and preference ordering.
        Evidence: negotiation algorithm tests.
        <!-- `CapabilitySupport` + `negotiate` + `validateSupport`. Supported is
        a set of EXACT versions, never a range: a range admits versions nobody
        implemented and the gap only appears when a client asks for one. The
        SERVER's preference wins over the client's, recorded in
        notDerivableFromSource as a decision with a cost — the server knows
        which of its implementations is healthy and which is being drained, and
        letting the client pick keeps the oldest version alive as long as one
        client asks for it, which is the state a sunset date exists to end. Four
        reasons are reported (`exact-single-offer`, `server-preference`,
        `highest-common`, `server-default`) and the spec asserts the REASON as
        well as the version, because a right answer reached the wrong way stops
        covering what it claims the moment the algorithm changes. A default that
        is not in the supported set is refused: every client that declares
        nothing would get a version that does not exist. -->
  - [x] S1.11.c Expose authenticated capability/version discovery with owner,
        lifecycle, deprecation, sunset, and documentation links. Evidence:
        discovery contract tests.
        <!-- `discoveryDocument` + `validateDiscovery`. An unauthenticated
        caller is REFUSED rather than served a redacted document: an inventory
        of every capability and exactly which old contracts are still served is
        a map of what to try, and a redacted one has informative gaps. Each
        entry must name an owner (a row nobody owns is one a caller cannot ask a
        question about) and a docs link (without it the entry says a version
        exists and nothing about how to move off it). Two deprecation checks
        matter: a deprecated version missing from the supported list makes a
        client on it conclude it is already off — the opposite of what a
        deprecation notice is for — and a sunset date that has passed while the
        version is still served teaches clients to ignore the next one. -->
  - [x] S1.11.d Return stable unsupported/too-old/too-new/unknown-version errors
        with supported alternatives and no silent downgrade. Evidence: refusal
        fixtures.
        <!-- Five codes added to the S1.10 registry — `validation.version_missing
        / _too_old / _too_new / _unsupported / _unknown` — so a version refusal
        is a registry code with transport and retry semantics like any other,
        and the spec asserts every code the negotiator can return resolves in
        that registry. The three no-overlap answers are deliberately DISTINCT:
        only "too old" and "too new" tell anybody which side to upgrade, and
        collapsing them is how a version mismatch becomes a support ticket
        instead of a one-line fix. Every refusal carries the supported set, so a
        client is told what it could have asked for. NO SILENT DOWNGRADE is data
        rather than prose: a negotiated result whose version is not the client's
        first choice reports `departedFrom` AND `direction`, and the two
        directions are separate events — an upgrade means the client under-asked
        and should raise its floor, a downgrade is the one with a sunset
        attached. -->
  - [x] S1.11.e Test old client/new server, new client/old server, overlapping
        ranges, no overlap, missing declaration, and disabled capability.
        Evidence: version-skew matrix.
        <!-- All six combinations are a CHECKED list in the fixture and
        `--check` fails on an uncovered one, because a per-case spec assertion
        would be perfectly green with three of the six missing — nothing in a
        per-case assertion knows what the other cases were supposed to be. 11
        skew cases in total, including the two that are easy to get wrong: an
        old client offering only v1 must get v1 even though the server prefers
        v2 (a preference must not override an offer set that does not contain
        it), and a missing declaration on a REQUIRED carrier is refused while on
        an optional one the default applies AND the reason records that it did,
        so a caller can tell a chosen version from an assumed one. A disabled
        capability returns `unavailable`, not a version refusal: it is not a
        version problem, and a version code would send somebody to change their
        client for no reason. -->
  - [x] S1.11.f Bind negotiated version to validation, serialization, cache,
        idempotency, audit, and telemetry. Evidence: end-to-end version
        propagation tests.
        <!-- `bindVersion` derives all six from the negotiated version, and
        `validateBinding` refuses a binding that lost it. Derived rather than
        configured so the version cannot be present in one key and absent from
        another. The two that carry the cell: a cache key without the version
        serves a v1 body to a v2 client, and an idempotency key without it
        dedupes a v2 retry against the v1 original and returns the OLD shape
        with a 200 — the failure a client is least likely to detect. Both look
        exactly like a working system until somebody upgrades. The propagation
        test negotiates, derives, asserts every derived key carries the version,
        and asserts a different negotiated version produces different keys so
        nothing can be shared between the two by accident. -->
- [x] S1.12 Define deprecation metadata and telemetry. Evidence: deprecated
      contract/route use is observable without breaking consumers.
      <!-- libs/oshun/workbench-kit/src/deprecation.ts, published as
      `@oshun/workbench-kit/deprecation`, plus
      evidence/v1-workbenches/workbench-kit-deprecation-fixtures.json (37 cases,
      21 violation kinds, the 9-surface coverage matrix) and a generator + 11
      structural tests wired into verify:inventory:v1-workbenches. 663 tests in
      the kit.
      THE TENSION THE CELL IS ABOUT: the two halves pull against each other,
      because anything loud enough for a consumer to notice is loud enough to
      change what their code does, and a warning that is genuinely safe is one
      nobody reads. The resolution is that the warning is always out-of-band and
      the OBSERVATION is the server's — usage is counted, alerted on and acted
      on here, so a consumer that never reads a header still shows up in the
      telemetry. That is the only way a sunset date is ever decided from
      evidence rather than from optimism. -->
  - [x] S1.12.a Define deprecated-since, replacement, migration guide, warning,
        owner, supported-through, sunset, removal version, and exception fields.
        Evidence: deprecation schema.
        <!-- `DeprecationNotice` + `validateNotice`, 10 fixture cases. Two
        design points beyond the field list. `Replacement` is discriminated so
        `no-replacement` is a REAL answer with a required reason — some surfaces
        are withdrawn because the thing they did was a mistake, and pretending
        there is a migration target sends people looking for one. And the three
        dates are ordered against each other: `supportedThrough` before
        `deprecatedSince` means consumers were never given a window at all, and
        a sunset before the supported-through means the two dates describe
        different promises and a consumer believes the later one. The migration
        guide is required, because a deprecation without one tells somebody to
        stop and not what to do instead. -->
  - [x] S1.12.b Attach deprecation metadata to registry capability, contract,
        field/enum, route, event/job, package export, bundle profile, and plugin
        versions. Evidence: coverage matrix.
        <!-- `SURFACE_ATTACHMENTS` + `validateAttachments`, nine surfaces —
        `event` and `job` split rather than the ledger's "event/job", because
        they deprecate differently: an event has consumers the producer cannot
        enumerate and a job has exactly one worker fleet.
        THE MATRIX IS HONEST RATHER THAN FLATTERING. Five surfaces have a
        descriptor field or a version range the kit already owns and names it
        (CapabilityDescriptor.lifecycle, RouteDescriptor.lifecycle,
        PluginDescriptor.lifecycle, the envelope schemaVersion ranges). The
        other four have neither, so they attach through a NAMED sidecar
        register and the matrix says `sidecar-register` instead of claiming a
        descriptor field that does not exist — which would make the matrix green
        and the deprecation unattachable. Every sidecar row is required to name
        its register, because those are the rows that rot first: nothing about
        the surface itself points at them. -->
  - [x] S1.12.c Emit protocol-appropriate warnings without changing successful
        response meaning or exposing other consumers. Evidence: compatibility
        tests.
        <!-- `warningFor` over five protocols, each with an out-of-band channel:
        HTTP response headers, envelope extensions (never the payload),
        job-update extensions (never the result), a separate bridge frame, and
        stderr (never stdout). The success payload never changes, so a client
        that parses the body sees exactly what it saw before.
        "WITHOUT EXPOSING OTHER CONSUMERS" is made checkable by making the
        warning CALLER-INDEPENDENT: every field is derived from the notice and
        nothing from the request, and `warningsAreCallerIndependent` proves it by
        COMPARING two warnings rather than by reading the implementation — a
        reading stops being true the first time somebody adds a helpful field
        and nothing fails. The spec also proves the check can FAIL, by handing
        it a hand-built warning carrying `alsoStillUsedBy`. A warning that named
        who else was still on the old version would be a migration lever and a
        disclosure at the same time. -->
  - [x] S1.12.d Record bounded usage by deprecated ID/version, consumer class,
        environment, and tenant-safe cohort. Evidence: telemetry schema tests.
        <!-- `UsageRecord` + `validateUsage`. TENANT-SAFE is enforced twice: a
        cohort containing `tenant:` is refused outright, and a cohort with fewer
        than COHORT_ANONYMITY_THRESHOLD (5) distinct tenants must report as the
        `below-threshold` bucket rather than as itself — a cohort that small is
        a tenant id with extra steps, and the record is read by every dashboard
        the surface appears on. The escape is the CORRECT behaviour rather than
        a suppression: the count is still published, under a bucket that names
        nobody.
        BOUNDED means the window: a record covers at most 24 hours, because a
        counter that only grows cannot be alerted on — the number goes up
        forever and never crosses a threshold that means anything. An inverted
        window is refused too, since it still sums into a dashboard. Usage
        against a surface no notice covers is reported, and the surface check is
        SKIPPED when the host supplied no register, for the same reason the
        plugin capability check is: a check that failed every record because the
        host has no list is one somebody disables. -->
  - [x] S1.12.e Alert on usage after migration deadline and block removal while
        supported consumers remain without approved exception. Evidence:
        lifecycle gate tests.
        <!-- `removalDecision` (four outcomes) + `deprecationAlerts` (two
        severities and silence). The alert line falls where it does for a
        reason: past `supportedThrough` the surface still answers and nobody
        will fix it, which is a warning; past `sunsetOn` it was supposed to be
        gone, which is either a migration that did not happen or a removal that
        did not, and both are somebody failing to do a thing they said they
        would. Inside the supported window nothing is said at all, because
        alerting on use of something still supported trains everybody to ignore
        the alert.
        TWO CASES CARRY THE CELL and they look alike. An EXPIRED exception stops
        PERMITTING a removal rather than continuing to block it — an exception
        with no expiry is a decision nobody revisits and the surface outlives
        everybody who agreed to it. And development/staging usage does not block
        a removal, because that traffic is ours and blocking on our own smoke
        tests is how a surface outlives its replacement. The `blocked` outcome
        names WHICH consumer classes remain, since "somebody is using it" is not
        an answer anybody can act on. -->
  - [x] S1.12.f Remove telemetry and compatibility adapters with the deprecated
        surface. Evidence: closure checklist.
        <!-- `closureChecklist` + `validateClosure`, six items, and the two that
        get left are on the list from the start: a telemetry counter for a
        removed thing sits at zero forever and reads as success, and a
        compatibility adapter nobody removed is a second implementation of a
        contract that no longer exists — which the next person to read the code
        will assume is load-bearing. The violation is claiming to be FINISHED
        with items open, not being unfinished: being mid-removal is normal, and
        a checklist that complained about it would be one people stop opening.
        A checklist that never mentions an item is reported separately, because
        an item nobody listed is one nobody forgot. -->
- [x] S1.13 Export test builders separately from production entry points.
      Evidence: production bundle scan contains no fixture factories.
      <!-- libs/oshun/workbench-kit/src/testing/ (six test-only subpaths, 51
      utilities) plus libs/oshun/workbench-kit/src/synthetic.ts published as
      `@oshun/workbench-kit/synthetic`, the evidence artifact
      evidence/v1-workbenches/workbench-kit-test-utility-register.json, two new
      Nx targets (`test-only-imports`, `production-scan`) and a generator + 20
      structural tests wired into verify:inventory:v1-workbenches. 811 tests in
      the kit; all nine Nx targets green.
      THE SHAPE OF THE ANSWER: four mechanisms, because each covers what the
      others structurally cannot — the export map's runtime lattice reads the
      SOURCE graph, the lint ban reads the IMPORT a consumer wrote, the emitted
      scan reads DIST, and the runtime guard is what happens when a fixture
      arrives anyway. Three of the four are static, and the static ones all
      assume the fixture travelled as code. The one that matters in an incident
      is the fourth, because a fixture usually arrives as DATA — a promoted seed,
      a container that resolved a double because it was registered first. -->
  - [x] S1.13.a Inventory builders, factories, fixtures, fake clocks/IDs,
        in-memory stores, mock providers, and assertion helpers. Evidence:
        test-utility register.
        <!-- `TEST_UTILITY_REGISTER` in src/testing/register.ts — 51 rows over
        nine kinds (builder, violation-builder, fixture-corpus, clock, id-source,
        in-memory-store, assertion, builder-contract, constant) — and
        generate-workbench-kit-test-utility-register.mjs, which DERIVES the same
        inventory from the TypeScript AST and fails when the two disagree.
        An inventory is the easiest artifact in this ledger to fake: write a
        list, ship it, and it is wrong by the second commit. So it is checked in
        BOTH directions and by two independent readers. An export nobody
        registered fails; a row naming a symbol nobody exports fails — that is
        the direction that rots quietly, because a renamed builder leaves behind
        a row that reads as coverage. The generator evaluates the register as a
        literal and THROWS on a computed row rather than reading it as undefined,
        which would produce a clean report over an inventory describing nothing.
        The 30 type-only exports are reported separately, because the runtime
        completeness check in register.spec.ts cannot see them and a .d.ts leak
        is still a leak. -->
  - [x] S1.13.b Define explicit test subpaths by runtime and capability without
        reexport from production roots. Evidence: export-map review.
        <!-- Six subpaths: ./testing (aggregate), /assertions, /doubles,
        /envelopes, /identity, /invariants, each added to KIT_OWNED_ENTRY_POINTS,
        package.json exports and the tsconfig alias table, all three derived from
        the same gate. The single `test` runtime class became two: `test-contract`
        (runtime-agnostic, and genuinely constrained — no Node built-in, no DOM
        global, no React) and `test-server` (declared with NO members, so the
        first helper needing a temp directory is placed rather than smuggled into
        the agnostic class). The old `test` class permitted everything, which is
        a lattice entry that checks nothing.
        THE ONE DECISION HERE IS THE OMISSION: ./testing re-exports every pure
        subpath and NOT ./testing/doubles. Everything else is a function that
        returns a value; the doubles hold mutable state that outlives the call,
        and a suite that asked for a builder and received a shared clock with it
        is one stray reference from a test that passes alone and fails in a run —
        which gets labelled flaky and retried rather than fixed. Checked three
        ways: the register spec, the generator over index.ts's export clauses,
        and the packed-consumer smoke test in the resolution mode a domain
        actually uses. -->
  - [x] S1.13.c Ensure builders produce schema-valid defaults while requiring
        explicit values for identity, tenant, rights/consent, or other fields
        whose defaults would hide test intent. Evidence: builder contract tests.
        <!-- `requireExplicit` in src/testing/contract.ts plus
        src/testing/builders.spec.ts: a probe table covering all 28 builders,
        checked against the register for completeness, so "add a builder, forget
        the probe" fails here rather than shipping an unchecked factory. Every
        builder validates its own output through the kit's REAL parser before
        returning it — a factory that can silently emit an invalid value hands
        the suites that expect a rejection a pass for free.
        THE HONEST HALF IS THE OTHER COLUMN. A register where every builder
        requires everything is easy to write and impossible to use, so each
        sensitive field a builder DOES default is listed with the argument for
        it, and the gate requires the argument to be longer than a word. The
        reviewer question becomes "is that reason true?" rather than "did anybody
        think about this?". One field has no defensible default anywhere and the
        gate refuses it outright however good the prose: `tenant`. There is no
        `aTenantId()`, because one shared tenant makes an entire suite of
        isolation tests pass against a query that never filtered by tenant. -->
  - [x] S1.13.d Mark fixture/synthetic values and prevent builders from being
        selected by production dependency injection. Evidence: production guard
        tests.
        <!-- src/synthetic.ts, published as `@oshun/workbench-kit/synthetic` —
        the PRODUCTION half, which therefore cannot live in a test-only module.
        Two marks, because neither survives what the other does: a global-registry
        symbol (enumerable, so a spread keeps it; symbol-keyed, so Object.keys
        and the closed-shape parsers never see it) survives in-process and dies
        in JSON, and a reserved id form survives JSON and a database column and
        says nothing about objects.
        THE RESERVATION IS STRUCTURAL, NOT LEXICAL, AND THE SCAN IS WHY. The
        first version reserved a bare `synthetic-` prefix on any string, and the
        S1.13.f scan immediately reported registry.ts shipping fixture data — it
        was the ratified S1.7 `FixturePolicy` value `'synthetic-only'`. A lexical
        reservation over an open vocabulary collides. So a fixture id now always
        carries a colon (`tenant:synthetic-acme`, `synthetic:idempotency-1`), a
        form an enum member and a stage name never take, and nobody maintains a
        list of words the scan should ignore.
        FOR DI: `selectProductionImplementation` treats "the only registered
        candidate is a double" as an ERROR, never a fallback — the double
        satisfies the interface and the process boots, and that boot is the
        incident. It also refuses an ambiguous choice rather than letting
        registration order decide. The scan reports truncation as a finding, so a
        clean result from a walk that stopped early cannot be read as clean.
        `assertTestRuntime` is the fourth layer and its LIMIT is stated in the
        code and covered by a test: it detects a declared configuration, not a
        deployment. -->
  - [x] S1.13.e Reject production source imports from test-only subpaths through
        boundary lint rules. Evidence: deliberate invalid import test.
        <!-- `@typescript-eslint/no-restricted-imports` in eslint.config.js —
        two blocks, one banning `@oshun/workbench-kit/testing*` repository-wide
        and one banning the relative spelling inside the kit, which the package
        pattern cannot see. Deliberately the typescript-eslint rule and not the
        core one: the core `no-restricted-imports` is already configured
        per-directory (the V1-P2-3500 axe contract, an `off` for the V4/V5
        suites) and flat config resolves by last match, so a repository-wide core
        block would have silently replaced them.
        tools/test-only-import-fixtures.mjs writes eight real TypeScript files,
        runs the repository's real ESLint, and deletes them: five forbidden
        imports must be rejected and three permitted ones accepted, because a
        rule that also refuses the permitted imports is one the first person to
        hit it turns off. It fails on a PARSING error rather than counting it as
        a pass — the sibling boundary-fixtures tool documents that exact
        near-miss. The refusal message is part of the contract and is asserted:
        it must name `@oshun/workbench-kit/synthetic` as the production
        alternative, or the next reader reaches for eslint-disable.
        NOT COVERED, AND SAID RATHER THAN IMPLIED: the root config globally
        ignores libs/**/*.spec.ts, so a spec is never linted and this rule never
        runs on one. That is the intended outcome, but it means the ban rests on
        "every linted file is production source" rather than on a carve-out. -->
  - [x] S1.13.f Scan built exports, bundles, source maps, and transitive modules
        for test utilities and fixture data. Evidence: clean production scan.
        <!-- tools/production-scan.mjs (Nx target `production-scan`, dependsOn
        build). Five scans over dist: reachability over the emitted .js AND .d.ts
        graphs separately (different graphs — a type-only leak still ships the
        names), a token scan, a fixture-identifier scan, a source-map scan, and a
        re-read of the emitted manifest. Result: 12 production entry points, 24
        production files, 24 source maps, 72 forbidden tokens, zero findings.
        Tokens are read from the AST rather than by grep, so a doc comment
        mentioning a builder is not a finding and a renamed identifier still is.
        The token list is DERIVED from the S1.13.a artifact, so a builder added
        tomorrow is scanned for without anybody remembering to add it — and the
        fixture id forms are read from src/synthetic.ts through the same
        artifact, so the guard, the register and the scanner cannot hold three
        opinions about what a fixture id looks like.
        THE NEGATIVE CONTROL IS THE PART THAT MAKES THE RESULT WORTH ANYTHING:
        the same scan runs over the emitted TEST files and must find hits (264
        today). Without it every assertion passes when the scan is looking at
        nothing — a renamed dist directory, an empty token list, an AST walk that
        returns no identifiers. The scan has already earned its place twice: it
        found the `'synthetic-only'` collision that redesigned S1.13.d, and the
        packed-consumer smoke test's flat module resolution, which stopped at the
        new directory boundary and reported clean. -->

  Two further defects this cell surfaced, fixed here rather than filed: the
  export map's module graph read `src/` non-recursively and resolved relative
  specifiers by stripping `./`, so a nested module was invisible and `../x.js`
  matched nothing — every check downstream saw a smaller graph than exists. And
  `assertTestRuntime` read the `process` global directly, which compiles only
  where `@types/node` is installed; the alias-resolution gate caught it by
  compiling a consumer program with `types: []`, which is what a `test-contract`
  subpath claiming to be runtime-agnostic has to survive.
- [x] S1.14 Generate API reference from source contracts. Evidence: generation
      is deterministic and CI detects drift.
      <!-- evidence/v1-workbenches/workbench-kit-api/ — eleven pages, 501
      exported symbols, 749 fields, 45 examples drawn from 181 committed fixture
      cases — plus workbench-kit-api-reference.json (the model + violations),
      the generator, and 29 structural tests wired into
      verify:inventory:v1-workbenches.
      THE REASON IT IS GENERATED AND NOT WRITTEN: a hand-written reference for
      eighteen entry points is wrong within a week and nobody finds out, because
      the only reader who would notice already knew the answer. So every fact on
      every page is read from the TypeScript AST of the module the page owns, the
      export map, or a committed fixture corpus, and the only authored text in
      the whole output is the one-line intro per page, which lives in the
      generator beside the page definition so it changes with it. -->
  - [x] S1.14.a Define authoritative contract/registry sources and exclude
        internal, test-only, unsupported, and secret configuration detail.
        Evidence: documented generation inputs.
        <!-- The `PAGES` table names, per page, the modules it owns, the fixture
        corpus it draws from and the subpaths it documents; the index page prints
        the inputs and the exclusions rather than leaving them in the generator.
        Four exclusions, each enforced rather than described. INTERNAL: only
        exported declarations are read, because a private helper in a reference
        is an invitation to depend on it. TEST-ONLY: a page owning a `testing/`
        module is a violation, and a test tries four real builder names against
        every rendered page. UNSUPPORTED: no capability page exists, because all
        91 S0.12 rows are still pending and the index says so with the number.
        SECRET: five credential shapes are scanned for.
        THE SECRET RULE EARNED ITS PLACE ON THE FIRST RUN. It found three hits in
        error.md — the S1.10.e leak corpus deliberately carries a JWT, a provider
        key and a DSN with a password, because a leak scan nobody can watch
        detect anything is not a scan. Exempting the page would have been the
        easy fix and the wrong one: it is the page most likely to carry a real
        leak. So the examples are REDACTED to their shape, the page says how many
        and why, and the scan runs AFTER the redaction — a credential arriving by
        a path the redactor does not cover still fails the build. -->
  - [x] S1.14.b Generate capability, route, command/query, event/job, schema,
        error, version, lifecycle, and plugin reference pages. Evidence:
        complete reference index.
        <!-- All nine, plus `identity` and `invariants`, because those two carry
        the types every other page links to and a reference that omitted them
        would have 200 dangling links.
        COMPLETENESS IS A PROPERTY, NOT A COUNT: every production module is owned
        by exactly ONE page, and `checkCoverage` fails on a module no page owns,
        a module two pages own, a page that documents nothing, and a required
        page that was not generated. So "is the reference complete?" is answered
        by the gate rather than by reading the index. command-query and event-job
        are VIEWS over `envelopes` rather than owners — those four envelope kinds
        are what a reader looks up, and the shared schema is one page down —
        which is why ownership and presentation are separate ideas here. -->
  - [x] S1.14.c Include valid and invalid examples, authorization/scope,
        idempotency/concurrency, pagination, state/failure, compatibility,
        deprecation, and evidence links. Evidence: section completeness check.
        <!-- Twelve sections, identical on every page, and a test asserts every
        one is present on all eleven. A reader who learns where the idempotency
        answer lives on one page knows where it lives on all of them, and a
        missing section is a hole rather than a formatting choice.
        THE SIX CROSS-CUTTING SECTIONS ARE DERIVED, not written: a field or symbol
        appears when its NAME matches a named vocabulary. That is mechanical on
        purpose, and both directions of the trade are stated on the page — it can
        list a symbol whose name suggests a concern it does not carry, and it
        cannot go stale, because a new field carrying that concern appears
        without anybody remembering to mention it. A section with no matches
        prints why it does not apply (20 of them across the set) rather than
        being omitted, because an omitted section is indistinguishable from an
        unanswered one. Examples are split accepted/refused by ONE uniform rule
        over seven differently-shaped corpora, each refusal naming the code or
        violation kinds it must produce, and a page shows at most three of each
        WITH the full count — a truncated view must never read as coverage. -->
  - [x] S1.14.d Sort output and serialize examples deterministically with pinned
        tool versions. Evidence: repeat generation is byte-identical.
        <!-- Symbols sorted by name, pages by id, examples by case id, payloads
        through the repository's stable stringifier; a test builds twice and
        compares every page byte for byte.
        THE PINNED TOOL IS PRETTIER, AND IT HAD TO BE. lint-staged runs
        `prettier --write` on every staged .md, so generated markdown that was
        not already Prettier-shaped would be reformatted at commit time and the
        drift gate would fail on every commit forever. The generator therefore
        formats each page through the repository's own resolved Prettier config
        and RECORDS the version in the index. A Prettier upgrade then shows up as
        drift, which is the correct outcome: the formatter is part of the output
        contract, and the alternative is a silent reflow in an unrelated pull
        request. -->
  - [x] S1.14.e Validate internal links, anchors, schema refs, example parsing,
        and source provenance. Evidence: documentation validation output.
        <!-- `validatePages` over the RENDERED markdown, not over the model:
        every `[text](#anchor)` and `[text](./page.md#anchor)` must resolve to a
        heading that exists (slugged the way the renderer slugs them), every
        fenced JSON block must parse, no page may match a credential shape, and
        every documented symbol must name a source file the page claims. Schema
        refs are the interesting one — a field typed `ArtifactRef` is rendered as
        a LINK to the page that owns it, so the anchor check turns "the type
        reference resolves" into something the build proves rather than something
        the reader discovers is false. Each violation kind has a deliberate
        failing fixture, including the one that matters most: a cross-page anchor
        that DOES resolve must not be reported, or a validator that rejects
        everything would look identical to one that works. -->
  - [x] S1.14.f Add CI check that regenerates in memory or a temp directory and
        prints actionable drift. Evidence: deliberate source-change failure.
        <!-- `--check` rebuilds the whole model and every page in memory, and
        compares against what is committed. Actionable means three things it
        prints and most drift gates do not: WHICH file differs, the first
        differing LINE with both versions quoted, and the command that fixes it.
        A gate that says "output differs" sends somebody to a 700-line diff. It
        also fails on an ORPHAN — a committed page the generator no longer
        produces — which a file-by-file comparison alone would never notice, and
        which is exactly what a renamed page leaves behind.
        The deliberate-source-change evidence runs the real reader over a changed
        contract and asserts the output changes, rather than editing the kit and
        putting it back; an assertion that a gate fires must not depend on
        somebody remembering to undo the thing that made it fire. -->

### S2 — Lifecycle, revisions, decisions, and gates

- [x] S2.1 Extract the generic stage-machine engine with typed stage unions.
      Evidence: illegal transitions cannot be expressed without an explicit
      guard bypass type.
      <!-- libs/oshun/workbench-kit/src/stages.ts, published as
      `@oshun/workbench-kit/stages`, with 43 cases in stages.spec.ts. 855 tests
      in the kit; all nine Nx targets green.
      WHY IT IS A SEPARATE MODULE FROM S1.9: that cell lets a plugin DECLARE a
      stage model and has the host validate it at boot; this one decides whether
      a transition may happen. They fail differently — a bad model is a boot
      error somebody fixes before shipping, and a bad transition is a published
      document nobody can unpublish — so they are separate surfaces with one
      shared implementation of the two graph analyses (below).
      THE PROPERTY THE MODULE EXISTS FOR: an illegal transition cannot be
      EXPRESSED. Not rejected at runtime — expressed. `plan()` takes only a
      transition id the graph contains, and anything else needs
      `PrivilegedBypass`. There is deliberately no third path, because the third
      path is what every workflow engine grows under deadline: a `force`
      boolean, then a `force` boolean with a comment. -->
  - [x] S2.1.a Define typed stage identity, stage metadata, initial/terminal
        flags, and domain-owned stage union registration. Evidence: stage
        definition type tests.
        <!-- `StageSpec<TStage>` + `defineStages`, returning a BRANDED
        `StageUniverse`. The brand is load-bearing: without it a caller can
        assemble a `StageSpec[]` inline and skip the function where the
        one-initial-stage and duplicate-name rules live, and a universe that was
        never checked looks exactly like one that was.
        Two refusals worth naming. Zero initial stages means nothing can be
        created; TWO means the answer to "where does a new object start?"
        depends on which row the reader saw first, and both readers are
        confident. `meaning` is required on every stage — a sentence saying what
        being in it means — because that is the sentence the next person adding
        a stage needs and the one nobody writes down. -->
  - [x] S2.1.b Define typed transition identity, source set, target, trigger,
        actor scope, guard set, side-effect intent, and reversal metadata.
        Evidence: transition schema tests.
        <!-- `Transition<TStage>` carries all eight. `from` is a SET rather than
        one stage: "cancel" is one transition available from three stages, and
        modelling it as three transitions is three places to add the fourth
        guard to.
        THE FIELD THAT DOES REAL WORK IS `effects`. It is declared rather than
        discovered, because it decides whether a transition can be undone by
        writing the old stage back — publish sends email, and unpublishing does
        not unsend it. The graph therefore REFUSES a transition that claims
        `reversible: true` while causing a notification, a publish or a payment:
        a reversal that only looks complete is worse than none. `reversal` also
        names the transition that undoes it, and the pair is checked to actually
        lead back, so "reversible" is not a boolean somebody set once. -->
  - [x] S2.1.c Build a transition graph that rejects unknown stages, duplicate
        transitions, unreachable stages, unintended cycles, and missing initial
        state. Evidence: graph validation tests.
        <!-- `buildStageGraph` over twenty violation kinds. Every check is one a
        reviewer would otherwise do by hand over a diagram, and the two a diagram
        makes hardest are the ones that matter: REACHABILITY (a stage nothing can
        get to is either dead or missing an edge, and both look like a complete
        diagram) and CYCLES (a loop is invisible in a list of transitions and
        obvious only once an object is stuck in one).
        A cycle is legitimate — rework, reopen, re-review — and is declared by
        marking the edge that CLOSES it, not every edge on it; requiring all of
        them would mean marking most of a healthy graph, which is how a rule
        stops being read. A self-loop needs no `revisits` at all, because
        `reentrant` on the stage is already that declaration, and a self-loop on
        a stage that did not declare itself gets the better error
        (`self-transition-not-reentrant`) rather than "there is a cycle
        somewhere". Cycles are reported per COMPONENT: an eight-edge cycle
        reported eight times reads as eight problems. -->
  - [x] S2.1.d Expose allowed-transition queries with exact guard requirements
        but no mutation. Evidence: query known-value tests.
        <!-- `allowedTransitions`, `isLegalTransition`, `deadEnds`. The queries
        return the EXACT guard ids each transition demands, not "some guards
        apply" — a UI built on this can answer "why is this disabled?" in the
        client instead of by attempting the transition and reading the error.
        `deadEnds` is the query nobody asks for and everybody needs: a stage with
        no exit that never declared itself terminal reads as "still in progress"
        on every dashboard forever.
        `NoInfer` on the stage parameter is what makes the typing real. Without
        it TypeScript takes a candidate from both arguments and widens the union,
        so querying one domain's graph with another domain's stage typechecks —
        the exact mistake S2.1.g exists to prevent. The `@ts-expect-error` in the
        spec was UNUSED until this was added, which is how it was found. -->
  - [x] S2.1.e Execute transitions only through a typed authorized command and
        expected revision. Evidence: direct-state-write negative tests.
        <!-- `TransitionCommand` requires a non-null `expectedRevision`: unlike a
        create, a transition always has a previous state, and one applied without
        it is a lost update that leaves the object in a stage nobody chose. The
        module exports NO writer — `plan()` returns a decision and a planned
        write, and the caller's persistence layer performs it. A module that can
        both decide and write is one where the decision can be skipped by calling
        the writer, and the writer is always the more convenient of the two; the
        negative test asserts the surface contains no set/apply/commit/write/
        force/move symbol at all.
        The check order is deliberate: authorization first (an unauthorized
        caller learns nothing about which transitions exist), then existence,
        then GUARD COVERAGE, then the verdicts. Coverage before verdicts is the
        one that matters — a command carrying two verdicts for a three-guard
        transition has a guard nobody ran, and folding the verdicts it did carry
        reports a pass. Missing verdicts come back `indeterminate`, which is a
        different outcome from `blocked`: one is a policy message for the user,
        the other is an incident for whoever owns the unreachable dependency. -->
  - [x] S2.1.f Require an explicit privileged bypass type carrying authority,
        reason, scope, evidence, and audit linkage. Evidence: compile/runtime
        bypass tests.
        <!-- `PrivilegedBypass` + `planBypass`, a SEPARATE entry point rather
        than a parameter on `plan`. Sharing one would make the override reachable
        by passing one more argument, and every override in every workflow engine
        started as one more argument. All five fields are required and an empty
        string is not a value — a reason of `""` is worse than no reason, because
        it makes the audit row look complete. Six runtime cases, one per field
        plus a whitespace-only evidence entry.
        The compile-time half is that `TransitionCommand` has no `bypass` field
        at all, asserted with a `@ts-expect-error`. A bypass overrides the
        WORKFLOW and not the vocabulary: a target stage the universe does not
        define is still refused. -->
  - [x] S2.1.g Instantiate two domains with disjoint stage unions to prove no
        shared hard-coded stages. Evidence: cross-domain compile fixtures.
        <!-- A story desk (draft/review/legal/published/retracted) and an
        incident console (triage/mitigating/monitoring/resolved) — no shared
        stage name, no shared transition, no shared guard. Both build a graph and
        both answer the same queries, which is what "the kit assumes no domain
        vocabulary" has to mean. A kit that quietly assumed
        `'draft' | 'review' | 'published'` would pass every runtime test in the
        file and be a content-authoring kit wearing a general name.
        Two `@ts-expect-error` directives make it a COMPILE fixture rather than a
        naming convention, and they are checked by `tsc --noEmit` on
        tsconfig.spec.json — an unused directive is itself an error, so the
        fixtures cannot rot into assertions that no longer assert. -->

  The extraction the cell names is the two graph analyses. `plugins.ts` carried
  its own reachability loop for the declarative model a plugin contributes, and
  two implementations of "can you get there from here?" is one that disagrees
  with the other the first time a source set or a self-loop is involved. Both
  now call `unreachableFrom`/`cyclesIn` over an untyped `EdgeSet` — the plugin
  contract is a WEAKER shape than an engine transition (no trigger, no effects,
  no reversal), so it lifts into that rather than into a full graph, and nothing
  has to invent the fields the plugin never stated.
- [x] S2.2 Make transition guards pure and evidence-driven. Evidence:
      known-value tests cover pass, fail, stale, missing, and unavailable
      evidence.
      <!-- libs/oshun/workbench-kit/src/guards.ts, published as
      `@oshun/workbench-kit/guards`, 40 cases. 896 tests in the kit; all nine Nx
      targets green.
      PURITY IS ENFORCED, NOT REQUESTED. The whole value of a guard's answer
      rests on it being reproducible: a guard that reads the clock says yes on
      Tuesday and no on Wednesday about the same object, and one that reads
      randomness cannot be replayed at all — so "the publish gate passed" stops
      being a claim anybody can check. `evaluateGuard` therefore takes the
      ambient world away for the duration of the call rather than asking authors
      not to use it. -->
  - [x] S2.2.a Define immutable guard input containing current object/revision,
        actor/scope, target transition, versioned evidence refs, policy, and
        clock snapshot. Evidence: guard input contract.
        <!-- `GuardInput` carries all seven. THE DESIGN IS THE ABSENCE: there is
        no client, no repository, no config object and no clock — only a clock
        SNAPSHOT, which is a value. A guard cannot reach for what it was never
        handed, and adding a field here is a kit decision a reviewer sees.
        `EvidenceRef` is versioned because "the rights check passed" is
        meaningless without which rights record it passed against: the record
        changes and the decision it justified does not. The input is deep-frozen
        before the call, so a guard that writes to its subject fails instead of
        making its own answer depend on whether it ran first. -->
  - [x] S2.2.b Prohibit database, network, provider, current-time, random,
        global configuration, and mutation access inside guard evaluation.
        Evidence: boundary lint/type tests.
        <!-- A RUNTIME HARNESS rather than a lint rule, because guards live in
        domain packages the kit's lint config does not reach, and because a lint
        rule is advice while a throwing stub is a failing test. `Date` is
        replaced wholesale (a guard reaches the clock through the constructor as
        readily as through `now()`, and stubbing only the static leaves the more
        common spelling working), `Math.random` and `fetch` likewise; `Date.parse`
        and `Date.UTC` stay, because a guard has to read the instant it was
        given. Everything is restored in a `finally` — a harness that could leave
        `Math.random` replaced would take the process down the first time a guard
        failed, and that is asserted.
        ANY throw becomes `evaluator-error`, including a plain TypeError: the
        alternative is a harness that classifies some failures and lets the rest
        escape into the caller, where a broken gate becomes a 500 instead of a
        gate outcome.
        THE LIMIT, STATED IN THE MODULE: a guard that captured a database client
        in a closure before evaluation is invisible to a harness that only
        controls globals. That half belongs to the package hosting the guard;
        this half covers what people reach for without noticing. -->
  - [x] S2.2.c Define pass, fail, stale, missing, unavailable, unsupported, and
        evaluator-error results with stable reason/remediation fields. Evidence:
        result union tests.
        <!-- Seven statuses and a closed seven-value remediation vocabulary, with
        a default mapping per status and a per-answer override. The reason there
        are seven and not two: `pass`/`fail` are decisions about the SUBJECT and
        the other five are about the evidence or the guard, so collapsing any of
        them into `fail` tells a user to fix something that is not wrong with
        their work — refresh evidence, produce evidence, retry later, this does
        not apply, and page the team that owns the broken guard are five
        different actions. `evaluator-error` must never read as a fail, because a
        broken guard reporting failure is a broken guard nobody fixes.
        Remediation is a closed vocabulary rather than prose for the reason the
        error module gives: the first person to paste a working command into a
        free-text field puts a credential in a log line. -->
  - [x] S2.2.d Compose multiple guards deterministically with explicit
        blocker/warning and short-circuit policy. Evidence: composition
        truth-table tests.
        <!-- `composeGuards` sorts by guard id before folding, so a decision does
        not depend on the order a host registered guards in — asserted by
        composing the same results forwards and backwards and comparing
        fingerprints. A seven-row truth table covers blocking vs advisory
        against pass/fail/unavailable/unsupported.
        Two decisions worth arguing with. An `unsupported` guard ABSTAINS: not a
        pass, because "this guard does not apply" is not evidence the transition
        is safe, and not a block, because a guard that does not apply cannot
        refuse. And short-circuit is OFF by default — it saves work and produces
        a UI that reveals one problem at a time, so a user fixes four things in
        four round trips. When it is on, the guards that never ran are NAMED in
        the decision, so one reported problem does not read as one problem.
        An unregistered guard is treated as blocking: a result from a guard
        nobody registered is more suspicious than one from a guard that was. -->
  - [x] S2.2.e Preserve every consulted evidence version and policy version in
        the result. Evidence: provenance assertions.
        <!-- The HARNESS attaches provenance, not the guard, so a guard cannot
        forget to. Consulted evidence is sorted, so two runs record it
        identically, and the policy id and version are on every result including
        the ones that consulted nothing.
        The staleness budget is applied here for the same reason: a guard that
        checks its own evidence age is one that can forget to, and forgetting
        produces a PASS — the direction a missing check must never fail in. It
        applies only to a pass; a `fail` stays a fail however old the evidence
        is, because the subject is wrong either way and "refresh evidence" would
        send the user to fix the wrong thing. -->
  - [x] S2.2.f Re-evaluate identical input byte-for-byte to the same semantic
        result. Evidence: determinism/property tests.
        <!-- `decisionFingerprint` folds every guard id, status, reason key,
        remediation, policy version and consulted evidence version into one
        string. Comparing fingerprints rather than results is the point:
        comparing results passes whenever both runs happen to be wrong in the
        same way, and a fingerprint that never changed would make every
        re-evaluation agree — so the suite also asserts it CHANGES when a single
        evidence version or the policy version moves. -->

  The purity harness is the part worth reusing elsewhere in this ledger. Every
  other "must be deterministic" cell in Phase S is currently a convention; this
  is the shape that makes one enforceable — take the capability away for the
  duration of the call, restore it in a `finally`, and turn the resulting throw
  into a typed outcome rather than an exception.
- [ ] S2.3 Add optimistic concurrency with expected revision. Evidence:
      concurrent updates return a typed conflict and never last-write-win
      silently.
      <!-- evidence/partial: a–c, e and f are done and checked below;
      `libs/oshun/workbench-kit/src/concurrency.ts`, published as
      `@oshun/workbench-kit/concurrency`, 70 cases plus a 10-case race harness
      and 13 committed conflict goldens. 980 tests in the kit at that point; all
      ten Nx targets green (the tenth is new — see S2.3.b). The PARENT stays unchecked
      because S2.3.d's named evidence is a browser/BFF journey and the kit has
      no host to run one in until S7.
      THE THREE HOLES A PRECONDITION DOES NOT CLOSE BY ITSELF, which is what
      this cell is actually about. (1) It is optional — a nullable
      `expectedRevision` makes "I mean to create this" and "I do not know what
      revision I am editing" the same value, and the second one is a lost
      update. (2) The conflict is a dead end: a 409 with no payload tells a user
      to reload, which throws away what they typed. (3) The retry helpfully
      refreshes the revision, which defeats the check on exactly the requests
      the check existed for. Each is closed by a type rather than a convention.
      One collateral find, in S1.10's leak scan rather than here: the api-key
      canary detector required an unbroken alphanumeric body after `sk-`, so it
      matched only the unsegmented form no issuer mints and passed every real
      `sk-live-…` / `sk_test_…` / `sk-proj-…` key. Found by planting a realistic
      key in a DECLARED conflict field, where the closed-shape rule cannot see
      it and only the value scan can. Detector broadened, boundary pinned by a
      new lookalike case. -->
  - [x] S2.3.a Require expected revision or an explicitly authorized
        create-if-absent precondition on every mutable command. Evidence:
        command schema tests.
        <!-- `Precondition` is a two-variant union — a revision, or a
        create-if-absent carrying the grant that authorized it, the natural key
        that makes absence checkable and idempotent, and a reason. THE DESIGN IS
        THAT THERE IS NO THIRD VALUE and no absence: `parsePrecondition(null)`
        is refused rather than defaulted to a create, because defaulting is the
        repair that turns every malformed update into a brand new object.
        The "on every mutable command" half is a SCHEMA audit rather than a
        runtime check, because the question is "is there a mutable command
        anywhere in this workbench that can be sent without one?" and no amount
        of testing the happy path asks it. `auditCommandSchemas` also runs the
        cross-checks that only exist between two columns: a command declaring
        itself safe to replay while carrying no idempotency key is the
        combination that turns a network timeout into a double application, and
        each column reads as fine alone. -->
  - [x] S2.3.b Compare expected revision and mutate/increment revision
        atomically in persistence. Evidence: real-database race tests.
        <!-- `pnpm nx revision-race @oshun/workbench-kit` —
        `tools/revision-race.mjs`, a tenth Nx target. THREE real concurrent
        PostgreSQL connections, run green on PostgreSQL 16.14: an atomic
        `UPDATE … WHERE revision = $expected` produced exactly one winner and no
        lost update, and `INSERT … ON CONFLICT (natural_key) DO NOTHING`
        produced exactly one creator.
        THE TWO CONTROLS ARE WHY THAT MEANS ANYTHING. The same race runs against
        a read-then-write UPDATE and a check-then-INSERT, and both MUST come
        back red — they did, losing an update and creating a duplicate. Without
        them a green run is indistinguishable from contenders that never
        overlapped, or from a harness that measures nothing.
        The harness itself is `@oshun/workbench-kit/testing/concurrency`, so the
        property is defined once for all seven domains rather than seven times.
        Its two calibration stores live in `./testing/doubles` because they hold
        state, which is the kit's own rule about test utilities applied to the
        kit's own test utilities. And the target FAILS rather than skips with no
        database: a skipped race that reports success would let "S2.3.b is
        green" mean "nobody has a database". It is therefore not in the CI
        inventory gate, which has no database service. -->
  - [x] S2.3.c Return typed current/expected revision, affected target, safe
        current metadata, and compare/reload remediation on mismatch. Evidence:
        conflict envelope fixtures.
        <!-- 13 golden envelopes + 3 refusals in
        `evidence/v1-workbenches/workbench-kit-conflict-fixtures.json`, written
        by hand in the generator and rebuilt by `conflictEnvelope()` in the
        spec. Two independent statements of the same fact: a corpus derived from
        the code it checks agrees with any bug that code has.
        THE AFFECTED TARGET is the field that makes a conflict actionable on the
        six non-single paths. "Your import failed" versus "row `sku-40199` was
        edited by someone else at 14:02" is the whole difference, and the goldens
        cover all seven paths so a corpus of single-object conflicts cannot pass
        for coverage.
        SAFE METADATA is a closed set of scalars checked as a closed set — the
        union of detail keys across every golden must be inside it, which is the
        assertion that fails the first time somebody adds a helpful
        `currentTitle`. `updatedBy` is an actor id and never a name, inherited
        from `ActorRef` refusing display data rather than re-decided here.
        Two registry changes came with it. `reload-and-compare` is a new
        remediation action and the first SELF-SERVICE one, so `publicError`'s
        `requiresOperator` default now reads off a named list instead of
        `action !== 'none'` — routing a routine conflict to an operator buries it
        in a support queue. And `state.subject_absent` is a new code because an
        absent subject is NOT a stale revision: there is nothing to reload, and
        "reload and compare" sends the user to something that will 404. It also
        gave the registry its first 404, so the transport cross-check row mapping
        rpc `not-found` to HTTP 404 can now fire at all. -->
  - [ ] S2.3.d Preserve the rejected client intent for compare, retry, copy, or
        support recovery without applying it. Evidence: browser/BFF conflict
        journey.
        <!-- evidence/partial: the SUBSTANCE is implemented and covered by 9
        cases — `preserveIntent` (payload verbatim, precondition left STALE),
        `compareIntent`, `copyableIntent`, `supportViewOfIntent` (scoped like the
        S1.10.f cause lookup, expiry checked so the two refusals do not leak
        existence) and `resumeIntent`, which is the ONLY route from an intent to
        a write and requires a fresh observation, an explicitly restated
        precondition and a new idempotency key. Restating the rejected
        precondition is itself refused: resuming with it either fails again or,
        if the head happens to have returned, applies against a state the author
        never saw.
        The checkbox stays UNCHECKED because the named evidence is a browser/BFF
        journey and there is no host to run one in: the kit has no consumers yet
        (S1.2's export map is still empty of ratified capabilities) and the
        shared application shell is S7. Writing a throwaway app to satisfy the
        wording would be a fixture asserting itself. Re-check this when the first
        workbench adopts the kit. -->
  - [x] S2.3.e Apply the same precondition to bulk, import, job, restore,
        transition, and compatibility mutation paths. Evidence: alternate-path
        negative tests.
        <!-- Seven named paths, ONE `applyPrecondition`. Seven copies of the
        comparison would be six chances to write `>=` where `===` belongs, and
        the copy that drifts is always the one nobody reads — so the path is a
        parameter that a conflict reports, never a reason to fork the logic.
        Asserted by classifying the same conflict on all seven and comparing.
        Four negatives, each a real per-path bug: a bulk request with ONE
        request-level revision (right about one subject at most, so the rest are
        unchecked writes); a job whose precondition is checked when it is queued
        rather than when it runs (the gap between the two IS the window the
        check exists for); a restore whose precondition names its SOURCE
        revision rather than the current head (the mistake is nearly invisible —
        the source is the revision the request is about, and using it makes the
        restore succeed no matter what has happened since, which is exactly the
        change the user needed to be shown); and an automatically replayed
        restore, which writes a second revision every time.
        Coverage is reported HONESTLY: an audit of one command returns no
        violations AND names the six paths it did not look at, because an empty
        violation list otherwise reads as full coverage.
        `preconditionOfTransition` bridges S2.1 rather than duplicating it, so
        the transition path goes through the same function as the other six. -->
  - [x] S2.3.f Prevent automatic retry from changing the expected revision or
        overwriting a remote update. Evidence: retry conflict tests.
        <!-- THE SHARPEST CELL IN S2.3. An automatic retry that re-reads the
        current revision and tries again is a last-write-win wearing an
        optimistic-concurrency costume: it defeats the precondition on exactly
        the requests the precondition was carried for, using the mechanism meant
        to be safe. So `retryDecision` has no code path that produces a
        refreshed attempt — asserted exhaustively over every failure kind and
        every budget, not just the obvious one — and a conflict returns
        `stop-conflict`, a decision distinct from `stop` so a caller routes it to
        the recovery flow rather than to an error toast.
        Only `unknown-outcome` may be retried, and the safety argument is a PAIR:
        the idempotency key covers "it already applied" and the precondition
        covers "somebody else applied something". Either alone is insufficient,
        which is why `retryViolations` reports a changed key and a changed
        precondition as separate findings, along with a re-minted next revision
        and a counter that did not move (a budget that never advances is
        infinite). All four are reported at once rather than first-wins.
        Backoff and jitter are deliberately absent and said so: timing belongs to
        the transport, and this module answers only "what may change between
        attempts?". -->
        <!-- evidence: libs/oshun/workbench-kit/src/concurrency.ts +
        concurrency.spec.ts (70 cases), src/testing/concurrency.ts +
        concurrency.spec.ts (10), tools/revision-race.mjs,
        scripts/v1-workbenches/generate-workbench-kit-conflict-fixtures.mjs
        (+12 structural tests), evidence/v1-workbenches/
        workbench-kit-conflict-fixtures.json. -->

  The reusable shape here is the CALIBRATED HARNESS. S2.3.b is the first cell in
  Phase S whose property lives outside the code — no type makes a write atomic —
  and the answer was to ship the race in the kit and run it against a real
  engine with deliberately broken controls beside it. A conformance harness with
  no negative control proves that it ran, not that it can fail; every later cell
  that has to check something a database, a browser or a provider does should
  carry one the same way.
- [x] S2.4 Add immutable revision metadata and parent references. Evidence:
      history integrity test detects gaps, cycles, and hash mismatch.
      <!-- libs/oshun/workbench-kit/src/revisions.ts, published as
      `@oshun/workbench-kit/revisions`, 49 cases; plus
      `tools/history-constraints.mjs`, an eleventh Nx target running against a
      real PostgreSQL. 1,031 tests in the kit; all eleven targets green.
      A REVISION HISTORY IS THE PART OF A WORKBENCH EVERYBODY READS AND NOTHING
      CHECKS, because append-only things are assumed to be fine. They are not: a
      parent pointer into a hard-deleted row, a sequence number two writers
      allocated, a merge with no recorded base, a content hash left behind by an
      in-place typo fix. Each is silent, and each turns the history from a record
      into a story. So the module is mostly `checkHistoryIntegrity` —
      seventeen violation kinds, every one of them reachable, asserted as a set
      rather than as a count so a kind nothing can produce fails the suite.
      A NINE-MUTANT RUN over the checker killed nine of nine after it killed one
      of my own: `Object.is(input, -0) ? '0' : JSON.stringify(input)` in the
      canonicalizer is DEAD, because `JSON.stringify(-0)` is already `"0"`. The
      branch was removed and the assertion kept — the property is what matters,
      and a branch nothing can reach is a claim the reader believes and the code
      does not keep. -->
  - [x] S2.4.a Persist stable revision ID, entity/workbench scope, schema
        version, author/delegate, timestamp, reason, and content hash. Evidence:
        revision metadata tests.
        <!-- `RevisionRecord` carries all eight plus `sequence`. The sequence is
        there AS WELL AS the lineage because the two catch different faults: a
        broken parent pointer is a structural break, and a reused or skipped
        sequence is a concurrency break that leaves the structure intact and
        looking fine.
        `reason` is required and non-empty, enforced by the integrity check and
        again by a database CHECK. A history whose entries say nothing about why
        they exist is a list of timestamps, and "why is this like this?" is the
        only question anybody actually brings to one. The author is an
        `ActorRef`, so a delegated author carries both identities — a row with
        one of the two can answer neither "who did this" nor "whose authority
        was used". -->
  - [x] S2.4.b Persist one parent for linear history and explicit multiple
        parents/base for merge or divergence semantics. Evidence: lineage
        fixture tests.
        <!-- `Lineage` is three variants rather than a `parents[]` array: the
        array turns "is this the first revision?" and "is this a merge?" into
        arithmetic on a length that every reader has to redo, and it cannot make
        `base` required only where it means something.
        A MERGE REQUIRES ITS BASE. Without the common ancestor the three-way
        comparison was computed against, "which side won this field, and why?"
        has no answer for the rest of the object's life. Refused rather than
        defaulted, because the default would have to be a guess at an ancestor.
        `merge-base-not-an-ancestor` catches the base that is merely plausible.
        DIVERGENCE IS NOT A FAULT. Two revisions off one parent is a legitimate
        state; the integrity check stays silent about it and the reconstruction
        reports two heads and a branch point, for a UI to resolve. And the cycle
        walk is a THREE-COLOUR walk rather than a visited set, because a visited
        set cannot tell a cycle from a diamond — and a diamond is the normal
        shape of every correct merge, so the naive check would fire on all of
        them. -->
  - [x] S2.4.c Prevent update/deletion of revision content and metadata outside
        governed retention/tombstone operations. Evidence: immutability negative
        tests.
        <!-- Three governed operations and nothing else: tombstone the content,
        place a legal hold, release one. `immutabilityBreaches` compares two
        states of the same revision and names every immutable field that moved,
        which is the check for the edit that happens OUTSIDE the module — a
        migration, a support script, an ORM `save()` on a loaded entity.
        A TOMBSTONE NEVER CLEARS THE HASH. That single decision is what lets an
        erasure request and a verifiable chain both hold: the content goes, the
        metadata and the original digest stay, and the history now says "this was
        removed", which is different from "this was never here" and different
        again from a gap nobody can explain.
        A legal hold BLOCKS a tombstone rather than losing to it. The collision
        between "delete my data" and "preserve this for litigation" is routine
        and a person resolves it; what the type does is make the collision
        visible at the moment of removal instead of letting whichever code path
        ran last decide. Releasing is by hold IDENTITY, never by position.
        THE SAME RULE AT THE LAYER THAT CAN HOLD IT: a `BEFORE UPDATE` trigger in
        `tools/history-constraints.mjs` refuses any change to an immutable
        column, verified against a real PostgreSQL — six in-place edits rejected
        (reason, timestamp, content hash, re-parent, un-tombstone, tombstone
        under hold) while the governed tombstone and the legal hold still go
        through. An application-level rule runs where somebody called it; this
        runs where the write lands. -->
  - [x] S2.4.d Enforce unique revision sequence/identity and detect missing
        parent, cross-entity parent, self-parent, and cycle. Evidence: database
        and graph integrity tests.
        <!-- BOTH halves, and the split between them is the finding.
        DATABASE (`pnpm nx history-constraints`, real PostgreSQL 16.14): the
        engine refused 12 classes — duplicate identity, duplicate sequence
        (raced on two real connections, because a unique constraint is only
        interesting under concurrency), dangling parent, self-parent, empty
        reason, deleting a parent that still has children (no cascade: a cascade
        deletes history to keep a pointer tidy), and the six immutability edits
        above.
        GRAPH: the same run then inserts a history every column constraint
        accepts and hands it to `checkHistoryIntegrity`, which found 6 kinds the
        engine structurally cannot see — cycle, multiple-roots,
        schema-version-regression, sequence-gap, sequence-not-after-parent,
        timestamp-before-parent. Each is a property of the GRAPH rather than of a
        row. Either half alone would read as coverage.
        Learned in the doing: the checker HOLDS its cycle walk while a structural
        fault is outstanding, so a calibration fixture sliced out of the valid
        history produced `missing-parent` and no cycle. Suppressing the walk is
        right — a walk over dangling edges reports a loop that is really a
        missing row — but it means a fixture has to be built whole. -->
  - [x] S2.4.e Compute/verify content hash from canonical stored content or
        referenced artifact bytes. Evidence: tamper detection tests.
        <!-- THE KIT DOES NOT HASH, and says so. Its `contract` runtime class may
        not reach a Node built-in and the Web Crypto digest is async, which would
        make every caller of a pure comparison async; the host has a real hash
        function. What the host does NOT have is a canonical form both ends
        agree on, and that is the half that produces mismatching digests for
        identical content, so it lives here.
        `canonicalize` REFUSES rather than coerces, and every refusal is a value
        whose JSON round trip is lossy: NaN and Infinity both serialize to
        `null`, so two different contents would hash the same; `undefined`
        vanishes from an object and becomes `null` in an array, so the same
        content hashes differently depending on where it sits. It also names the
        PATH — `blocks[1].text` — and refuses a circular reference while
        accepting the same object twice, because a shared child is not a cycle.
        Six verdicts, not two. Collapsing them sends somebody to investigate a
        tamper that is a privacy request being honoured: `tombstoned` is not
        `tampered`, and an artifact-backed revision returns
        `artifact-pin-verified` — a COMPARISON against the digest the reference
        pins, because a function claiming to have verified bytes it never read
        would be the fabricated success this module exists to prevent. -->
  - [x] S2.4.f Reconstruct heads, branches, ancestry, and chronological audit
        links without mutable denormalized history. Evidence: reconstruction
        tests.
        <!-- NOTHING IS STORED. No `isHead`, no `childCount`, no `depth`. The
        property is asserted the way it has to be: append a child and the head
        moves, with nothing written to the old head. A denormalized head is a
        second source of truth, and the failure it produces is an object nobody
        can edit because the head it points at was rolled back — unfixable from
        the application, because the application believes the column.
        `clockDisagreesWithSequence` is the honest part. Two orderings exist and
        can differ: the sequence is what the database allocated, the timestamp is
        what a machine's clock said. Both are returned along with the fact that
        they disagree, rather than silently preferring one — when they do
        disagree, an audit rendered in timestamp order shows events in an order
        that did not happen.
        `ancestryOf` is breadth-first, so a merge shows both sides before their
        shared parent rather than one side's whole history first, and it returns
        a typed MISS for a revision that is not in the set: an empty ancestry is
        what a root has, so returning it for "not loaded" renders "this is the
        first version" over a revision that simply was not there. -->
        <!-- evidence: libs/oshun/workbench-kit/src/revisions.ts +
        revisions.spec.ts (49 cases, 9/9 mutants killed),
        libs/oshun/workbench-kit/tools/history-constraints.mjs (Nx target
        `history-constraints`, real PostgreSQL). -->

  Worth carrying forward: this cell and S2.3.b are the two where the kit stopped
  being able to prove its own claim alone, and both were answered the same way —
  put the rule in the layer that can hold it, then run a harness that shows what
  the OTHER layer catches that this one cannot. A constraint set with no graph
  check beside it, or a graph check with no constraints beneath it, each reads as
  complete on its own.
- [x] S2.5 Add semantic diff adapters for scalar, list, tree, graph, timeline,
      and binary-metadata changes. Evidence: golden diff fixtures.
      <!-- libs/oshun/workbench-kit/src/diff.ts, published as
      `@oshun/workbench-kit/diff`, 78 cases; 21 golden scenarios in
      `evidence/v1-workbenches/workbench-kit-diff-fixtures.json` written by hand
      in the generator and replayed by the spec against the real adapters, plus
      12 structural tests over the corpus. 1,110 tests in the kit; all eleven Nx
      targets green.
      THE CORPUS IS WEIGHTED TOWARDS WHAT A STRUCTURAL COMPARISON GETS WRONG
      rather than what it gets right, because the wrong answers are all
      defensible: a reparented section reported as a delete and an add, a
      rerouted edge that loses its identity, a conform to a different frame rate
      that reports nothing at all, one cut reported as three changes, and
      identical bytes whose metadata disagrees about them.
      A TWELVE-MUTANT RUN killed 10 of 12 on the first pass. Both survivors were
      in the split detector and both were masked by a later check that happened
      to catch the same fixtures; the two cases that distinguish them — pieces
      that OVERLAP rather than abut, and a re-identified clip covering the same
      span — were added and both mutants then died. Neither case was obvious
      from reading the code, which is the argument for running mutants at all.
      Coverage is three matrices rather than a count: every adapter, every change
      type, and every non-value `DiffValue` kind. The refusal states are what the
      contract exists for, and a corpus of ordinary changes proves none of
      them. -->
  - [x] S2.5.a Define common diff operation identity, path/anchor, before/after
        revision, change type, actor/provenance, and inaccessible/unsupported
        states. Evidence: shared diff contract.
        <!-- `DiffOperation`: a derived id (adapter, path, change type — so two
        runs agree and a cursor can address one), a sortable path, a translation
        KEY with scalar parameters, both `StableAnchor`s, and provenance carrying
        both revisions and the adapter version.
        THE ANCHORS ARE S1.4.e's, not a diff-only path. A client that can already
        jump to an anchor can jump to a change, and the time-base and
        graph-target vocabulary arrives with it instead of being re-modelled
        slightly differently here. Both sides are nullable and at least one is
        set: an added thing has no before-anchor.
        `DiffValue` is eight variants and every one of them is load-bearing.
        `absent`/`null`/`default` are three different facts — not sent, sent as
        nothing, not sent and the schema fills it in — and collapsing them makes
        "somebody cleared this" and "somebody never touched this" the same row.
        `redacted`/`inaccessible`/`unsupported` are three different refusals: it
        changed and you may not see to what; it may have changed and you cannot
        be told; nothing here can compare these two, which is a gap in the
        adapter rather than a fact about the content. `opaque` is bytes.
        `provenance.actor` is NULL between two revisions with several authors in
        between. Naming one of them would be a guess presented as a fact. -->
  - [x] S2.5.b Implement scalar/object-field comparison with
        absent/null/default, unit, locale-neutral, enum, and sensitive-value
        semantics. Evidence: scalar golden fixtures.
        <!-- Five semantics, and three of them are judgements rather than
        mechanics.
        UNITS: 1000 m and 1 km are one distance, so the magnitudes are compared
        after conversion and the result is a PRESENTATION operation naming the
        respelling. Reporting a change would send somebody to review a unit
        preference; reporting nothing would hide that the preference moved. A
        change of DIMENSION is `replaced`, not a resize. An unknown unit is a gap
        in the conversion table and the side carrying it says so, rather than the
        field being reported as changed.
        LOCALE-NEUTRAL: comparison is over typed values, never over formatted
        text — "1,234.5" and "1.234,5" are one number, and a diff that compared
        the strings would report every locale switch as an edit.
        ENUM: a value that is no longer a member is not a change to the field, it
        is the vocabulary having moved underneath it. Reported as `unsupported`,
        because an ordinary change sends somebody to fix content that was valid
        when it was written.
        SENSITIVE: the comparison still runs and only the values are withheld,
        parameters included. A diff that skipped sensitive fields would let a
        change hide behind its own sensitivity. -->
  - [x] S2.5.c Implement ordered/unordered list insert, delete, move, replace,
        duplicate identity, and item-field changes. Evidence: list golden
        fixtures.
        <!-- THE LIMIT IS THE POINT. With item identity a move is a move and an
        edit is one row; WITHOUT it a move is indistinguishable from a delete
        plus an add — not because this implementation is weak but because the
        information is not there. So `listCanDetectMoves` reports the capability
        and the unkeyed path runs a real longest-common-subsequence rather than a
        similarity heuristic that guesses. The LCS is O(n*m) and that tradeoff is
        written down: right for the tens-to-hundreds a workbench field holds,
        wrong for a hundred-thousand-line file — and a field that large is an
        artifact, which goes through the binary adapter.
        A DUPLICATED KEY is reported and then skipped. Which of two same-keyed
        items is being compared with which is a guess, and a guessed pairing
        produces a diff that is confidently wrong.
        An UNORDERED list is compared as a set, where a move cannot exist:
        reordering a permission list produces nothing at all. -->
  - [x] S2.5.d Implement tree add/remove/move/reparent/reorder/field changes
        with stable node identity and cycle-invalid state. Evidence: tree
        fixtures.
        <!-- A reparent is `moved` and a sibling shuffle is `reordered`, and they
        are separate change types because they call for different review
        attention — one collapsed "moved" makes the cheap one look expensive.
        A node that moved to a new parent ALSO lands at a new position, and the
        reorder is deliberately suppressed for it: one action shown as two rows
        is how a review of six edits reports twelve.
        A CYCLE IS NOT A TREE. The nodes in the loop are reported `unsupported`
        and the walk does not happen — comparing a structure that cannot exist
        either loops or produces an answer about it. -->
  - [x] S2.5.e Implement graph node/edge/port/property add/remove/reconnect and
        layout-versus-semantic change separation. Evidence: graph fixtures.
        <!-- An edge whose endpoints moved is `changed`, not remove-plus-add: the
        edge has an identity, and a review that loses it cannot say "this
        connection was rerouted", which is the only useful sentence about it. A
        retyped node IS a replacement, because nothing about a gain node survives
        becoming a reverb.
        LAYOUT IS SEPARATED FROM MEANING. A node's position is a real stored
        property and a change to it is a `presentation` operation, so a client
        renders "3 changes, and 44 layout changes" rather than "47 changes" — the
        second is a review nobody reads, and the reason they stop is that an
        afternoon of tidying looks exactly like an afternoon of work. -->
  - [x] S2.5.f Implement timeline/temporal add/remove/move/trim/split/merge,
        time-base conversion, track/lane, and parameter changes. Evidence:
        timeline fixtures.
        <!-- THE TIME BASE IS PART OF THE POSITION. Frame 300 is 10s at 30fps and
        10.01s at 29.97, so a comparison of the numbers reports "unchanged" for a
        conform that moved every clip in the sequence. Positions convert through
        the exact rationals S1.4.e already carries and are compared by
        cross-multiplication — 29.97 is 30000/1001 and comparing it as a float is
        how a cut lands one frame out after a round trip. The converse is
        asserted too: 10 seconds IS frame 300 at 30fps, and a rebase that changed
        nothing must not report a move.
        ONE CUT IS ONE ACTION. A split is detected on source identity plus
        CONTIGUITY plus exact span coverage, so it is not claimed whenever
        somebody adds a clip from the same source nearby; a merge is the same
        detector run backwards. A trim names WHICH edge moved, because "the head
        was trimmed" and "the tail was trimmed" are different edits.
        A MUSICAL time base is refused rather than converted: seconds per pulse
        depends on a tempo map the timeline does not carry, and converting would
        mean inventing a tempo and reporting the invention as a position. -->
  - [x] S2.5.g Implement binary metadata comparison for identity/hash, size,
        media type, dimensions/duration, codec, channels, embedded metadata,
        provenance, and availability without pretending to diff opaque bytes.
        Evidence: binary-metadata fixtures.
        <!-- The bytes are `opaque` and the twelve things around them are
        compared properly, which is both the honest answer and the useful one:
        "the codec changed and the duration did not" is actionable and "the file
        is different" is not.
        THE INTERESTING CASE IS THE INCONSISTENCY. Identical digests with
        different dimensions means one side's metadata was written by something
        that did not read the bytes — a finding rather than a change. Three
        fields are excluded from it with reasons: `provenance` is a record ABOUT
        the bytes rather than a property of them, `mediaType` is a label a
        re-ingest can legitimately correct, and embedded metadata may live in a
        sidecar the digest does not cover — flagging that would report an
        inconsistency every time an archive was re-catalogued.
        An unretrievable artifact is `inaccessible`, never `removed`: the second
        sends somebody to find out who deleted it. -->
  - [x] S2.5.h Provide stable ordering, accessible summaries, pagination, and
        redaction across all adapters. Evidence: cross-adapter contract tests.
        <!-- All four live OUTSIDE the adapters, in `assembleDiff`. An adapter
        that ordered its own output would be a second ordering, and two orderings
        disagree the first time somebody adds a change type.
        ORDERING is total — path, then change type, then id — and asserted by
        assembling the same operations forwards and backwards and comparing bytes.
        SUMMARIES are counts plus a per-operation translation key with scalar
        parameters; never a prose sentence, for the reason the error module
        gives. Redacted, inaccessible and unsupported operations are COUNTED: a
        diff over restricted content that looked empty would say "nothing
        changed", which is the one wrong answer a reviewer cannot recover from.
        PAGINATION is by cursor on the last operation id, not by offset. A diff
        is recomputed per request, so an offset skips or repeats rows — that is
        the failure mode here rather than an edge case.
        REDACTION hides VALUES and never rows, parameters included, and is
        prefix-matched on path SEGMENTS so `payroll` is not caught by a rule
        about `pay`. The cross-adapter test runs all six through the same
        assembly and asserts every one produces at least one operation — a
        contract test over five would pass with the sixth unwired. -->
        <!-- evidence: libs/oshun/workbench-kit/src/diff.ts + diff.spec.ts (78
        cases, 12/12 mutants killed),
        scripts/v1-workbenches/generate-workbench-kit-diff-fixtures.mjs (+12
        structural tests), evidence/v1-workbenches/workbench-kit-diff-fixtures.json
        (21 scenarios, 29 operations). -->

  A method note for the rest of the ledger: the two mutants that survived here
  both survived because a LATER check happened to reject the same fixtures, not
  because the code was right. That is the class of bug a passing suite cannot
  show you and a mutant can — a guard that has never been the reason anything
  failed. Worth running a short mutation pass on any cell whose value is a
  decision procedure rather than a shape.
- [x] S2.6 Add restore-as-new-revision. Evidence: restoring never mutates or
      deletes history.
      <!-- libs/oshun/workbench-kit/src/restore.ts, published as
      `@oshun/workbench-kit/restore`, 33 cases; plus
      `tools/restore-transaction.mjs`, a twelfth Nx target running against a real
      PostgreSQL. 1,144 tests in the kit; all twelve targets green.
      "RESTORE THIS OLD VERSION" IS THE FEATURE MOST LIKELY TO BE IMPLEMENTED AS
      AN UPDATE, and that implementation is correct exactly once: after it, the
      thing it overwrote is the only record of what was there and it is gone. So
      the cell's own check is `historyHarms`, which compares the record set
      before and after and reports anything that was not a pure append — over the
      SETS rather than over the code, so it catches the update however it was
      performed, including by a migration that never went near this module.
      This cell is also where the pieces built in S2.3–S2.5 are used rather than
      restated: the precondition and its restore-specific check come from S2.3,
      the record and lineage from S2.4, the preview diff from S2.5. The only new
      vocabulary is what a restore adds — the refusals, the downstream impact,
      and the outbox. -->
  - [x] S2.6.a Authorize restore against object, source revision, current head,
        target stage, and protected-field policy. Evidence: restore
        authorization tests.
        <!-- Ten refusal kinds, every one reachable and asserted as a set.
        THE ONE THAT MATTERS IS `source-is-tombstoned`. A revision whose content
        was removed under an S2.4 governed operation cannot be restored, refused
        by IDENTITY rather than by policy so no configuration can enable it: a
        restore that reinstated erased content would undo a legal obligation
        through a feature nobody thinks of as a data path.
        `grant-missing` is a separate grant from editing, because restoring
        reinstates content the current editors chose to move away from.
        `source-is-the-head` refuses a no-op that would mint a history entry
        recording that nothing happened. A diverged history has no single head,
        so `actualHead` returns null rather than picking a branch — choosing
        which branch is current is not this function's decision.
        Authorization runs BEFORE anything about the content, so a caller with no
        grant learns nothing about which fields the object protects — asserted by
        checking that the unauthorized case returns exactly one refusal even when
        a protected field would also have been touched.
        The preview operations are a PARAMETER rather than something this
        recomputes, so the decision is made against the same preview a human was
        shown rather than against content the approver never saw. -->
  - [x] S2.6.b Preview semantic differences, dependency/gate staleness, rights
        or consent impact, and irreversible downstream effects. Evidence:
        restore preview fixtures.
        <!-- THE PREVIEW IS NOT THE DIFF. What changed is the easy half and the
        S2.5 adapters already answer it; the half that decides whether a restore
        is safe is everything downstream. The case that makes the point is a
        rights withdrawal: the CONTENT is identical to what was approved, the
        permission that made it publishable is gone, and a preview showing only
        the diff shows a green screen.
        Gate staleness keys on whether the restore changes anything SEMANTIC, not
        on whether it changes anything at all. A restore that only respells a
        unit leaves every verdict standing, and saying otherwise trains people to
        re-run gates for nothing — and a gate people re-run for nothing is a gate
        people click through.
        Irreversible effects are filtered through S2.1's `EFFECT_KINDS`: writing
        an old stage back undoes an internal write and does not unsend an email,
        so only the second kind belongs in a warning. Everything is sorted, so
        two previews of one restore read identically. -->
  - [x] S2.6.c Require expected current revision and reject changed head before
        commit. Evidence: restore race test.
        <!-- `pnpm nx restore-transaction @oshun/workbench-kit` — two REAL
        concurrent PostgreSQL transactions restoring off the same head: exactly
        one commits and one is refused as head-moved.
        A WRONG TURN WORTH RECORDING, and it is now written in the tool. The
        obvious implementation is `SELECT … FROM head WHERE revision_id =
        $expected FOR UPDATE`, and it does not work: with a DERIVED head (S2.4.f)
        there is no row the winner modifies — appending a child does not touch
        its parent — so the lock serializes the transactions and their SNAPSHOTS
        still both predate the other's insert. The first run had one commit and
        one duplicate-key ERROR rather than one clean refusal. What actually
        decides an append-only race is the UNIQUE (entity, sequence) constraint:
        the next sequence is derived from the head the caller named, so two
        restores off one head necessarily collide, and 23505 IS "the head moved".
        SERIALIZABLE would also work, at the cost of a retry loop.
        In-kit, the same precondition is refused before it reaches a database:
        S2.3's `restoreTargetsHead` catches the precondition that names the
        SOURCE — the mistake that makes a restore succeed no matter what has
        happened since. -->
  - [x] S2.6.d Create a new immutable revision whose content derives from and
        links to the selected historical revision. Evidence: restore lineage
        assertions.
        <!-- PARENT = THE HEAD, BASE = THE SOURCE, and that is exactly why S1.4.d
        has two fields. The new revision COMES FROM the head (that is where it
        sits in the history) and its content was EXPRESSED AGAINST the revision
        being restored. A model with one field has to choose: with parent =
        source the history forks and the intervening revisions fall off the main
        line; with base = head, "what is this a copy of?" is gone and the restore
        is indistinguishable from somebody retyping the old text.
        The schema version comes from the HEAD, not from the source. Content
        cannot un-migrate — S2.4's `schema-version-regression` refuses exactly
        that — so a restore across a migration restores content that has been
        brought forward, and recording the old version would make the history
        claim otherwise. Asserted by planning a restore over a migrated head.
        The planned append is fed straight back into `checkHistoryIntegrity` and
        must produce zero violations: the two cells check each other rather than
        each asserting its own shape is fine. -->
  - [x] S2.6.e Preserve intervening revisions and current history; never
        reassign old heads or delete descendants. Evidence: history integrity
        tests.
        <!-- Six harm kinds, every one reachable: a removed revision, a mutated
        one, a re-pointed parent, more than one revision for one restore, nothing
        added at all, and an appended revision that does not follow the head.
        The last two are the interesting pair. `nothing-added` is the SHAPE AN
        IN-PLACE UPDATE LEAVES BEHIND — the object changed and the history does
        not say so — and it is reported alongside `revision-mutated` for the same
        input, which is what that implementation looks like from the outside.
        `added-revision-does-not-follow-the-head` catches the restore that forks:
        it appends honestly and leaves the intervening revisions on a branch
        nothing points at, which reads as history preservation and is not.
        Checked against the DATABASE too: the restore harness loads the rows
        before and after a real committed restore and runs the kit's own
        `historyHarms` over them, so the property is asserted against what the
        engine holds rather than against what the planner returned. -->
  - [x] S2.6.f Invalidate dependent gates/projections and emit audit/outbox
        events transactionally. Evidence: restore integration tests.
        <!-- An OUTBOX rather than a publish. A restore that writes the revision
        and then publishes is two operations, and the gap between them is where a
        crash leaves a restored revision nothing downstream heard about; rows in
        the same transaction as the revision cannot be half-written.
        Proved against a real engine with an INJECTED FAILURE after the revision
        insert — a genuine constraint violation rather than a thrown error in
        application code, because what is being tested is whether the ENGINE
        unwinds. Neither the revision nor the outbox rows survive: an orphan
        revision is a restore nothing heard about, and an orphan event
        republishes a revision that does not exist. The same restore then runs
        WITHOUT the injected failure and must succeed — a rollback proves nothing
        if the happy path never writes anything either.
        One event per invalidated gate and per dependent projection, plus the
        restore event itself even when nothing downstream is affected: a restore
        that emitted nothing would be invisible to anything not watching the
        revision table. And a replayed restore under one idempotency key writes
        ONE revision, or two identical revisions land and "who restored this?"
        has two answers. -->
        <!-- evidence: libs/oshun/workbench-kit/src/restore.ts + restore.spec.ts
        (33 cases), libs/oshun/workbench-kit/tools/restore-transaction.mjs (Nx
        target `restore-transaction`, real PostgreSQL: race, atomicity with an
        injected failure, append-only via the kit's own checker, idempotent
        replay). -->
- [x] S2.7 Add domain-neutral decision records. Evidence: rationale, actor,
      options, evidence refs, and supersession invariants are tested.
      <!-- libs/oshun/workbench-kit/src/decisions.ts, published as
      `@oshun/workbench-kit/decisions`, 43 cases, 12/12 mutants killed. 1,188
      tests in the kit; all twelve Nx targets green.
      A WIKI PAGE HAS THREE PROPERTIES THAT MAKE IT USELESS WITHIN A YEAR:
      nobody can tell whether it is still in force, the alternatives that were
      rejected are gone, and the evidence it rested on is a link to something
      that has changed since. Each of the three is a field here with a rule
      attached, and 23 violation kinds — every one reachable, asserted as a set.
      THE SMOKE TARGET CAUGHT A REAL DEFECT. `DECISION_GRAPH` was a module-scope
      IIFE, and S1.2.f refused it: work at import time is exactly what
      `"sideEffects": false` tells a bundler it may drop, and a throw at import
      would take down a consumer that merely imported a type. Now a memoized
      `decisionGraph()`. Worth remembering as a class — a validated CONSTANT is
      the natural way to write this and it is a side effect. -->
  - [x] S2.7.a Define decision ID/type/scope, owner, status, effective version,
        review date, and affected capability/object references. Evidence:
        decision schema tests.
        <!-- Six types and five scopes, both closed. The temptation is an open
        string so a domain can name its own kinds; the cost is that "who must
        approve an editorial decision?" becomes unanswerable at the kit level,
        and the approval rules are the reason the record exists.
        `DecisionReview` IS THE CELL'S BEST FIELD. Two variants rather than a
        nullable date, because a null means both "never needs review" and
        "nobody set one" — and the second is how a temporary exception becomes
        permanent. A standing decision is allowed and has to say why it stands. A
        scheduled review date that was already past when the record was written
        is refused: a decision that arrives overdue is one nobody notices is
        overdue. -->
  - [x] S2.7.b Define considered option identity, description, status,
        benefits/costs, dependencies, risks, and rejection reason. Evidence:
        multi-option fixtures.
        <!-- THE OPTIONS THAT LOST ARE THE RECORD. A decision listing only what
        was chosen cannot answer the only question anybody brings to it later —
        "did they think of X?" — and "we did, and here is why not" is the entire
        value of having written it down. Exactly one chosen, every rejected or
        deferred option carrying a reason, and no chosen option carrying one (if
        it does, one of the two fields is wrong and a reader cannot tell which).
        A SINGLE-OPTION DECISION IS ALLOWED and has to say so. "We considered
        nothing else, because the vendor contract permits one shape" is a
        different claim from "we considered three and this won", and a schema
        that refused the first would just get three fabricated alternatives. -->
  - [x] S2.7.c Define immutable evidence references with version/hash and
        access-safe summary. Evidence: evidence integrity tests.
        <!-- A reference to "the latency report" is a reference to whatever that
        document says today; a version plus a digest is a reference to what its
        author actually read, which is what makes a decision re-checkable.
        The `safeSummary` is scanned with S1.10's OWN detectors, reached through
        a new `scanText` export rather than a second implementation — the whole
        argument for a shared kit, and the alternative is two sets of detectors
        one of which is always behind the other. The case that motivates it: a
        decision archive is exported, read by people who were not there, and kept
        for years, so the first connection string pasted into a summary field
        outlives everyone who could recognise it. -->
  - [x] S2.7.d Record proposer, decider, reviewers, delegated authority,
        timestamp, rationale, consequences, and residual risk. Evidence:
        actor/required-field tests.
        <!-- `delegatedAuthority` is separate from a delegated `ActorRef`
        because the two answer different questions: the ref says who was at the
        keyboard and the field says whose authority the decision carries, and a
        record with only the first cannot say whether the decision binds.
        SEPARATION OF DUTY IS A POLICY, NOT A UNIVERSAL. Architecture, rights and
        exception decisions bind other people, so the decider may not be the
        proposer; an operational decision an engineer makes and records is the
        normal case rather than a governance failure. Both directions asserted.
        Same shape for reviewers: a reviewed TYPE that left `proposed` with none
        named fails, because "the review did not happen" and "nobody recorded who
        did it" look identical afterwards. -->
  - [x] S2.7.e Define
        propose/review/approve/reject/effective/superseded/reversed transitions
        and authorization. Evidence: decision state tests.
        <!-- BUILT ON THE S2.1 ENGINE, not as a `switch`. That is reuse in both
        directions: the decision lifecycle inherits reachability, terminality,
        cycle and effect-versus-reversal checking — a `switch` would have had
        none of them — and the engine gets its first consumer that is not a test
        or an archetype.
        The checks it inherits are asserted rather than assumed: no dead ends;
        the three terminal stages have no way out; a proposed decision cannot
        jump straight to effective, skipping the review nobody did; an approval
        by somebody holding only the propose grant is unauthorized; and a
        transition whose guard was never evaluated is `indeterminate` rather than
        a pass, which is the engine's coverage-before-verdicts rule.
        Every transition declares itself irreversible with a reason, because an
        approval is a record of what somebody decided and reversing one is a new
        decision that should read as one. -->
  - [x] S2.7.f Supersede or reverse only by creating a new linked record while
        retaining original content. Evidence: lineage immutability tests.
        <!-- Exactly TWO fields may move on the original: its stage, to
        `superseded`, and its link. Everything else — the rationale, the options,
        the evidence, the people — is the record of what was believed at the
        time, and a supersession that edited it would make the archive agree with
        the present, which is the one thing an archive must not do. Six harm
        kinds, all reachable.
        The constructor and the checker are SEPARATE on purpose: a system where
        only `supersede()` can produce a supersession is a system that has not
        met a migration, so `supersessionHarms` compares two states and does not
        care how they were produced.
        `resolveInForce` walks the chain and returns a typed MISS rather than the
        last record it could reach, because both ways this goes wrong in a real
        archive produce a plausible answer otherwise: a successor that is not in
        the set (never written, or in a tenant this reader cannot see) and a loop
        (two records superseding each other, which a migration produces and
        nothing else notices). -->
- [x] S2.8 Add score-vector provenance without a default fabricated score.
      Evidence: absent scores remain absent end-to-end.
      <!-- libs/oshun/workbench-kit/src/scores.ts, published as
      `@oshun/workbench-kit/scores`, 49 cases, 11/11 mutants killed. 1,238 tests
      in the kit; all twelve Nx targets green.
      THE CHAIN THIS CELL IS ABOUT: the evaluator did not run, so the row is
      missing; the aggregate needs a number, so missing becomes zero; the UI
      needs a bar, so zero becomes a bar at the bottom; the gate needs a
      comparison, so a bar at the bottom becomes a fail. Nobody lied. Four
      reasonable local decisions produce a confident number describing nothing,
      and the suite is organised link by link along it. -->
  - [x] S2.8.a Define score dimension ID, raw value, unit, direction, scale,
        applicability, and optional uncertainty independently from verdict.
        Evidence: score dimension schema.
        <!-- `"score"` is refused as a unit, by name, along with "points",
        "value", "rating" and the empty string: each is a refusal to name one,
        and a dimension without a unit can be averaged with anything.
        THREE DIRECTIONS, NOT TWO. `target-is-best` is the one that gets
        forgotten, and forgetting it inverts every comparison on that dimension —
        loudness has a target and a track further from it is worse on either
        side. A target-direction with no target and a target on a non-target
        direction are both refused, because one of the two fields is then wrong
        and a comparison picks the wrong one.
        The `ordinal` scale exists so a rubric CANNOT be averaged later. And
        uncertainty is separate from the value and checked against it: an
        interval that excludes its own measurement means one of the three
        numbers came from a different run, and a standard deviation over n=1
        came from somewhere else entirely. -->
  - [x] S2.8.b Persist evaluator/corpus/model/provider, preprocessing, policy,
        threshold, input revision/hash, run, and timestamp versions. Evidence:
        score provenance tests.
        <!-- Sixteen fields, long on purpose: every one has been the reason two
        scores that looked comparable were not. The cross-checks are the useful
        part — a model with no provider (the same model name behind two
        providers is two models, and the swap is invisible), a corpus with no
        version (a corpus that gained rows is a different ruler), a reading whose
        `inputRevision` is not the vector's subject (a stale reading presented as
        current is the fabrication that looks most like data).
        `comparabilityFaults` turns all of it into a refusal rather than a
        warning, and the sharp one is `different-evaluator-version`: the number
        moved because the RULER moved, and a chart of the two is a chart of a
        release note. A POLICY change is deliberately NOT a comparability fault —
        it makes two verdicts incomparable, not two measurements, and that is
        S2.8.d's problem. Both directions asserted. -->
  - [x] S2.8.c Represent absent, not applicable, insufficient data, unsupported,
        unavailable, failed, stale, and redacted explicitly without numeric
        sentinels. Evidence: nonvalue state fixtures.
        <!-- Eight states, each a different action: run the evaluator, do
        nothing, get more input, retry, page the owner, re-run, ask for access.
        The expensive collapse is `not-applicable` into `absent` — the first must
        not count against a total and the second must.
        THE SENTINEL IS NOT REFUSED AT RUNTIME, IT IS UNTYPEABLE. A reading is a
        measured number or a named state and there is no third shape, so `-1` is
        not something the type can hold. What IS checked at runtime is the wire:
        `parseScoreReading` refuses a payload carrying both a `raw` and a `state`,
        which is exactly the shape a fabricating adapter emits — it filled the
        number in and left the state behind.
        `adoptSentinel` is the one honest seam. A legacy feed that writes `-1`
        exists; pretending otherwise means the `-1` reaches an average. It
        converts at the boundary, refuses to guess (the caller declares the
        mapping) and RECORDS every conversion, so "900 of 4,000 rows were
        sentinels" is a fact somebody can see rather than a silence. -->
  - [x] S2.8.d Preserve raw values when policy/threshold changes and recompute
        derived verdicts separately. Evidence: policy recomputation tests.
        <!-- `applyThresholds` returns the SAME reading object it was given —
        asserted by reference identity rather than by comparing fields, because a
        copy that is equal today can stop being equal. Three policies over one
        measurement produce pass, warn and fail, and the measurement never moves.
        Every verdict is stamped with the policy id and version it came from;
        without it, a verdict computed under v2 and one under v3 are the same
        row and a threshold change silently rewrites history.
        NO THRESHOLD IS NOT A PASS. A dimension the policy says nothing about is
        `undetermined`, because "nothing was said" is not "it was fine". -->
  - [x] S2.8.e Serialize, persist, query, display, compare, export, and publish
        an absent score without creating zero, neutral, average, or pass.
        Evidence: end-to-end absence tests.
        <!-- Every hop, asserted: a non-value round-trips through JSON as a
        non-value; it is `undetermined` at the gate for all eight states (never a
        pass, which ships, and never a fail, which blocks a release for something
        nobody measured); it exports as a null value BESIDE a state column, so a
        spreadsheet summing the values gets nothing from the row; and the whole
        chain is run end to end with a final assertion that no zero appeared
        anywhere in the serialized output.
        `aggregate` reports `n` and `excluded` by state, because a mean of 0.9
        over two measurements and eleven absences is a different fact from a mean
        of 0.9 over thirteen — and a return value carrying only the number cannot
        tell them apart. A mean of nothing is REFUSED rather than returned as
        zero, and so is a mean across units, across directions, or over an
        ordinal scale. -->
  - [x] S2.8.f Reject UI/BFF adapters that fill missing dimensions or average
        incompatible units. Evidence: adversarial adapter tests.
        <!-- `probeAdapter` RUNS the adapter rather than reading a declaration.
        "This adapter does not fill missing values" is a sentence in a review;
        finding a `0` in the output is a fact. The corpus contains all eight
        non-values deliberately, because the one an adapter mishandles is never
        the one it was written against.
        Five fabrication kinds, all reachable: inventing a number, dropping the
        row (a dimension that vanishes reads as one nobody asked for), changing a
        measured value, turning a non-value into a verdict, and sorting the
        absences among the numbers. A throw counts too — an adapter that cannot
        represent a non-value fails the same way as one that invents a number.
        THE ORDERING CHECK IS WHERE THE MUTATION PASS EARNED ITS KEEP. The first
        version asked "is a non-value between two measured rows?" and flagged an
        HONEST adapter that merely preserved input order — a check that fires on
        the correct implementation gets suppressed, and the suppression covers
        the real case too. It now asks whether the output equals what sorting by
        `value ?? 0` would produce AND differs from the input order, so an
        alphabetical table is not a finding and a default `value ?? 0` sort is. A
        mutant removing that guard survived until a "reorders for another reason"
        case was added. -->
        <!-- evidence: libs/oshun/workbench-kit/src/scores.ts + scores.spec.ts
        (49 cases, 11/11 mutants killed, 19 violation kinds and 5 fabrication
        kinds each reachable and asserted as a set). -->
- [x] S2.9 Add gate definitions with severity, owner, applicability, evidence
      version, expiry, and remediation metadata. Evidence: schema tests.
      <!-- libs/oshun/workbench-kit/src/gate-definitions.ts, published as
      `@oshun/workbench-kit/gate-definitions`, 44 cases, 13/13 mutants killed.
      1,283 tests in the kit; all twelve Nx targets green.
      A DEFINITION IS NOT A CONTRIBUTION, and the two fail differently. S1.9
      already gave a plugin a way to contribute a gate — evaluator, policy, three
      fallbacks. A bad contribution is a bug somebody fixes; a gate with no
      owner, no appeal path and a severity nobody agreed is a governance hole
      that shows up the first time it blocks a release at 2am and there is nobody
      to ask.
      So `conformsToDefinition` is the cell's reason for existing: the catalogue
      says what a stale gate does and the contribution decides what it ACTUALLY
      does, a release manager reads the first and a workflow obeys the second,
      and when they differ NOTHING FAILS — the gate simply behaves differently
      from its documentation, which is discovered during an incident review.
      27 definition violation kinds and 5 registry ones, every one reachable. -->
  - [x] S2.9.a Define stable gate ID/type/version, domain/object applicability,
        lifecycle stage, audience/target, and enabled/retired state. Evidence:
        gate identity tests.
        <!-- Applicability carries AUDIENCE as well as object kind: a rights gate
        on something going to a regulator is not the gate on an internal preview,
        and a definition that says neither has to be strict enough for the
        regulator everywhere.
        THE LIFECYCLE HAS THREE STATES, NOT TWO. `shadow` is the one most gate
        systems lack, and its absence is why new gates either block on their
        first day or never ship: a gate that runs, records and blocks nothing is
        how anybody finds out what it would have done. It carries a `since` and a
        REASON, so a shadow that has been running for a year is visible as the
        decision nobody made. `canBlock` returns false for a shadow gate whatever
        its severity says — asserted, because that is the whole point of the
        state. -->
  - [x] S2.9.b Define blocker/warning/informational severity independently from
        raw evaluator result. Evidence: severity fixtures.
        <!-- SEVERITY VARIES BY LIFECYCLE STAGE, which a single field cannot
        express. The same failing measurement is a blocker on a published asset
        and a warning on a draft; one field forces the strictest reading
        everywhere, and that is how a drafting surface acquires eleven blockers
        and people learn to work around gates. A default plus per-stage
        overrides, and an override naming a stage the gate does not run at is
        refused — an override that can never apply reads as coverage.
        Independence from the verdict is asserted in both directions: severity
        says what the platform DOES and the verdict says what the evaluator
        FOUND, and no severity name appears in the verdict vocabulary. -->
  - [x] S2.9.c Assign accountable owner, evaluator/provider owner, remediation
        owner, reviewer/override authority, and escalation. Evidence: ownership
        validation.
        <!-- Five roles because in practice they are five teams: who owns whether
        the gate should exist, who owns the code that answers it, who helps
        somebody past a failure, who may waive it, and where a disagreement goes.
        Collapsing them produces the failure message that tells an editor to
        contact the platform team.
        Two separation-of-duty rules, and both are the same shape as S2.7's: the
        override authority may not be the accountable owner (otherwise the gate
        constrains nobody who can reach that team) and neither may the escalation
        path (otherwise a disagreement about the gate escalates to the people who
        own it). -->
  - [x] S2.9.d Define required evidence types, exact version constraints,
        freshness/expiry, invalidating dependencies, and unavailable behavior.
        Evidence: evidence policy tests.
        <!-- `whenEvidenceUnavailable` HAS NO `pass` OPTION — as a type, not as a
        rule. A gate that passes when its evidence is missing passes when the
        thing it checks is broken in exactly the way that hides the evidence.
        The three are block, warn and skip-with-record, and the third still
        writes a row, so the absence is countable rather than silent.
        `invalidatedBy` is the field that stops a gate being green forever: it
        names what makes the evidence stale REGARDLESS of age, so a rights check
        passed against last month's consent record does not survive the record
        moving. Without it, freshness is a timer, and a timer only knows how long
        ago the answer was given. Zero and infinite budgets are both refused.
        A BLOCKING GATE WITH NO REQUIRED EVIDENCE is refused: "it was green" is
        not a checkable claim about a gate with no declared inputs. A gate that
        only warns is permitted one — both directions asserted. -->
  - [x] S2.9.e Define pass/warn/fail/review/unsupported policy, thresholds, and
        deterministic composition references. Evidence: policy schema tests.
        <!-- THE CELL'S WORDING NAMES THE VERDICTS THE KIT ALREADY HAS.
        pass/warn/fail/review/unsupported and S1.9's
        pass/warn/block/needs-human/indeterminate are the same five outcomes, so
        `LEDGER_OUTCOME_TO_VERDICT` publishes the mapping instead of a parallel
        enum — two words for "block" is how a client switches on one of them,
        handles four cases and falls through on the fifth. Asserted to be a
        bijection onto the shipped vocabulary.
        A fallback verdict that PASSES is refused, for the same reason the
        evidence behaviour has no pass.
        Composition here is the REFERENCE and the shape, not the aggregation
        (that is S2.10): standalone with references, a composite with none, a
        quorum with no number, and a quorum larger than its references are all
        refused, and the last is the one that reads as configured and can never
        be met. -->
  - [x] S2.9.f Define user-facing reason, affected fields/refs, remediation,
        documentation, recompute, appeal, and waiver metadata. Evidence:
        remediation metadata tests.
        <!-- A BLOCKER WITHOUT AN APPEAL IS A DEAD END, and a dead end produces
        exactly one behaviour: somebody turns the gate off. So a blocker must
        carry a remediation or a document, an appeal path, and an appeal must
        have a clock — an appeal with no time to answer in is a refusal with
        extra steps. A gate that only warns needs none of it, and that is
        asserted too, or the rule would make every informational gate carry an
        escalation nobody uses.
        The waiver may not be granted by the accountable owner: otherwise the
        gate is advisory to the team that owns it and mandatory to everybody
        else. And a waiver with no expiry is a gate that was removed by somebody
        who did not have to say so.
        `reasonKey` is a translation key, because the one sentence a blocked
        person reads is the last one that should be untranslatable. -->
        <!-- evidence: libs/oshun/workbench-kit/src/gate-definitions.ts +
        gate-definitions.spec.ts (44 cases, 13/13 mutants killed). The registry
        check finds the cross-gate faults a single definition cannot show — a
        composition cycle, a composite still depending on a retired gate, a
        retirement pointing at a replacement nobody wrote — using the same
        three-colour walk S2.4 uses, because a visited set cannot tell a cycle
        from a diamond and two gates composing a third is a diamond. -->
- [x] S2.10 Add gate-run results with deterministic aggregate policy. Evidence:
      blocker, warning, waived, unavailable, and stale combinations have known
      outcomes.
      <!-- libs/oshun/workbench-kit/src/gate-runs.ts, published as
      `@oshun/workbench-kit/gate-runs`, 108 cases, 39/39 mutants killed. 1,392
      tests in the kit; all twelve Nx targets green.
      THE CELL NAMES TEN WORDS AND THEY ARE FOUR DIFFERENT KINDS OF THING.
      blocker/warning are SEVERITIES and belong to the definition; pass/fail are
      VERDICTS and belong to the evaluator; unavailable/unsupported/stale/
      missing/error are NON-VALUES and belong to the measurement; and `waived`
      belongs to none of them, because it is a decision taken after all three.
      A tenth enum containing all ten is how a switch ends up handling `blocker`
      and `stale` in the same statement, so `ComponentState` is a union of the
      three vocabularies the kit already has (S1.9 verdicts, S2.8 non-values) and
      severity and waiver are applied to it rather than mixed into it.
      THE FOLD STARTS FROM THE APPLICABLE DEFINITIONS, NOT FROM THE ROWS THAT
      EXIST. A fold over the results in hand cannot tell "every gate passed" from
      "the gate crashed before it wrote a row", and under that fold the cheapest
      way to pass a release is for a gate to fail early enough.
      Six headlines, because a console that renders four of them as one green is
      the reason they exist: everything passed, a check did not run, somebody
      signed for a failure, and a sibling covered a failure are four different
      conversations and only the first is uneventful. -->
  - [x] S2.10.a Persist run ID, gate definition/version, target revision,
        dependency/evidence versions, evaluator/policy, actor/job, and
        timestamps. Evidence: result provenance tests.
        <!-- The trigger is a union — actor, job or schedule — because "no actor
        and no job" is the state a row lands in when the writer forgot, and it
        reads in a table exactly like a system run.
        FRESHNESS IS COMPUTED, NOT STAMPED. A stored `stale: false` is true
        exactly once and stays in the column afterwards, so `evidenceFreshness`
        derives it at read time from the recorded timestamps, the definition's
        budget, and the CURRENT dependency versions. That last is what makes
        S2.9's `invalidatedBy` operative: an `EvidenceRecord` carries the
        versions it was computed over, so a rights check answered four minutes
        ago against a consent record that has since moved comes back stale —
        which freshness-by-age cannot see, because the answer really was recent.
        `evidence-recorded-after-the-run-finished` is the quiet one: evidence
        stamped later than `finishedAt` is not what the gate read, it is what
        somebody attached afterwards. -->
  - [x] S2.10.b Persist raw measurements and nonvalue states separately from
        policy verdict and aggregate release eligibility. Evidence: separation
        tests.
        <!-- Three fields, and the cell's wording ("raw measurements and nonvalue
        states") IS S2.8's `ScoreReading`, so the observation reuses it rather
        than restating an absence vocabulary. `GateObservation` is
        evaluated-with-readings or not-evaluated-with-a-named-state; the verdict
        is S1.9's, derived under the policy named in the provenance; the effect
        is what the platform does about it.
        `passed-without-evaluating` is the rule this cell exists for, and it
        covers `warn` as well as `pass` because warn is in the kit's PASSING set
        — a row that says "did not run" and "warn" ships exactly as readily. It
        is written by ordinary code: a catch that defaults the verdict, a
        provider timeout falling through to the happy path, a migration that
        backfilled a column. -->
  - [x] S2.10.c Define precedence and aggregation for blocker, warning, pass,
        fail, unavailable, unsupported, stale, missing, waived, and error.
        Evidence: exhaustive truth table.
        <!-- 336 combinations generated rather than a chosen few (14 states x 2
        blockable x 3 evidence behaviours x 2 gap policies x 2 ledger states),
        with the count asserted so that adding a state without revisiting the
        suite fails there rather than silently halving coverage.
        A WAIVER'S ONLY POWER IS TO TURN A BLOCK INTO A WAIVED BLOCK, and that is
        checked by generating the table TWICE — with and without a waiver — and
        diffing. The diff must be exactly `blocks -> waived`; a waiver that could
        promote a gap or satisfy anything appears as a cell that moved and should
        not have. Reading the function could not establish that.
        Nothing is `satisfied` except an actual pass. An ambiguous ledger (two
        results for one gate) is fail-closed INDEPENDENTLY of the evidence
        policy, because it is not a missing input — it is a question about which
        row is the answer, and choosing by iteration order is the order
        dependence S2.10.d forbids.
        A deliberate divergence from S2.8: `unsupported` is not-counted for a
        score so it cannot drag an average, and for a gate it means "the check
        did not happen" — treating that as not-applicable is how a new asset kind
        ships unchecked. -->
  - [x] S2.10.d Define all-of, any-of, quorum, conditional, target-specific, and
        domain plugin composition without order dependence. Evidence:
        aggregation strategy tests.
        <!-- Kleene three-valued, and the third value is load-bearing: `any-of`
        over a failure and a check that could not run is neither satisfied nor
        refuted, because the one that did not run might have passed. A quorum is
        refuted only when `trues + unknowns < need` — a two-valued quorum reports
        "not enough approvals" for a panel where one reviewer merely has not
        finished, and the remediation for those two is not the same.
        `conditional` and `target-specific` dispatch on RECORDED facts rather
        than closures: a predicate given as a function cannot be stored, replayed
        or compared between two policy versions, so a strategy built from
        closures is unrecomputable by construction.
        A DOMAIN FOLD IS RUN TWICE — over the members and over the same members
        reversed — and disbelieved if it disagrees with itself, which catches the
        actual bug (a strategy that reads `members[0]`) more cheaply than a code
        review. The members are FROZEN rather than compared afterwards: taking
        the capability away is stronger than checking whether it was used, and
        the mutation pass found that freezing only one direction lets a strategy
        that writes on one particular ordering through.
        An advisory gate can never satisfy a strategy: otherwise `any-of` over a
        blocker and an informational gate ships whenever the informational one is
        green. -->
  - [x] S2.10.e Preserve every component result and reason; never hide a blocker
        behind an average or passing sibling. Evidence: component visibility
        tests.
        <!-- THE STRATEGY DECIDES SATISFACTION AND MAY NOT DECIDE BLOCKING, which
        makes this structural rather than a rule somebody has to remember. A
        component an `any-of` tolerated keeps its `blocks` effect, stays in
        `components`, appears in `tolerated`, and forces the headline to
        `eligible-by-composition` — a different sentence from `eligible`. And
        there is no average to hide behind: the aggregate carries counts and a
        headline, never a pass rate.
        `visibilityFaults` is the other half, the same shape as S2.8.f's adapter
        probe and for the same reason — the fold can be correct and the thing a
        person reads can still be wrong, and no test of the fold sees that.
        `waived-shown-as-passing` is the one that matters most: rendering a
        signed exception in the same green as a pass is precisely how waivers
        become routine, and it is the most common way a gate stops meaning
        anything without anybody deciding to remove it. A headline may be more
        cautious than the fold and never less. -->
  - [x] S2.10.f Recompute identical input deterministically and version
        aggregate policy changes. Evidence: property and version-change tests.
        <!-- Order independence is asserted over every permutation of the
        results, of the definitions, and of the waivers, by comparing serialised
        aggregates — not by reading the folds. Instants are the kit's UTC-with-Z
        text, so string order is time order and no date arithmetic enters the
        comparison.
        A RELEASE POLICY CAN ONLY EVER TIGHTEN. `gapsBlockFor` lets a deployment
        refuse a gate's own record-and-continue for one audience; waivers are
        NARROWED per audience and never granted, so a gate whose definition
        carries no waiver policy is not made waivable by any deployment — that is
        a recorded fault. Waivers for the regulator audience are refused
        outright, the same shape as S2.9's refusal to let the accountable owner
        grant one: a waiver is an internal decision and that audience is the one
        place an internal decision cannot stand in for the check.
        `explainPolicyChange` ships a fold change with the list of decisions it
        reverses, because `loosened` is the half that needs a human, and
        `changedWithoutAVersionBump` is the unambiguous defect: every aggregate
        already recorded carries the old version and would now recompute
        differently under it, so the history of what was approved is rewritten
        with nothing saying it moved. -->
        <!-- evidence: libs/oshun/workbench-kit/src/gate-runs.ts +
        gate-runs.spec.ts (108 cases, 39/39 mutants killed; 12 run violation
        kinds, 12 aggregate fault kinds, 8 policy violation kinds and 7
        visibility fault kinds each reachable and asserted as a set). The
        mutation pass earned its keep three times: it found a dead `any-of`
        empty-list branch, an unreachable post-hoc mutation check the freeze had
        already made impossible, and two guards (a blocker returning `warn`, an
        evidence policy of `warn`) that no test covered. -->
        <!-- Not in scope here, on purpose: the waiver REQUEST, approval,
        renewal and revocation history are S2.11, so this cell defines the
        minimum `WaiverRecord` the fold needs and no more, rather than two cells
        each defining a waiver. -->
- [x] S2.11 Add authorized, time-bounded gate waivers. Evidence: expiry
      automatically makes the release ineligible.
      <!-- libs/oshun/workbench-kit/src/gate-waivers.ts, published as
      `@oshun/workbench-kit/gate-waivers`, 61 cases, 54/54 mutants killed. 1,454
      tests in the kit; all twelve Nx targets green.
      THE HEADLINE IS PROVED THROUGH A REAL FOLD, not asserted about a status
      field: `toWaiverRecord` returns null for anything but an active standing,
      so the S2.10 aggregate goes from `eligible-with-waivers` to `blocked`
      across the expiry instant with nothing else moving, no job having run and
      no row updated. The suite drives four instants either side of it.
      A REQUEST IS NOT A GRANT. One row with a status column makes "who asked"
      and "who agreed" the same record and leaves the request editable after
      approval, so the grant restates the request's four identifying fields and
      carries a digest of what was approved — checked against a recomputation
      the CALLER supplies, because a kit contract entry point may not reach a
      Node builtin and comparing a stored digest with itself passes by
      construction.
      That test conflating two clocks was the one real bug this cell surfaced:
      crossing the waiver's expiry also crossed the evidence's freshness budget,
      and the release came back `undetermined` for a reason that had nothing to
      do with waivers. Both readings are now pinned separately. -->
  - [x] S2.11.a Define waiver request for exact gate result, target revision,
        audience/channel/environment, reason, evidence, and risk. Evidence:
        waiver schema.
        <!-- The request names a RUN, never a gate: a waiver against a gate
        rather than a result is an exemption, and it will outlive every reason it
        was granted for.
        Channel and environment are separate fields from audience because they
        fail differently — an exception agreed for staging that silently covers
        production is a deployment mistake, and one agreed for a newsletter that
        covers the wire is an editorial one. A scope naming neither is refused,
        because it then covers whatever the reader assumes.
        Risk is ordinal so the interesting comparison can be made: an approval
        that accepted LESS than the request declared, with no reassessment
        recorded, means the approver agreed to a smaller thing than the one
        described and the record shows only the smaller one. A severe residual
        risk needs a second signature, and that signature may not be the
        approver's own. -->
  - [x] S2.11.b Enforce gate-specific waiverability and actor/role/tenant/object
        scope plus separation of duties. Evidence: authorization negative tests.
        <!-- Four independent narrowings a waiver has to survive, and the gate's
        own definition is the first: a gate whose `presentation.waiver` is null
        cannot be made waivable by any deployment, any policy, or any approver.
        Three separations of duty, all negative-tested: the approver may not be
        the requester (a waiver anybody can grant themselves is not a control, it
        is a field), may not be the gate's accountable owner (S2.9's rule,
        re-checked at USE time because ownership changes after a waiver is
        signed), and may not hold a role the gate never delegated to.
        Scope is checked as seven independent misses — tenant, object, audience,
        channel, environment, revision, run — each asserted on its own, and
        scope is checked BEFORE binding because "this is not your story" and
        "the evaluator moved" are different conversations. -->
  - [x] S2.11.c Require start, expiry or one-use, review date, revocation, and
        renewal as a new decision. Evidence: clock-controlled lifecycle tests.
        <!-- ONE-USE IS NOT A SHORT EXPIRY. "An exception for this one publish"
        and "an exception for everything that shipped in that hour" are different
        decisions, and modelling the first as a one-hour window is how it becomes
        the second. A one-use grant records what spent it and stops authorising
        every other release, inside a window that still bounds it — an unused
        one-use waiver from last year is not live either.
        A backdated start is refused: a waiver authorising a period before
        anybody agreed to it is how a release that already shipped acquires
        cover. A review date after expiry is refused: a review scheduled for
        after the thing has lapsed is one nobody does, and it is what makes a
        renewal automatic. A zero-length window is refused alongside an inverted
        one — the mutation pass found `<=` untested at the equal instant, which
        is the nastier half, since it reads in a table as a waiver with dates.
        RENEWAL IS A NEW DECISION — there is no renew(), only a grant that
        supersedes, in the same shape S2.7 uses. `renewalChain` walks back to the
        origin and ADDS THE INSTALMENTS UP, which is the finding the function
        exists for: eight consecutive forty-eight-hour waivers are a two-week
        exemption no single approval authorised, every link reads as within the
        gate's limit, and nothing sees it unless something sums them. It also
        catches a renewal that widened the scope (a new exception wearing a
        renewal's name) and one that shaved the accepted risk down a level at a
        time until a severe exception is on the books as a low one. -->
  - [x] S2.11.d Bind the waiver to gate/input/evaluator/policy/dependency
        versions and invalidate it on any out-of-scope change. Evidence: binding
        invalidation tests.
        <!-- A GATE VERSION BUMP CAN NEVER BE TOLERATED, as a type rather than a
        rule: `REBINDABLE_DIMENSIONS` is input/evaluator/policy/dependency and
        has no member an approver could write for the gate version, because S2.9
        bumps that when the gate's MEANING changes and an approval of the old
        meaning is not one of the new. Asserted by granting a waiver that
        tolerates every dimension there is and watching it still come unbound.
        The tolerance list is the approver's judgement and the default is empty,
        which is the strictest reading. It earns its existence in the other
        direction too: a waiver that survives nothing at all gets re-requested
        constantly, and people learn to ask for a week instead of an hour.
        A dependency that stops being REPORTED is unbound rather than tolerated —
        there is nothing left to compare against, so the check cannot be shown to
        still hold. -->
  - [x] S2.11.e Include active waiver scope, reason, approver, expiry, and
        residual risk in review/publish/release surfaces. Evidence: visibility
        tests.
        <!-- The five fields are five different objections, and the check names
        each: without the scope you cannot tell whether it covers what you are
        looking at, without the approver there is nobody to ask, without the
        expiry you cannot tell a decision from a habit, without the reason there
        is nothing to disagree with, and without the residual risk the release
        reads as fine.
        Two faults run the other way and both matter more than an omission. A
        rendered expiry that is not the grant's own is worse than none, because
        it is believed. And a revoked or expired waiver drawn as live tells a
        release manager they are covered when they are not — the same shape as
        S2.10.e's `waived-shown-as-passing`, one layer up.
        The five fields are demanded only of a waiver that AUTHORISES something;
        a lapsed one shown as lapsed needs none of them, and asserted, or the
        rule would make every historical exception carry a full disclosure. -->
  - [x] S2.11.f Make expired, revoked, stale, or out-of-scope waivers
        nonauthorizing without deleting their audit history. Evidence:
        release-eligibility tests.
        <!-- Eight standings, EXACTLY ONE OF WHICH AUTHORISES — asserted as
        `authorizes === (status === 'active')` over the whole vocabulary, with
        every status reached and exactly one authorizing standing in the set.
        The precedence is deliberate and is the answer to "which reason wins":
        revoked, then superseded, then the clock, then scope, then binding.
        Reporting "expired" for a waiver somebody revoked would put the wrong
        sentence in front of the person asking why the release stopped.
        NOTHING IS DELETED. The approver, the approval date, the expiry, the
        accepted risk and the scope sit OUTSIDE the status, so a revoked waiver
        stays fully readable — a revoked waiver that vanishes takes with it the
        fact that somebody once thought this was acceptable, which is the fact an
        incident review is looking for. -->
        <!-- evidence: libs/oshun/workbench-kit/src/gate-waivers.ts +
        gate-waivers.spec.ts (61 cases, 54/54 mutants killed; 22 grant violation
        kinds, 8 statuses, 6 renewal finding kinds and 9 disclosure fault kinds
        each reachable and asserted as a set). The mutation pass found two real
        gaps: a zero-length window at the `<=` boundary, and three of the four
        grant/request identity fields never varied on their own — a check that
        fired on one of them would have passed the suite and let the other three
        through. -->
      <!-- Deliberately still S2.10's: the fold itself. This cell decides
      authorization and hands the aggregate a record or nothing; it does not
      re-implement precedence, and the two agree because the record carries the
      grant's own window rather than a copy that can drift. -->

- [x] S2.12 Add dependency-triggered staleness propagation. Evidence: a source,
      model, consent, canon, or asset update marks every dependent gate stale.
      <!-- libs/oshun/workbench-kit/src/staleness.ts, published as
      `@oshun/workbench-kit/staleness`, 59 cases, 48/48 mutants killed, plus a
      new Postgres target `outbox-atomicity`. 1,514 tests in the kit; all
      THIRTEEN Nx targets green.
      S2.9 declared what makes evidence stale and S2.10 computed it for ONE
      result against one set of current versions. Neither can answer the question
      this cell exists for: a consent record moved — WHAT ELSE is now wrong. That
      is a graph walk, and it is the difference between a gate that goes red when
      somebody looks at it and a platform that says so.
      The headline is driven end to end: one withdrawal, through a three-deep
      graph, produces marks on the rights, canon and likeness gates — and every
      one of them then REFUSES to be recomputed automatically, because the thing
      that moved was a withdrawal. -->
  - [x] S2.12.a Define dependency node/edge and invalidating mutation types for
        sources, models, consent, canon, assets, policies, evaluators, and
        target configurations. Evidence: invalidation registry.
        <!-- NOT EVERY MUTATION INVALIDATES, AND THE REGISTRY HAS TO SAY WHY
        NOT. A source whose title was corrected does not invalidate a rights
        check; one whose licence changed does. A registry where everything
        invalidates makes the recompute queue the whole corpus every night, and
        the response to that has never once been to tune the registry — it is to
        turn propagation off. So `why` is required on BOTH answers and the
        negative one is the more important: an unexplained `false` is a judgement
        somebody made to stop the queue filling up, and there is no way to tell
        it from a real one afterwards.
        Two structural rules pointing opposite ways: a kind where NOTHING
        invalidates will never propagate and reads exactly like a covered kind,
        and a kind where EVERYTHING invalidates is the registry nobody kept. Both
        refused, both asserted.
        27 shipped entries across the eight kinds, and `model/alias-repointed` is
        the one that justifies the subsystem: a provider moves a stable name onto
        new weights, nothing here changed, no revision moved, no editor did
        anything, and every answer that name gave is now an answer to a different
        question. Nothing inside a single result can see it.
        An UNREGISTERED mutation is treated as invalidating, and says so — a
        mutation nobody classified propagating nothing is the exact failure this
        cell exists to prevent, and a noisy queue is the cheaper mistake. -->
  - [x] S2.12.b Emit versioned dependency-change events transactionally with
        authoritative mutations. Evidence: outbox atomicity tests.
        <!-- "The mutation and its event commit together or neither commits" is a
        property of a TRANSACTION, and a test with a fake store proves the fake.
        So `tools/outbox-atomicity.mjs` builds both tables and drives a real
        PostgreSQL, and THE NEGATIVE CONTROL IS THE POINT: the same failure is
        injected twice, once inside one transaction (both tables must come back
        unchanged) and once with the two writes committed separately (which MUST
        leave an authoritative row with no event beside it). If the control does
        not break, the harness cannot tell the two designs apart and every green
        it has reported is worthless. Calibrated by splitting the atomic write
        into two transactions and confirming the run goes red.
        The kit half is the one no engine can hold: `UNIQUE (node, sequence)`
        refuses two events claiming position 3 and cheerfully accepts 1, 2, 4 —
        and 1, 2, 4 is exactly the shape a lost event leaves. `validateEventStream`
        finds the gap, the broken version chain and the backwards version in a
        stream the constraints accepted.
        The harness caught its own fixture: the first draft's backwards-version
        row was 100 -> 7, which the `from_precedes_to` CHECK rejects — a
        row-level fault, and the wrong kind to calibrate a stream checker
        against. -->
  - [x] S2.12.c Traverse direct and transitive dependents with bounded fan-out,
        cycles, depth, pagination, and version checks. Evidence: graph traversal
        tests.
        <!-- A TRUNCATED WALK IS NOT A COMPLETED ONE, as a type. Fan-out caps,
        depth limits and pages are all necessary and are also how a propagation
        silently leaves half its dependents fresh, so `PropagationOutcome` is a
        union whose incomplete arm carries the truncation notes and a resume
        cursor and cannot be read for `affected` without the caller seeing that
        it stopped.
        Cycles are REPORTED AND SURVIVED rather than refused: a canon graph
        genuinely has mutually-referential entities, and a traversal that threw
        on one would make the whole propagation depend on the cleanliness of
        somebody else's data. Breadth-first, so `depth` means what it says and a
        limit cuts the far edge rather than one arbitrary branch.
        The version check is what makes a replayed event cheap instead of a
        second sweep: a result already at or past the new version is not
        affected.
        The mutation pass found TWO GUARDS MASKING EACH OTHER — dropping the
        visited set still produced the right `affected`, because the result dedup
        caught the duplicate on the way out. Separated by counting expansions (a
        wide diamond re-expanded per path is how this becomes exponential) and by
        a result reachable from two genuinely distinct nodes, which the visited
        set cannot deduplicate. -->
  - [x] S2.12.d Mark affected results stale with old/new dependency version,
        event, reason, and time before downstream authorization. Evidence:
        stale-state tests.
        <!-- Every field the cell names, plus one it does not: the PATH. "Your
        rights check is stale" is unactionable; "your rights check is stale
        because the consent record under the canon entity it cites was withdrawn"
        is a task, and the path is the only place that sentence can come from.
        Omitted for a direct dependency, where it would be noise.
        "Before downstream authorization" is made checkable rather than asserted:
        `unappliedMarks` compares two timestamps that already exist — a mark
        stamped BEFORE an S2.10 aggregate ran, against a component that aggregate
        did not treat as stale. That combination is a release decided over a
        staleness somebody had already detected.
        A mark stamped AFTER the aggregate is reported as its own kind and is not
        a fault: it is the world moving on, and treating it as a violation would
        make every propagation indict every earlier release. -->
  - [x] S2.12.e Queue idempotent recomputation or require explicit action
        according to policy/cost/consent/provider constraints. Evidence:
        recomputation routing tests.
        <!-- WITHDRAWN CONSENT IS NEVER AUTO-RECOMPUTED, and it is checked FIRST.
        Re-running a model over material whose consent was withdrawn, because an
        automated staleness sweep noticed the evidence was old, is the worst
        automatic action this subsystem could take — and any ordering that lets a
        budget or provider check reach it first is one where a configuration
        change makes it possible. Asserted with every other constraint
        simultaneously hostile.
        Three explicit-action routes rather than a queue: a human-answered gate
        (a queue that re-ran it would be re-recording a judgement nobody made
        twice), a provider that is down (a job in front of a dependency that is
        down retries until somebody notices the queue rather than the outage),
        and a cost past budget (a sweep that overspends gets switched off
        entirely rather than tuned).
        THE IDEMPOTENCY KEY MAY NOT CONTAIN THE EVENT OR THE CLOCK. It names what
        the job would DO — target, dependency, version being caught up to,
        evaluator — so a replayed event collapses onto the job already queued. A
        key carrying the event id makes every replay a new job, which is the
        incident that makes people delete the outbox. Asserted in both
        directions: a replay matches, and a version or evaluator move does
        not. -->
  - [x] S2.12.f Reconcile missed events and partial propagation against stored
        dependency versions. Evidence: fault-injection repair tests.
        <!-- RECONCILIATION COMPARES STORED VERSIONS, NOT EVENT LOGS. An event
        that was never emitted leaves no trace in the log that would have carried
        it, so a repair pass built on the log finds a consistent log and reports
        nothing. The only durable evidence is a result whose observed version is
        behind its source and which carries no mark — a comparison between two
        pieces of state, neither of which is the thing that failed.
        `partial-propagation` is the half that says the walk RAN: some results
        under a node caught the change and others did not, which is a walk
        truncated, timed out or crashed halfway, and it looks nothing like one
        that never started. Only the mixture distinguishes them, and a fully
        missed node is asserted NOT to be reported as partial.
        Two findings about wrong state rather than missing propagation:
        `observed-version-ahead-of-the-source` (the observation was written from
        somewhere that is not the source of truth) and
        `mark-for-a-version-that-never-arrived` (an event emitted and rolled
        back, whose consequence outlived it). The second needed a
        `markedToVersion` field to be reachable at all — it was declared and
        unreachable in the first draft, which is the shape this ledger refuses,
        so it was made real rather than deleted.
        `repairPlan` re-emits only for the two findings that are propagation to
        redo; the other three are state to fix. -->
        <!-- evidence: libs/oshun/workbench-kit/src/staleness.ts +
        staleness.spec.ts (59 cases, 48/48 mutants killed; 5 registry violation
        kinds, 8 event violation kinds, 3 application fault kinds and 5
        reconciliation finding kinds each reachable and asserted as a set), plus
        libs/oshun/workbench-kit/tools/outbox-atomicity.mjs driving a real
        PostgreSQL with a negative control that must come back broken. -->

- [x] S2.13 Add a human-signoff gate type that automation cannot self-complete.
      Evidence: system actor attempts are rejected.
      <!-- libs/oshun/workbench-kit/src/signoff.ts, published as
      `@oshun/workbench-kit/signoff`, 55 cases, 55/55 mutants killed. 1,570 tests
      in the kit; all thirteen Nx targets green.
      Every other gate in the kit is a MEASUREMENT — something runs, something is
      compared against a threshold, a verdict comes out. This one is a person
      taking responsibility, and the whole cell is one word of its title:
      SELF-COMPLETE. Automation may schedule it, remind about it, escalate it,
      show it and record it. It may not produce one.
      The failure guarded against is almost never an attacker. It is an agent
      holding a service token that happens to carry the `editor-in-chief` claim,
      a seeded environment where every reviewer is a fixture, a support
      engineer's impersonation session left open in another tab — and each of
      those satisfies every role check ever written. -->
  - [x] S2.13.a Define required human role, named-person or eligible-pool scope,
        quorum, sequence, independence, and expiry. Evidence: human gate schema.
        <!-- A QUORUM WITHOUT INDEPENDENCE IS ONE SIGNATURE WITH A COUNT, and the
        count is what a release console renders. So a quorum above one that
        declares no independence is refused, and the dedup by signer is
        UNCONDITIONAL — the mutation pass found that a gate which happens not to
        declare previous-signer independence could otherwise reach a quorum of
        two with one person.
        Independence is DECLARED, not inferred. `same-team` is reported as
        undecidable rather than silently passed: the kit has no org chart, and a
        membership check invented from an id prefix is wrong in exactly the cases
        it matters.
        A REJECTION STANDS. It is not outvoted by later approvals and cannot be
        cleared by collecting more — a quorum reachable by asking until somebody
        says yes has no lower bound, and it reads in an audit as "three of four
        approved".
        `sequential` exists because some reviews are only meaningful in an order:
        legal reading a piece editorial has not finished is legal reading a
        draft. An order over a quorum of one is refused, because it reads as a
        stronger control and is the same one. -->
  - [x] S2.13.b Require authenticated human presence and step-up authentication
        where risk policy demands. Evidence: identity/step-up tests.
        <!-- A STEP-UP REUSED FROM LOGIN IS NOT A STEP-UP. It has to have
        happened AFTER the review opened and inside a fifteen-minute window, or
        it is a property of the session rather than of the judgement — which is
        exactly what makes a stolen session enough. Both bounds asserted,
        including the edge of the window in each direction.
        `WEAK_STEP_UP_METHODS` names password and SMS, and a gate whose only
        accepted methods are weak is refused: re-entering the session credential
        proves the session is still the session, which is precisely what somebody
        holding it can also prove. -->
  - [x] S2.13.c Reject service, system, agent, model, fixture, unauthenticated,
        and impersonated actors even when they possess a generic role claim.
        Evidence: actor-type negative tests.
        <!-- THE ROLE CLAIM IS NOT THE CONTROL — THE ACTOR KIND IS, AND IT IS
        CHECKED FIRST. The suite's central case is a service token holding
        `editor-in-chief`: it satisfies every role check ever written and is
        refused before the role is read. Where it can be a type it is one — a
        completed record carries a `HumanPresence`, and only `admitSigner`
        produces one, so a service actor has no path to a valid signoff at all.
        The cell's list maps onto S1.4's actor model plus S1.13.d's mark:
        service and system are `not-a-human-actor`, an impersonation is
        `delegated` (a real person, at a real keyboard, whose signature would
        land the responsibility on somebody who never saw the thing), and a
        FIXTURE is caught by BOTH halves of the synthetic mark — the symbol the
        builders stamp, which does not survive JSON, and the reserved id prefix,
        which survives JSON and a database column. A check on one of them passes
        for exactly the fixtures the other catches, and both are asserted.
        The strongest evidence here is self-referential: no actor this
        repository's own test builders can produce is able to complete a human
        gate. -->
  - [x] S2.13.d Record reviewed target/evidence versions, decision, comment,
        timestamp, authority, conflict/recusal, and signature or equivalent.
        Evidence: signoff record tests.
        <!-- The VERSIONS are the point. "They approved it" is unfalsifiable a
        month later; "they approved revision 7 against consent record v3 and
        rights record v2" can be checked against what those things are now, which
        is what makes S2.13.e possible at all — so a record with no evidence
        versions is refused, on the grounds that nothing could ever make it
        stale.
        Conflict and recusal are two fields rather than a boolean, because
        "declared a conflict and recused" and "declared a conflict and signed
        anyway" are the two outcomes and only one is a problem. Signing over an
        unrecused conflict is refused — a conflict declaration is only worth
        collecting if it is, otherwise it is a field somebody fills in and
        nothing reads — and so is a recusal that still produced a decision.
        `role-does-not-match-what-was-held` catches the recorded authority
        drifting from the authority that was actually checked. -->
  - [x] S2.13.e Invalidate signoff on expiry or relevant target/evidence/policy
        change. Evidence: staleness tests.
        <!-- A signoff is EVIDENCE, and it goes stale in exactly the way S2.12
        makes everything else go stale: a person approved specific versions of
        specific things, and any of them moving means they approved something
        else. Seven standings, exactly one of which stands, with the signer, the
        time and the decision held OUTSIDE the status so a lapsed signoff stays
        fully readable.
        `scope-widened` is the one a version comparison misses entirely. Nothing
        about the content changed; the audience did. An approval given for an
        internal preview that now covers a regulator submission is the same bytes
        reviewed for a different question. Narrowing is deliberately NOT
        invalidating — the person already approved the wider case, and
        invalidating on it would mean every audience restriction costs a new
        review.
        A dropped evidence entry is distinguished from a moved one, because the
        remediations differ: re-review against the new version, versus find out
        why the thing the reviewer looked at is gone. -->
  - [x] S2.13.f Require a new human action for renewal, reversal, or materially
        changed scope. Evidence: no-automation lifecycle tests.
        <!-- There is NO ARM of `planRenewal` that produces a valid signoff, and
        that is asserted over all three intents. There is no `renew()` and no
        `extend()` — an extend is how a person who reviewed something once is
        recorded as having reviewed it every quarter since — and a reversal is a
        person deciding the other way, so a system that can flip a decision
        without one has a decision field rather than a signoff.
        "Materially changed scope" is COMPUTED by comparing what was reviewed
        against what is now proposed, never taken from a flag, because the flag is
        the thing an automated pipeline learns to leave false. The type-level half
        holds the rest: the only thing a renewal could produce is a
        `HumanPresence`, and nothing but `admitSigner` can build one. -->
        <!-- evidence: libs/oshun/workbench-kit/src/signoff.ts + signoff.spec.ts
        (55 cases, 55/55 mutants killed; 12 refusal reasons, 10 requirement
        violation kinds, 9 record violation kinds, 7 quorum fault kinds and 7
        standings each reachable and asserted as a set). The mutation pass found
        two guards masked by later checks: the unconditional signer dedup, and a
        dropped evidence entry that fell through to the moved-version branch and
        produced the same standing with the wrong sentence. -->
        <!-- Not done here: wiring the human gate into S2.10's fold as a gate
        CONTRIBUTION. That is a plugin registration rather than a contract, and
        S2.10 already routes a `requiresHuman` recomputation to a person instead
        of a queue; a domain wires the two together when it ships a real one. -->

- [x] S2.14 Add stage/gate audit projections. Evidence: the projection can be
      rebuilt from events and matches stored read models.
      <!-- libs/oshun/workbench-kit/src/audit.ts, published as
      `@oshun/workbench-kit/audit`, 46 cases, 49/49 mutants killed. 1,617 tests
      in the kit; all thirteen Nx targets green. THIS CLOSES PHASE S2.
      A PROJECTION IS A CACHE. That sentence is either true or it is a lie
      somebody tells about a second source of truth, and the difference is
      entirely whether the thing can be thrown away and rebuilt — so the cell's
      headline evidence is not one test among several here, it is the property
      the rest of the module exists to make checkable. -->
  - [x] S2.14.a Define immutable event types for transition request/refusal/
        completion/bypass and gate definition/run/stale/waive/recompute/signoff
        lifecycle. Evidence: audit event schemas.
        <!-- THE VOCABULARY IS DERIVED FROM THE CELLS, NOT INVENTED. Fifteen
        kinds, each naming a decision S2.1–S2.13 already makes, and
        `uncoveredSurfaces` checks the mapping in the direction that matters: an
        unused event kind is dead weight, and a decision the platform MAKES and
        does not record is a thing that happened and cannot be shown to have
        happened. `transition-bypassed` is the kind people leave out and the
        first one an incident review looks for. -->
  - [x] S2.14.b Include target/revision, prior/new state, actor/delegate,
        reason, evidence/policy versions, correlation/trace, and safe error
        refs. Evidence: event completeness tests.
        <!-- Actor and principal are two fields, because a row recording one of
        them answers neither "who did this" nor "whose authority was used" —
        the two questions an incident review asks, in that order — and both
        directions are refused (a delegated actor with no principal, and a
        principal on an actor that is not delegated).
        The error ref is typed as a REFERENCE and prose in it is refused: the
        field exists so a failure can be joined to an incident, and text is how a
        stack frame ends up in an export.
        `detail-carries-a-leak` BORROWS RATHER THAN REINVENTS — S1.10 already
        argued about what a credential, an email address or a cross-tenant id
        looks like in free text, an audit detail is free text crossing the same
        boundary, and a second set of detectors is one that is always behind the
        first. -->
  - [x] S2.14.c Build per-object timeline, current stage, available/refused
        transitions, gate status/freshness, waiver, and signoff projections.
        Evidence: projection reducer tests.
        <!-- One reducer, total over the kind union with no default arm, so
        adding an event kind is a compile error rather than a fact the projection
        quietly drops. Refused transitions keep their reason, bypasses are
        COUNTED separately (of every number in the projection that is the one
        worth reconciling first), gate rows carry staleness and waiver together
        because a release console reads them together, and a revoked or expired
        waiver clears the row rather than leaving it green. -->
  - [x] S2.14.d Apply deterministic ordering, idempotency, versioning,
        out-of-order handling, poison quarantine, and replay. Evidence:
        event-delivery tests.
        <!-- ORDER COMES FROM THE SEQUENCE, NEVER FROM THE TIMESTAMP. Two
        writers a hundred milliseconds apart on skewed clocks produce a trail
        that reorders itself, and BOTH ORDERS READ AS PLAUSIBLE — asserted with a
        stream whose timestamps are deliberately out of order and whose fold is
        not.
        Out-of-order delivery is HELD, not applied: folding event 5 before 4
        produces a state that never existed and produces it silently. Duplicates
        by event id are dropped (that IS idempotency here); duplicates by
        POSITION are quarantined, because two events in one place is a story with
        a hole whichever is chosen.
        A REAL BUG THIS CELL SURFACED: the stall only fired when a LATER event
        revealed the gap, so a stream whose trailing events were all quarantined
        came back COMPLETE. Fixed by making quarantine imply incomplete
        independently of position, with redelivery the one exception since
        dropping a duplicate removes nothing from the story. Both directions are
        now pinned. -->
  - [x] S2.14.e Rebuild projections from an empty store and compare to
        transactional read models and authoritative state. Evidence: full-replay
        parity test.
        <!-- FIELD BY FIELD RATHER THAN BY A DIGEST, deliberately. A checksum
        answers "do they differ", which is the question you already suspected the
        answer to; the question worth asking is WHICH FIELD, because that is what
        distinguishes a drifted reducer from a lost event from a stored copy
        something wrote to directly.
        `completeness-disagrees` is the sharpest of the nine: a stored projection
        that believes it is whole while a replay stalls is one that stepped over
        something. -->
  - [x] S2.14.f Apply tenant/object/property authorization and redaction to
        audit queries and exports. Evidence: disclosure negative tests.
        <!-- REDACTION IS APPLIED TO THE PROJECTION, NOT TO THE QUERY — a filter
        in a query is one the next endpoint forgets, and every audit export is a
        next endpoint. A cross-tenant viewer gets `visible: false` rather than an
        empty projection, because "there is an object here and you may see none
        of it" is itself the disclosure, and returning an empty result feels like
        a refusal already, which is why people ship it.
        Three capabilities, each removing something different, and
        `disclosureFaults` is the adversarial half in the shape S2.8.f and
        S2.10.e use: the narrowing can be correct and what reached the wire can
        still be wrong. `leak-in-a-disclosed-detail` runs the details a viewer IS
        permitted through the kit's own S1.10 detectors — permission to read the
        reason is not permission to read a bearer token somebody pasted into
        it. -->
        <!-- evidence: libs/oshun/workbench-kit/src/audit.ts + audit.spec.ts (46
        cases, 49/49 mutants killed; 13 event violation kinds, 7 delivery fault
        kinds, 9 parity fault kinds and 5 disclosure fault kinds each reachable
        and asserted as a set). The mutation pass found two guards masked by
        sharing a fault KIND across two loops — the timeline actor check and the
        signoff signer check each satisfied a `toContain` the other should have
        — now separated by asserting the fault's SUBJECT. -->
      <!-- PHASE S2 COMPLETE: S2.1–S2.14, with only S2.3.d outstanding
      (browser-gated). -->


### S3 — Persistence, tenancy, events, and jobs

- [ ] S3.1 Write the shared persistence conventions document. Evidence: it
      covers explicit columns, `DateTime`, migrations, indexes, generated
      clients, and real-DB verification.
      <!-- PARENT HELD UNCHECKED: S3.1.h needs two named human approvals (data
      platform and security) and an agent may prepare the packet but may not
      approve it. Everything else is done.
      docs/conventions/shared-persistence-conventions.md, with a machine-checked
      companion: scripts/v1-workbenches/generate-persistence-conformance.mjs
      writes evidence/v1-workbenches/persistence-conformance.json, registered in
      `inventory:v1-workbenches`, `verify:inventory:v1-workbenches --check`, and
      a new `gate:persistence-ratchet`. 16 tests.
      A CONVENTIONS DOCUMENT NOBODY CAN MEASURE AGAINST IS ALREADY OUT OF DATE
      AND THE ONLY QUESTION IS BY HOW MUCH. So the document ships with a scanner
      over all 16 Prisma schemas / 439 models, and the report is EXPECTED TO BE
      MOSTLY RED — those models predate the document, and a scanner tuned until
      it came back green would be measuring the tuning. The enforcement that
      works on a tree this size is the RATCHET: a schema whose conforming count
      goes DOWN fails, new shared entities conform, existing ones do not get
      worse. -->
  - [x] S3.1.a Define entity/table naming, stable IDs, tenant/workspace scope,
        actor/revision/tombstone metadata, relationships, and extension fields.
        Evidence: normative data-model section.
        <!-- §1. Identity is the kit's `@oshun/workbench-kit/identity`
        vocabulary rather than bare UUIDs, so `idEquals(workbench, tenant)` is a
        COMPILE ERROR rather than a comparison that returns false; sequential
        integer keys are prohibited on anything a client sees, because a number
        that reveals how many rows exist is a business fact shipped in a URL.
        Scope is a COLUMN, never a join — an isolation rule enforced by a join is
        one a missing join disables — and a global reference table's omission
        must be DECLARED, because an undeclared omission is indistinguishable
        from a forgotten one. `updatedById` is a column for the same reason: an
        audit trail that joins to answer "who" stops answering when the join
        breaks, and it breaks exactly when somebody is looking.
        ON DELETE CASCADE is prohibited on anything carrying history: a cascade
        deletes an audit trail to keep a pointer tidy. -->
  - [x] S3.1.b Define which operational fields require explicit typed columns
        and the limited allowed uses of JSON/blob payloads. Evidence:
        column-versus-payload decision rules.
        <!-- §2. Six conditions that force a column (queried, constrained,
        authorized on, part of a lifecycle/version/identity, computed from by a
        retention rule, grepped for during an incident) and exactly THREE
        permitted payload uses: an opaque third-party response kept verbatim for
        provenance, schema-versioned document content owned by a domain plugin,
        and a rebuildable read-model projection. Everything else in a payload is
        a field somebody needs to query within a year, and the extraction
        migration then is strictly more expensive than the column now.
        The section says out loud that the scanner catches only the crudest
        version of this (a majority-JSON model, 439/439 passing today) and that
        whether a payload is genuinely opaque is a review obligation — listed in
        §11 rather than left as silently-unchecked prose. -->
  - [x] S3.1.c Define UTC instant, local date/time, duration, time zone,
        calendar, precision, server-clock, and serialization rules for
        `DateTime` values. Evidence: temporal semantics section.
        <!-- §3. Instants are `timestamptz`, always UTC, never String — a
        timestamp in a text column sorts as text and two spellings of one instant
        both exist. Serialisation is the kit's `Instant` shape (literal `Z`), so
        STRING ORDER IS TIME ORDER, which every ordered log and keyset cursor
        depends on.
        A LOCAL DATE IS A DIFFERENT TYPE, not an instant: a `date` plus an IANA
        zone column, because a local date coerced to an instant moves when the
        server does. Durations carry the unit in the NAME. Every platform
        timestamp comes from the database transaction or one injected clock, and
        §S2.14 is cited for why — two writers on skewed clocks produce an audit
        trail that reorders itself and both orders read as plausible. -->
  - [x] S3.1.d Define constraints, indexes, pagination order, query-plan review,
        row-level authorization, encryption, and sensitive-field storage.
        Evidence: integrity/performance section.
        <!-- §4. CONSTRAINTS BELONG IN THE DATABASE, because application checks
        run where somebody remembered to call them and a migration, a support
        script and a bulk importer all reach the table directly — with the kit's
        own four Postgres harnesses named as the template. Uniqueness that
        matters is a UNIQUE constraint, not a SELECT before an INSERT (the kit's
        revision race exists because that lesson cost a run). Pagination is
        keyset with the id as tie break, because offset pagination over a table
        receiving writes returns duplicates and skips rows silently and only
        under load. An index declares the query it serves, or nobody can remove
        it.
        Personal data goes in NAMED COLUMNS so a retention rule and an erasure
        request can find it — an identifier inside a JSON payload is one no
        erasure request will reach. Secrets are references, never columns. -->
  - [x] S3.1.e Define migration expand/backfill/verify/contract, compatibility,
        rollback/compensation, production rehearsal, and evidence conventions.
        Evidence: migration section.
        <!-- §5. Four SEPARATE DEPLOYABLE STEPS, and the compatibility rule is
        the one that makes them necessary: every intermediate state must be one
        where the PREVIOUSLY DEPLOYED version still works, because that is the
        state a rollback lands in. A migration only correct after the deploy
        completes cannot be rolled back.
        A backfill that cannot be resumed cannot be run twice, and it will be.
        Destructive steps ship alone and late: there is no compensating
        transaction for a dropped column, there is a restore from backup and an
        outage. Rehearsal records WALL-CLOCK LOCK DURATION, which is the number
        that matters rather than row count. 13/16 schemas have a migrations
        directory; the three without are named as the first to fix. -->
  - [x] S3.1.f Define generated client/model ownership, commands, checked-in
        artifacts, drift, version skew, and hand-edit prohibition. Evidence:
        code-generation section.
        <!-- §6. Generated output IS checked in (460 tracked files today) and
        the section defends that choice for a monorepo where a consumer must
        typecheck without running a generator — then names the obligation it
        creates: A CHECKED-IN ARTIFACT WITH NO DRIFT CHECK IS A CACHE WITH NO
        INVALIDATION, so CI regenerates and fails on a diff.
        Hand-editing is PROHIBITED rather than discouraged: a hand edit survives
        until the next regeneration and fails silently at that moment, in a diff
        nobody reads. Generator versions are pinned in the pnpm catalog because a
        client generated by a newer CLI fails at the first query, in production,
        with an error message about a field. -->
  - [x] S3.1.g Define required real-database unit-of-work, concurrency,
        constraint, migration, query-plan, isolation, backup/restore, and
        compatibility verification. Evidence: verification section.
        <!-- §7. Eight required real-Postgres verifications, each with the reason
        a fake cannot answer it. The rule extracted from the kit's four existing
        harnesses and stated normatively: A HARNESS MUST CONTAIN A NEGATIVE
        CONTROL THAT FAILS — `outbox-atomicity` injects the same failure inside
        one transaction and across two, and the two-transaction control MUST
        leave an orphan, because a harness whose control does not break has
        proved that it ran and not that it can fail.
        And they FAIL RATHER THAN SKIP with no database, because a skipped check
        reporting success is the evidence shape this ledger refuses. -->
  - [ ] S3.1.h Review the document against each shared entity and obtain data-
        platform/security ownership approval. Evidence: applicability and
        approval record.
        <!-- §8 ships the applicability review: a table of the nine shared
        entities S2 introduced, each with its owner scope, whether it is
        append-only, and whether it soft-deletes — and the observation that every
        one but the projection is append-only, which is the largest difference
        between them and most of the 439 models already in the tree.
        THE APPROVAL HALF IS GENUINELY BLOCKED. §10 names the two signatures
        outstanding (data platform for the identity scheme, payload policy,
        migration process and generated-artifact policy; security for the scope
        columns, row-level authorization, encryption and the verification list)
        and records that an agent may prepare the packet and may not approve it.
        This stays unchecked until both are recorded with a name, a date and a
        review date. -->
        <!-- evidence: docs/conventions/shared-persistence-conventions.md (11
        sections) + evidence/v1-workbenches/persistence-conformance.json + a
        16-test calibration suite. EVERY SCANNER RULE IS FED A MODEL THAT FAILS
        IT: a rule with only conforming fixtures cannot be told from one whose
        regular expression never matches, which matters most for the JSON rule
        that comes back 439/439. The calibration found a real parser defect
        before the baseline was committed — a `}` inside a `//` comment ended a
        model early and silently dropped every field after it. §11 lists the six
        things the document says and the scanner cannot check, so the gap between
        what is written and what is enforced is itself written down. -->
- [x] S3.2 Define tenant and workspace ownership for every shared entity.
      Evidence: row-access policy matrix.
      <!-- libs/oshun/workbench-kit/src/tenancy.ts, published as
      `@oshun/workbench-kit/tenancy`, 49 cases, 40/40 mutants killed, plus a new
      Postgres target `tenant-isolation`. 1,667 tests in the kit; all FOURTEEN
      Nx targets green.
      ISOLATION IS THE ONE PROPERTY IN THIS KIT WHERE BEING RIGHT 99% OF THE TIME
      IS INDISTINGUISHABLE FROM BEING WRONG: a single query that forgets its
      predicate is the whole failure, and it fails silently until the day two
      tenants happen to collide. So the module makes the scope of a row a
      DECLARED FACT a machine can check rather than a convention every call site
      has to remember. -->
  - [x] S3.2.a Inventory shared authoritative, revision, evidence, job, event,
        audit, blob, review, taxonomy, calendar, catalog, and projection
        entities. Evidence: entity register.
        <!-- `SHARED_ENTITY_REGISTER` ships sixteen entries covering all twelve
        kinds the cell names, and the register is NOT decoration: the suite runs
        every entry through `scopeFaults` with an empty predicate list and
        asserts a fault for each scoped one and none for each unscoped one, so an
        entry that stopped being enforceable fails there rather than sitting in a
        list. -->
  - [x] S3.2.b Classify each entity as tenant-owned, workspace-owned,
        cross-workspace tenant-owned, global reference, public immutable, or
        operational system state. Evidence: ownership classification.
        <!-- SIX CLASSES, BECAUSE COLLAPSING THEM IS WHAT PRODUCES THE LEAK, and
        the pair that matters is `cross-workspace-tenant` against
        `workspace-owned`: a taxonomy shared across a tenant's workspaces is not
        workspace-owned, and modelling it as one means either duplicating it per
        workspace or carrying a `workspaceId` that lies. A cross-workspace entity
        carrying one anyway is REFUSED — a column that must never be filtered on
        is one that will be, and the day it is, half the tenant's rows vanish for
        reasons nobody can reproduce.
        An unscoped class must DECLARE why, because an undeclared omission is
        indistinguishable from a forgotten one. A projection declared append-only
        is refused, since that is what turns S2.14's rebuildable cache into a
        second source of truth. All six classes are asserted to be in use, so
        none is decoration. -->
  - [x] S3.2.c Define creator, owner, contributor, reviewer, delegate, operator,
        service, and support access per operation and lifecycle state. Evidence:
        row-access matrix.
        <!-- The tenant check is FIRST and unconditional, which is the only
        ordering no later rule can undo — asserted for every principal.
        AN OPERATOR MAY READ OPERATIONAL STATE ACROSS TENANTS AND MAY NOT READ
        TENANT CONTENT. Somebody debugging a stuck queue needs the queue, not the
        story in it, and the reason every platform eventually grants both is that
        the matrix had one operator column.
        Support reads and never writes, and only while a time-boxed grant is
        live — the same principal S2.13 refuses a SIGNATURE from, for the same
        reason seen from the other side: it may see everything and take
        responsibility for nothing. An export counts as a write, because it is a
        write to somewhere else. A service with no capability is a token that can
        do whatever the code around it does. A reviewer who can edit is an author
        whose review nobody else performed. -->
  - [x] S3.2.d Define ownership inheritance and validation for child, join,
        revision, event, job, blob, cache, and projection rows. Evidence:
        relationship policy.
        <!-- A CHILD'S SCOPE MUST EQUAL ITS PARENT'S, AND A JOIN ROW HAS TWO
        PARENTS. The join is the case a foreign key cannot catch: each side is a
        perfectly valid reference to a row that exists, and it is only the PAIR
        that crosses a boundary, so neither table's constraints can see the other
        end. A link between one tenant's story and another's asset passes every
        per-table check ever written.
        A NULL scope is its own fault kind, because null matches nothing and is
        therefore filtered out of every isolation test that goes looking for the
        wrong tenant. The mutation pass caught the workspace half of that check
        being masked by the tenant half — same fault kind, two branches — now
        separated by asserting the subject. -->
  - [x] S3.2.e Define cross-workspace share, transfer, fork/copy, export/import,
        support access, and tenant migration behavior. Evidence: boundary
        transition rules.
        <!-- AN EXPORT CARRIES THE SCOPE IT CAME FROM, SO AN IMPORT IS A RE-SCOPE
        AND NEVER A RESTORE. That is the highest-consequence rule in the module
        and the least likely to be caught by anything else, because it is
        performed DELIBERATELY, by an administrator, using a SUPPORTED FEATURE.
        A share across tenants is refused outright: sharing grants read without
        copying, which inside a tenant is a permission and across tenants is one
        tenant reading another's rows — the honest operations across that
        boundary are a fork (copy and re-scope) or a transfer (move). A transfer
        moves the whole subtree or none: a parent in one place and its revisions
        in another is a history nobody can read. Operational state may not be
        exported at all, because it discloses volume and timing. A tenant
        migration needs an approver, being the largest scope change the platform
        can make and the one with no partial rollback. -->
  - [x] S3.2.f Enforce scope in schema/queries and test colliding IDs, counts,
        search, joins, caches, jobs, events, exports, and audit. Evidence:
        entity-complete isolation tests.
        <!-- The static half is `scopeFaults`, and the rule it exists for is
        `workspace-predicate-without-a-tenant-predicate`: a workspace id is
        unique inside a tenant AND NOWHERE ELSE, so `where workspaceId = ?` is
        correct right up until two tenants mint the same one — and it looks MORE
        careful than an unfiltered query, which is why it survives review. Joins
        are checked separately, because the predicate on the driving table says
        nothing about the table it was joined to.
        The runtime half is `tools/tenant-isolation.mjs`, and the reason it
        cannot be a unit test is the word COLLIDING: isolation bugs do not show
        up on distinct data, every query returns the right rows because there is
        only one tenant's worth of them. So two tenants are seeded with
        DELIBERATELY IDENTICAL workspace ids, slugs, external references and
        idempotency keys, and nine query shapes (count, the workspace-only
        filter, search, external lookup, join, aggregate, keyset page, job queue,
        audit trail) each run TWICE — the scoped form must return one tenant's
        rows and the unscoped form MUST return both. A fixture that fails to
        collide makes every green above it meaningless, so all nine controls are
        required to leak. Calibrated in both directions by hand: a scoped query
        made to leak is caught, and a control made not to leak is caught.
        The two layers are checked AGAINST EACH OTHER rather than each alone: the
        same shapes the engine runs are handed to `scopeFaults`, which must be
        silent on every scoped one and must complain about every unscoped one.
        The engine also holds the composite FK that makes a workspace id safe to
        filter on beside a tenant, and refuses a join row whose ends sit in
        different tenants. -->
- [x] S3.3 Define soft-delete, retention, legal-hold, and purge semantics.
      Evidence: lifecycle tests cover all transitions.
      <!-- libs/oshun/workbench-kit/src/deletion.ts, published as
      `@oshun/workbench-kit/deletion`, 45 cases, 38/38 mutants killed. 1,713
      tests in the kit; all fourteen Nx targets green.
      EVERY OTHER LIFECYCLE IN THIS KIT IS RECOVERABLE. This one ends in an
      irreversible step performed by a machine at a scheduled time against data
      nobody is looking at, which is why almost all of the code is about refusing
      to take it. -->
  - [x] S3.3.a Define active, deletion-requested, soft-deleted, retained,
        legal-held, purge-eligible, purging, purged, restored, and failed
        states. Evidence: deletion lifecycle machine.
        <!-- Ten states, and the table is worth reading for what is ABSENT.
        There is no edge out of `purged`: a purge is the one operation in this
        kit with no compensating action, and an `unpurge` edge would be a lie in
        a diagram. There is no edge from `legal-held` straight to
        `purge-eligible` either — a hold is released first, by a person, and only
        then does the clock resume. Every state is asserted reachable from
        `active`, so none is a state nothing can enter. -->
  - [x] S3.3.b Define deletion authorization, impact preview, confirmation,
        grace period, tombstone visibility, undo/restore, and notification.
        Evidence: soft-delete transition tests.
        <!-- The legal-hold check runs BEFORE the edge table, because a hold is
        not a state the subject is in — it is a condition over it, and a subject
        can be held while active, soft-deleted or retained. Checking the edge
        first lets a perfectly legal-looking `soft-deleted -> purge-eligible`
        through while a hold is in force, which is a mutant this suite kills.
        The grace period is the only recovery that needs no operator: it exists
        so an accidental deletion can be undone by the person who made it. Every
        step requires an actor and a reason, because this is the lifecycle where
        "who did this and why" is actually asked. -->
  - [x] S3.3.c Define retention duration/source, min/max precedence, category,
        tenant policy, regulatory/contractual override, and review. Evidence:
        retention policy tests.
        <!-- The strictest FLOOR and the strictest CEILING both win — the maximum
        of the minimums and the minimum of the maximums — and AN UNSATISFIABLE
        POLICY IS REFUSED RATHER THAN RESOLVED. A regulatory minimum of seven
        years and a contractual maximum of two cannot both be met, and settling
        it at purge time means choosing which obligation to breach, at 3am, in a
        job, with nobody told. It is a policy error and it belongs in front of
        whoever wrote the policies.
        The resolution names WHICH rule decided each bound, so a dispute has an
        answer, and ties resolve by source rather than by input order — asserted
        by reversing the input. An empty rule set is refused too: a category with
        no rule is not "delete whenever". -->
  - [x] S3.3.d Define legal-hold scope, authority, reason, evidence, start/end,
        protected data, secrecy, release, and audit. Evidence: hold lifecycle
        tests.
        <!-- THE HARDEST CASE IN THE CELL, AND THE ONE USUALLY HANDLED BY DOING
        NOTHING: a subject asks for erasure while litigation requires the data
        kept. The hold wins — and the request is SUSPENDED, not refused, which is
        the whole of the person's remaining rights, because a suspended request
        has a review date and resumes on its own.
        SECRECY IS A SEPARATE FIELD AND A SEPARATE MESSAGE KEY. A confidential
        hold still suspends the request, and the subject is told it is under
        review WITHOUT being told why — a different sentence, which is why it is
        a different key rather than a conditional inside one, and the hold id is
        withheld with it. A disclosable hold is preferred when both apply.
        `no-review-date-on-an-open-hold` is the rule that matters over years: an
        open-ended hold is normal (litigation does not schedule itself) and one
        with no end AND no review is an indefinite suspension of every deletion
        right the subject has, created by somebody who has since left.
        `revisionHoldMarker` is the seam to S2.4's narrower per-revision hold, so
        that marker cannot be minted from nothing. -->
  - [x] S3.3.e Define dependency order for metadata, blobs, revisions, events,
        caches, indexes, derivatives, providers, exports, and ledger records.
        Evidence: purge graph.
        <!-- PURGE ORDER IS NOT AN OPTIMISATION. It runs leaves-first because
        deleting the metadata first destroys the map to everything else — the
        blobs, the derivatives and the provider-side copies are then unreachable
        and become the permanent residue nobody can find to delete. Asserted
        positionally rather than described: every one of search-index, cache,
        derivative, provider-copy and blob must precede metadata, and the ledger
        REDACTION comes after even that, because the ledger is amended rather
        than removed (S3.3.g). -->
  - [x] S3.3.f Make purge durable, idempotent, resumable, observable, and
        fail-closed around held/referenced data. Evidence: partial-failure purge
        tests.
        <!-- `failed` IS A STATE, NOT AN EXCEPTION, because the alternative is
        the worst outcome available: a purge that half-succeeded and reported
        success leaves the data partly gone, so it cannot be restored, and
        recorded as gone, so nobody looks. A failed run carries exactly which
        steps completed and which were never attempted.
        Resuming is re-running with the same completed set — every step is
        idempotent by contract, so a step that ran and failed to record is safe
        to run again, and a resumed purge does not start from the top and
        re-delete an index that no longer exists. A completed purge re-run
        attempts nothing. A step with nothing to do is `skipped` rather than
        `done`, so a purge over an entity with no derivatives is not recorded as
        having deleted some. -->
  - [x] S3.3.g Preserve allowed noncontent audit/tombstone proof without
        retaining prohibited personal or protected content. Evidence: post-purge
        privacy review.
        <!-- A CONTENT HASH IS NOT ANONYMISATION. The surviving tombstone
        deliberately has NOWHERE TO PUT ONE — asserted structurally, by checking
        that no field name contains digest, hash or content — because a hash of a
        name, an email or a phone number is a lookup key for anybody who can
        guess the input, and guessing the input is the entire threat model for
        personal data. A digest would let a holder of the tombstone confirm
        whether a specific person was in the system, which is precisely the fact
        the purge removed.
        `residueIn` is the adversarial pass, in the shape S2.8.f, S2.10.e and
        S2.14.f use: a purge can run every step correctly and still leave the
        answer in the tombstone. It catches a free-text reason ("removed at the
        request of <name>" survives the purge that deleted <name>), an identifier
        smuggled into a type field, a tombstone keyed by the id it replaced
        (which re-identifies what was erased whenever the id came from the
        subject), and a step list that claims more than ran — the record somebody
        relies on to say the data is gone.
        The digest scan deliberately EXCLUDES the tombstone's own id, and the
        mutation pass forced that exclusion to be justified: an opaque random id
        rendered as hex is indistinguishable from a digest by shape, and scanning
        it would make every correctly-minted tombstone report a residue, which is
        how the check gets switched off. Pinned in both directions. -->
- [x] S3.4 Define revision and idempotency unique constraints. Evidence:
      concurrent duplicate writes collapse safely.
      <!-- libs/oshun/workbench-kit/src/idempotency.ts, published as
      `@oshun/workbench-kit/idempotency`, 29 cases, 25/25 mutants killed, plus a
      new Postgres target `idempotency-contention`. 1,743 tests in the kit; all
      FIFTEEN Nx targets green.
      AN IDEMPOTENCY KEY IS THE THING EVERYBODY AGREES THEY HAVE AND ALMOST
      NOBODY SCOPES CORRECTLY, and the failure is asymmetric: scoped too widely
      it returns one caller another's result, scoped too narrowly it runs an
      irreversible operation twice. Both are silent. -->
  - [x] S3.4.a Define unique entity revision ID and monotonic sequence/head
        constraints scoped by tenant/workspace and object. Evidence: schema
        constraints.
        <!-- Eight named constraints, each PLACED at the layer that can hold it,
        with the reason written beside it. The placement is the content: a
        constraint list is otherwise a list of things somebody BELIEVES are
        enforced.
        `unique-sequence-per-entity` is called out as the one that decides an
        append-only race, and a SELECT-then-INSERT does not — the same lesson the
        kit's `revision-race` harness cost a run to learn. The database and
        application halves are asserted to PARTITION the set, so nothing is in
        both or neither. -->
  - [x] S3.4.b Define parent/base revision referential integrity and prevent
        self, cross-object, missing, and cyclic lineage. Evidence: constraint
        and integrity tests.
        <!-- TWO OF THE EIGHT CANNOT BE DATABASE CONSTRAINTS AT ALL, and saying
        so is the honest half. A FOREIGN KEY PROVES THE PARENT EXISTS AND NOT
        THAT IT BELONGS TO THIS ENTITY — comparing the two entity ids is a join
        the constraint does not perform — and a single head is a count over
        siblings rather than a property of a row. They are marked `application`
        rather than written down as if a CHECK covered them.
        Cyclic lineage is S2.4's `checkHistoryIntegrity` and is proved against a
        real engine by `history-constraints`, which is cited rather than
        duplicated: the same three-colour walk, in the one place it lives. -->
  - [x] S3.4.c Define idempotency record uniqueness by tenant, actor/client,
        operation/version, key, and target where required. Evidence: key scope
        constraints.
        <!-- THE KEY IS NOT THE CONSTRAINT. Five fields plus the target, and
        dropping any one is a bug somebody has shipped: without the tenant two
        customers collide, without the client one caller's retry returns
        another's result, without the operation a reused key crosses operations,
        WITHOUT THE OPERATION VERSION a retry arriving after the contract changed
        hands a v1 result to a v2 caller who will parse it.
        THE ENCODING IS INJECTIVE, and the suite found the first draft was not: a
        `-` sentinel for a null target collided with a real target id of `-`, and
        a separator inside an id let `a|b` and `a` + `|b` produce one key. A key
        two scopes can forge returns one caller another's result, which is the
        exact failure this module is about, so it is JSON rather than a join.
        `key-is-not-opaque` looks fussy and is not: a key derived from an email
        or a URL leaks that value into every log line, error message and support
        ticket that quotes it back. -->
  - [x] S3.4.d Store canonical request hash and reject same key with different
        input, target, identity, or contract. Evidence: mismatch tests.
        <!-- THE DIGEST IS COMPARED BEFORE THE STATE, and the ordering is the
        rule. A stored record in `completed` whose digest differs is NOT a replay
        — it is a caller reusing a key for something else, and returning the
        stored result reports SUCCESS FOR SOMETHING THAT NEVER RAN, to somebody
        who will never look again. Every other branch returns a result, so the
        comparison has to come first; asserted across all four states. -->
  - [x] S3.4.e Atomically return existing accepted/running/completed result
        under concurrent identical requests. Evidence: high-contention real-DB
        tests.
        <!-- THE ONE CLAIM IN S3.4 NO UNIT TEST CAN MAKE, and the wrong
        implementation — SELECT, then INSERT if absent — passes every serialized
        test ever written and fails only under exactly this load. So
        `tools/idempotency-contention.mjs` opens 32 real connections and fires
        them at one key four ways: the claim (exactly one wins, and the 31 losers
        RE-READ and must find the winner's row — returning NOTHING to the loser
        is the second-most-common bug here and looks like a transient failure
        rather than a race), the replay (all 32 get the same result reference),
        the conflict (all 32 sending a different digest are refused without
        disturbing the stored record), and THE NEGATIVE CONTROL: the naive
        read-then-write everybody writes first, raced identically, which MUST
        perform the effect many times. It performed it 32 times.
        Calibrated by hand in both directions — an upserting claim is caught, and
        a control repaired so it cannot double-effect is caught. -->
  - [x] S3.4.f Define expiry/partition/archive behavior without allowing late
        duplicates of irreversible commands. Evidence: retention boundary tests.
        <!-- AN IDEMPOTENCY RECORD'S RETENTION IS A PROPERTY OF THE OPERATION'S
        REVERSIBILITY, NOT A GLOBAL TTL. Expiring the record for an irreversible
        command re-enables a duplicate of it — a second charge, a second publish,
        a second delete — and a 24-hour default applied uniformly across every
        operation is how that reaches production. An expired irreversible record
        REFUSES the request rather than letting it through, because letting it
        through performs the operation a second time, which is exactly what the
        record existed to prevent.
        `earliestExpiry` takes the irreversible FLOOR and survives a
        misconfigured policy where somebody set the retry window shorter than the
        reversible cap — asserted, because that is the configuration a partition
        job would otherwise use to drop a record early. A record expired the
        moment it is written reports that ALONE, because every other check would
        be reasoning about a lifetime that does not exist. -->
- [x] S3.5 Define searchable indexes for stage, status, owner, dates, taxonomy,
      gate status, and stable identifiers. Evidence: query plans on realistic
      volume avoid sequential scans for critical paths.
      <!-- `src/indexes.ts` (36th entry point) + `tools/query-plans.mjs` (15th Nx
      target). AN INDEX LIST IS THE EASIEST ARTIFACT IN A SCHEMA TO WRITE AND THE
      HARDEST TO ARGUE WITH A YEAR LATER, because by then nobody remembers which
      query each one was for and removing any of them is a gamble — so the
      workload ships first and every index names the queries it exists for, in
      both directions.
      Verified on a real Postgres 16 at 948,000 rows: 24 queries judged, every
      interactive one blocking without its index, three separate controls. The
      static model and the engine calibrate each other — `coverageFor` predicts a
      sort or its absence BEFORE each query runs and the harness fails if the
      plan disagrees, which is how the module's central rule got corrected: the
      first version carried an "ordering is broken" flag that a mutation pass
      showed was dead (masked by the alignment check), and asking Postgres showed
      the real rule is that A LIST AND AN ORDERING ON THE SAME COLUMN ARE
      MUTUALLY EXCLUSIVE — the planner keeps the ordering and the list narrows
      nothing. 100 tests in the cell, 20 mutations killed. -->

  - [x] S3.5.a Enumerate critical filter/sort/pagination queries and production-
        shape row/cardinality distributions. Evidence: query workload manifest.
        <!-- `SHARED_WORKLOAD` in libs/oshun/workbench-kit/src/indexes.ts: 24
        queries, each with its predicates (equality/membership/range/text-prefix/
        is-null), sort keys, pagination mode and the person waiting for it.
        `SHARED_VOLUME_PROFILES` carries the distribution for all 8 entities —
        volume class, write class, p50/p99 rows per tenant, LARGEST TENANT SHARE,
        per-column distinct/null/sensitive, and the column sets unique within
        scope. `distributionFaults` reports 7 kinds, including a column any query
        filters on with no declared cardinality.
        THERE IS NO `offset` MEMBER of `PAGINATION_MODES`: a rule in a document
        can be forgotten and a type cannot. And a workload with no declared skew
        is one that will be measured on whatever the fixture contained — uniform
        data makes every index look excellent. -->

  - [x] S3.5.b Add tenant/scope-prefixed indexes for lifecycle stage and domain
        status combinations. Evidence: stage/status query plans.
        <!-- `LIFECYCLE_INDEXES`: `wbe_board_stage` (tenant, workspace, stage,
        updatedAt DESC, id DESC) and `wbe_review_queue`, partial on the one stage
        a review queue is ever about. The scope prefix is DERIVED from the
        ownership class S3.2 assigned rather than restated, and narrowed to the
        columns every served query actually pins — an inbox is a tenant-wide read
        of workspace-owned rows, so demanding a workspace prefix would demand an
        index it cannot enter.
        Plans measured by `pnpm nx query-plans`: the board pages with no Sort
        node. The same index for the same board filtered to SEVERAL stages sorts
        its whole match to return 25 rows, which is the one declared limitation
        below. -->

  - [x] S3.5.c Add owner, actor, assignee, reviewer, team, and delegated-scope
        indexes required by inbox/search. Evidence: ownership query plans.
        <!-- `OWNERSHIP_INDEXES` plus `OWNERSHIP_DIMENSIONS`, which gives each of
        the six words its own row: a plan covering five of six reads identically
        to one covering six. assignee → `wbe_assignee_open` (partial on the open
        flag, not on a stage list, because an index predicate rewrite is a
        rebuild); reviewer → `wbe_review_queue`; team/owner →
        `wbe_team_unassigned`, partial on `assignee_id IS NULL`, the one case
        where indexing a mostly-null column is the cheap option.
        ACTOR IS ANSWERED ON THE AUDIT STREAM (`audit_actor_activity`), not on
        the entity: "what did this person do" is a question about the trail, and
        the entity table can only answer it for the last person who touched each
        row — and audit_event has index budget while the central authoring table
        does not. DELEGATED SCOPE IS THE SAME INDEX with a different principal
        bound, so it is a separate ACCESS PATH in the manifest and not a second
        index; what differs is the authorization, and S3.2 owns that.
        Search: `wbe_title_prefix`. Plans measured. -->

  - [x] S3.5.d Add created/updated/due/effective/expiry/embargo/publish/event
        time indexes with deterministic ID tie-breakers. Evidence: date query
        plans.
        <!-- `TEMPORAL_INDEXES` + `TEMPORAL_DIMENSIONS`, one row per kind of
        time. Five are kit columns with an index (created, updated, due, expiry,
        publish, event); `effective` and `embargo` are declared DOMAIN-OWNED with
        the rule any plugin's version must follow, because an effective date
        belongs to the thing that is effective and an embargo is a publication
        rule a newsroom has and a courseware workbench does not.
        `sortIsTotal` is the tie-break check and it counts what the query FIXES,
        not whether the sort ends in `id`: the audit stream fixes objectId and
        sorts by sequence, and `(objectId, sequence)` is unique, so bolting an id
        on would buy a wider index for nothing.
        A TIE BREAK IS A CORRECTNESS PROPERTY, NOT A PERFORMANCE ONE, and the
        harness proves it rather than asserting it: 3,000 rows stamped with one
        instant, then the same keyset walked three ways. An exclusive cursor on
        the timestamp alone returned 25 of 150 rows and stopped; an inclusive one
        returned 75 rows of which 25 were distinct, handing back the same page;
        with the id in the cursor, 150 rows in exactly the total order. -->

  - [x] S3.5.e Add taxonomy membership/path/alias indexes for supported
        hierarchical and facet queries. Evidence: taxonomy query plans.
        <!-- `TAXONOMY_INDEXES` + `TAXONOMY_DIMENSIONS`. Membership in BOTH
        directions (`tax_link_term`, `tax_link_entity`) — a join table gets one
        index by accident, the one its key gives it, and the reverse lookup is
        the one that turns into a scan on the page showing a record's tags.
        `tax_path_prefix` makes a subtree a prefix range. `tax_alias_exact` is
        UNIQUE on the raw alias: an index on `lower(alias)` resolves two aliases
        that differ only in case to one term, and nobody sees the merge until the
        wrong term is applied.
        THE C COLLATION IS NOT AN OPTIMISATION HERE. In the database collation a
        b-tree cannot serve `LIKE 'x%'` at all — the plan reads the whole scope
        and filters, which is not a sequential scan and passes any gate looking
        only for one. Probed against Postgres 16 before the model was written:
        the other spelling, `text_pattern_ops`, makes the prefix work and then
        answers `ORDER BY path` with an incremental sort, because they are
        different operator families. `COLLATE "C"` gives both. -->

  - [x] S3.5.f Add gate ID/verdict/severity/freshness/evaluator/policy/expiry
        indexes for eligibility and triage. Evidence: gate query plans.
        <!-- `GATE_INDEXES` + `GATE_DIMENSIONS`, one row per property. gate id +
        freshness → `gate_freshness`; verdict → `gate_triage` (the console reads
        one verdict at a time, worst first, so it is an equality and not a
        filter); the S2.10 fold's read → `gate_target_history`; expiry →
        `waiver_expiry`, partial on active because S2.11 keeps revoked and
        superseded grants readable forever and none of them can expire into
        anything.
        SEVERITY IS DELIBERATELY NOT A COLUMN ON THE RUN. It is a property of the
        gate DEFINITION at a lifecycle stage (S2.9) — the same failing run is a
        blocker before release and a warning in draft — so denormalising it onto
        the run would make the copy authoritative, which is the exact drift S2.9
        exists to name. evaluator/policy are a DECLINED index with the trade-off
        stated: gate_run is append-only-hot with a budget of three, and the
        comparability report runs on the scale of a policy review rather than a
        page load, so it is declared `full-scan-batch` and the gate is told to
        expect a scan rather than to fail one. -->

  - [x] S3.5.g Add exact internal/external stable identifier uniqueness and
        lookup indexes without fuzzy collision. Evidence: identifier plans.
        <!-- `IDENTIFIER_INDEXES` + `SHARED_IDENTIFIER_LOOKUPS` +
        `identifierFaults`, which reports two things and neither of them shows up
        as slow: an identifier lookup with no UNIQUE index (the second row
        arrives without complaint and the lookup starts answering with whichever
        one the index reaches first), and uniqueness that omits the ISSUING
        SYSTEM.
        AN EXTERNAL IDENTIFIER IS UNIQUE PER ISSUER AND NOWHERE ELSE, so
        `wbe_external_ref` is unique over (tenant, issuer, ref): uniqueness over
        (tenant, ref) holds exactly until a second upstream is connected, and
        then two unrelated records collide on one string and one of them stops
        being reachable. No `lower()`, no trim, no normalisation — a fuzzy
        identifier index is a merge nobody asked for. -->

  - [x] S3.5.h Measure write/storage cost, redundant indexes, partial-index
        predicates, sensitive-data exposure, and plan stability. Evidence: index
        trade-off report.
        <!-- MEASURED, not estimated: evidence/v1-workbenches/
        workbench-kit-query-plans.json, written by the harness from a real run.
        Storage: 95.5 MiB of indexes against 82.1 MiB of table, per index. Write
        cost: 20,000 inserts took 337ms with the eight workbench_entity indexes
        and 44ms without them — 7.7x, which is the argument for a hard budget
        rather than a preference.
        Static half in `indexFaults`: `redundantIndexes` (a left prefix with no
        narrower partial predicate — and a UNIQUE index is never redundant
        however wide the other one is, because it is a constraint wearing an
        index's clothes); `partialIsImplied` (a partial predicate the query never
        states is an index the planner cannot prove contains the rows, so it will
        not use it); `sensitive-column-indexed` over columns AND include payloads
        (an index on personal data is a second copy an index-only scan reads and
        a base backup carries); `INDEX_BUDGET` per write class. Plan stability is
        `estimate-off-by-an-order-of-magnitude`, warn rather than block because it
        is a leading indicator — and it is suppressed under a `Limit`, since a
        node told to stop early produces fewer rows than planned BECAUSE it was
        told to.
        `DECLINED_INDEXES` is the mirror of "an index declares the query it
        serves": a query with a named reason for having no index, so the next
        person does not add one on an append-only table forever to speed up a
        twice-a-year report. -->

  - [x] S3.5.i Fail performance checks when critical realistic queries regress
        to unsafe sequential scan or unstable sort. Evidence: query-plan gate.
        <!-- `judgePlan` + `parseExplainPlan` in src/indexes.ts, driven by the
        new Nx target `query-plans` (tools/query-plans.mjs, 15th target). Seven
        plan faults, each with a verdict conditional on the entity's volume class
        and the query's criticality — a sequential scan over a reference table is
        the correct plan and failing it teaches people to add indexes that cost
        writes and buy nothing.
        THE MOST DANGEROUS PLAN IS NOT A SEQUENTIAL SCAN. It is
        `filter-discarded-most-of-what-it-read`: an index scan that enters on the
        tenant, reads 40,000 rows and returns 22, because the predicate that
        discriminates is not one the index could apply. Every gate that greps for
        `Seq Scan` passes it. `parseExplainPlan` THROWS on input it cannot read
        rather than returning an empty plan, and a missing plan is
        `indeterminate` — an absent measurement is not a passing one.
        THE HARNESS, at 948,000 rows across 8 tables: 24 queries judged, 1
        blocking and it is the declared limitation, asserted to be exactly the
        plan fault the declaration NAMES so a stale declaration cannot go on
        excusing something else. NEGATIVE CONTROL: with every index dropped, all
        18 interactive queries must come back blocking — if one passed, the
        fixture could not tell a good plan from a bad one. THIRD CONTROL: the
        same gate on a 500-row copy WITH every index blocks 6 of 18 correct
        plans, so the harness refuses to report anything below a 50,000-row
        floor. A toy fixture does not give a weaker answer, it gives a wrong one.
        Two more measured: the small tenant's board through an index led by
        `stage` discards 28,572 rows and blocks, and through the tenant-leading
        one discards 0 and passes — neither is a sequential scan; and a list plus
        an ordering on one column read 8,572 rows with no Sort node, confirming
        the planner keeps the ordering and drops the array. -->

- [x] S3.6 Implement a transaction-bound outbox. Evidence: commit-with-event and
      rollback-without-event integration tests.
      <!-- `src/outbox.ts` (37th entry point) + `tools/outbox-dispatch.mjs` (16th
      Nx target, `outbox-dispatch`). 53 tests in the cell, 1,898 in the kit, 24
      mutations killed with no survivors.
      The evidence line asks for two integration tests and the harness runs
      FIVE properties with FIVE controls, because each of these is a claim about
      a database and a scheduler that a fake store would only prove about the
      fake: the atomic write (control: two transactions, which MUST orphan), the
      concurrent claim (control: unlocked SELECT-then-UPDATE, which MUST
      double-claim), per-subject order (control: unpartitioned claiming, which
      MUST invert), the crash window (control: mark-then-publish, which MUST lose
      the event), and lease recovery.
      Two of the six decisions are enforced by TYPE rather than by rule:
      `PayloadRef` has no member for a pointer to mutable state, and
      `markPublished` cannot be reached without a branded receipt only a real
      broker acknowledgement produces — so the publish order that loses events
      cannot be written at all. -->

  - [x] S3.6.a Define outbox row identity, tenant, producer, aggregate/subject,
        event kind/version, sequence, causation/correlation/trace, payload ref,
        availability, attempt, and state. Evidence: outbox schema.
        <!-- `OutboxRow` in libs/oshun/workbench-kit/src/outbox.ts, with
        `rowFaults` reporting 14 kinds, every one of them fed a failing row.
        THE OUTBOX ID IS THE PUBLISHED EVENT'S ID, never re-minted per attempt:
        an outbox is at-least-once by construction, so the second delivery has to
        be recognisable as the same event or every consumer's dedup is
        decoration, and its key is exactly S3.4's idempotency scope.
        `PayloadRef` HAS NO `lookup` MEMBER — an event carrying only `{id}`
        publishes whatever that row holds when the consumer gets there, which is
        not what happened, it is what happened LAST; leaving the member out means
        no producer can write one. CAUSATION IS NOT CORRELATION: with only a
        correlation you know forty events belong together and not which produced
        which, which is what an incident asks. `leased` is a STATE, not a flag on
        pending — collapsing them is how a dispatcher that died leaves rows
        nothing will pick up. -->

  - [x] S3.6.b Insert authoritative mutation and outbox intent in one database
        transaction through the shared unit of work. Evidence: atomic commit/
        rollback tests.
        <!-- `commitWithEvents` is the unit of work, and THERE IS NO EXPORTED
        FUNCTION THAT COMMITS A MUTATION ALONE. That is the design: the guarantee
        is not "remember to append the outbox row", it is that a signature which
        lets you forget does not exist. `emissionFaults` runs BEFORE the
        transaction opens, so a commit carrying an event about a different
        aggregate — atomic with the wrong thing, and reading as more thorough
        rather than less — never reaches the database.
        Proved twice. Unit: the call order is asserted exactly
        (begin, mutate, append, commit), and an append that throws rolls the
        mutation back. Real engine (`pnpm nx outbox-dispatch`, phase 1): commit
        wrote the row and its event; an injected failure rolled both back; and
        THE CONTROL — the same failure across two transactions — left an
        authoritative row with no event beside it, which is the orphan this
        design exists to prevent. -->

  - [x] S3.6.c Claim unpublished rows with bounded batches, ordering/fairness,
        leases or skip-locking, and concurrent dispatcher safety. Evidence:
        dispatcher contention tests.
        <!-- `ClaimPolicy` + `claimFaults` (6 kinds) + `selectBatch` + `claimOrder`
        + `reclaimable`. PER-SUBJECT ORDER IS THE ONLY ORDER THERE IS AND
        CONCURRENCY BREAKS IT BY DEFAULT, so `subjectPartitioned` holds one event
        per subject in flight; the sequence numbers otherwise record an order
        nobody observed. Fairness is a PER-TENANT CAP inside the batch, because
        one tenant's backlog is otherwise every dispatcher's whole batch. A lease
        that is not longer than the publish timeout is a fault: it expires while
        the publish is still in flight.
        Real engine, phase 2: 8 dispatchers over 240 rows. FOR UPDATE SKIP LOCKED
        claimed every row exactly once; THE CONTROL — the unlocked
        SELECT-then-UPDATE everybody writes first — claimed ~900 rows more than
        once (and deadlocks outright when it updates a multi-row set, which is
        the same defect wearing a louder hat). Phase 2 also runs `selectBatch`
        from the BUILT package and the SQL claim over the same rows and fails if
        they choose different subjects: a selection rule and a statement that
        disagree is how a policy becomes a comment.
        LANDMINE: Postgres refuses FOR UPDATE beside DISTINCT, so the partition
        is two NOT EXISTS (no leased sibling, no earlier pending sibling). -->

  - [x] S3.6.d Publish with stable event ID and destination/partition semantics,
        then mark outcome without losing an externally published event.
        Evidence: publish/ack crash-window tests.
        <!-- PUBLISH, THEN MARK — NEVER THE REVERSE. The crash window between them
        cannot be closed, only pointed in a direction, and the tidier-looking
        order is the one that loses data. So `markPublished` takes a
        `DeliveryReceipt`, the receipt is branded with a type-only symbol, and the
        ONLY function returning one is `receiveAck` — which refuses an
        acknowledgement with no broker offset, because a call that returned is not
        an acknowledgement. The unsafe order cannot be written with these types.
        The partition key is the SUBJECT and never the tenant: a broker preserves
        order within a partition and nowhere else, so partitioning by anything
        coarser than the unit of ordering throws that order away at the last
        possible moment.
        Real engine, phase 4: publishing first and crashing delivered the event
        TWICE UNDER ONE ID, which every consumer deduplicates. THE CONTROL,
        mark-then-publish, delivered it ZERO times with the row saying published —
        no retry will find it and no alarm will fire. Phase 3 measured the
        ordering half: partitioned claiming published 1,2 and the unpartitioned
        control published 2,1. -->

  - [x] S3.6.e Apply retry classification, backoff, max age/attempts, circuit,
        and poison quarantine. Evidence: delivery fault tests.
        <!-- `decideDelivery` + `backoffSeconds` + `circuitFor`. UNKNOWN IS NOT
        RETRYABLE AND NOT PERMANENT: folding it into the first turns one poison
        message into an infinite loop that starves everything behind it; folding
        it into the second discards an event because DNS was slow. It gets its own
        lower attempt ceiling and is then a person's problem.
        Order is not arbitrary and the mutation pass checks each step: a permanent
        failure quarantines whatever its attempt count OR its age (retrying a
        rejected schema forty times produces forty identical rejections and one
        very confident engineer); age is checked before attempts, because the row
        that has been failing for a week on a long backoff is the one nobody is
        looking at. MAX AGE AND MAX ATTEMPTS ARE BOTH NEEDED — attempts alone lets
        a row on a long backoff live for weeks. Jitter is a FRACTION of the delay,
        not a constant, because a constant added to a capped backoff stops
        spreading exactly when the queue is worst.
        The circuit counts a failure RATE over a window, not consecutive
        failures: a destination failing every other message is broken, and a
        consecutive counter never trips on it because every success resets it. It
        says nothing below a minimum sample count, because opening a circuit on
        two failures is how a deploy takes a destination down. -->

  - [x] S3.6.f Expose queue age, attempts, lag, failures, poison, throughput,
        correlation, and safe replay operations. Evidence: outbox operability
        tests.
        <!-- `queueHealth` + `byCorrelation` + `decideReplay` (5 refusals).
        DEPTH IS NOT THE ALARM; AGE IS. A queue holding a hundred thousand rows
        draining at ten thousand a second is healthy and a queue holding eleven
        rows that has not moved in an hour is an incident — depth alarms on the
        first and says nothing about the second, which is why every dashboard
        that alarms on depth has a story about the outage it missed. The suite
        pins that with the two queues side by side.
        A REPLAY IS SAFE FOR EXACTLY ONE REASON: it is the same event id, so
        every consumer that already saw it drops it. `replay-would-mint-a-new-
        event-id` is the refusal that matters — under a new id it is not a
        replay, it is a second event, and it is the shape a well-meaning operator
        reaches for when the original row looks stale. Replaying a quarantined
        row without resolving what quarantined it repeats the failure; past the
        retention window consumers have discarded their dedup state, so the
        replay arrives as new. -->

  - [x] S3.6.g Retain/archive/delete dispatched rows according to replay, audit,
        privacy, and incident policy. Evidence: outbox retention tests.
        <!-- `OutboxRetentionRule` + `outboxRetentionFaults` (4 kinds) +
        `outboxRetentionAction`. TWO CLOCKS, NOT ONE, and that is the whole
        section. The ROW is the only record that something was published, so
        deleting it on success makes the first incident question — "did we send
        it?" — permanently unanswerable; it is kept for the incident window. The
        BODY may hold personal data that S3.3's erasure has to reach, so it is
        redacted on its own shorter schedule. Collapsing them means choosing
        between an unanswerable incident and an unerasable payload, and
        `personal-data-kept-as-long-as-the-row` is the fault that names it.
        AGE IS NEVER APPLIED TO AN UNPUBLISHED ROW: a stuck row is exactly the
        evidence of why it is stuck, and a quarantined one is an open question
        whose answer is in the payload. A retention window shorter than the
        replay window the console offers is a fault too — the operation it lists
        would fail on rows it still shows. -->

- [x] S3.7 Implement an idempotent inbox. Evidence: duplicate and out-of-order
      events produce one correct projection.
      <!-- `src/inbox.ts` (38th entry point) + `tools/inbox-idempotency.mjs`
      (17th Nx target). 44 tests in the cell, 1,943 in the kit, 20 mutations
      killed.
      THE ASYMMETRY THE WHOLE MODULE FOLLOWS FROM: an event processed twice is a
      VISIBLE bug, and an event suppressed as a duplicate and never applied is an
      INVISIBLE one — because the suppression is exactly the mechanism that would
      otherwise have fixed it. Every decision here points away from the second.
      Two of them are enforced by vocabulary rather than by rule: there is no
      `receipt-first` binding strategy, and the dedup key has no shape that omits
      the consumer. -->
  - [x] S3.7.a Define inbox identity by tenant, consumer/projection, producer,
        event ID/version, partition/subject, and payload hash. Evidence: inbox
        schema.
        <!-- `InboxKey` + `inboxKey` + `InboxReceipt` + `validateKey` (7 kinds) in
        libs/oshun/workbench-kit/src/inbox.ts.
        THE DEDUPLICATION KEY CARRIES THE CONSUMER. One event delivered to three
        projections has to be processed three times, and a receipt keyed on the
        event id alone — which is what "have we seen this event?" naturally
        becomes — lets the first consumer's receipt silently suppress the other
        two. Proved rather than asserted: the harness runs both keys and the
        event-id-only one suppresses the second projection with no error, no row,
        nothing to find.
        The key is `JSON.stringify` of the tuple, for the reason S3.4 found the
        hard way: any separator can appear inside a field. `consumer === producer`
        is refused — a service consuming its own event deduplicates against its
        own write and stops processing anything. -->

  - [x] S3.7.b Validate envelope/version/authorization/scope and persist receipt
        before or with side effects. Evidence: invalid-event tests.
        <!-- `decideIntake` with 6 refusals, each fed a failing request. THE
        ORDER IS THE POINT: the tenant is checked before anything about the
        content, because an event that crossed a boundary is a security question
        and not a routing one — and a version check that refused first would give
        a probe a way to learn which versions exist. The suite pins that with a
        request that is BOTH foreign-tenant and unsupported-version and asserts
        which answer comes back.
        AN UNKNOWN KIND IS REFUSED, NOT IGNORED: silently dropping an event a
        consumer does not recognise is indistinguishable from the outside from
        processing it, and it is what happens on the day a producer ships a kind
        this consumer was supposed to handle. -->
  - [x] S3.7.c Deduplicate concurrent and repeated delivery and return prior
        processing outcome without repeating effects. Evidence: duplicate
        contention tests.
        <!-- `decideClaim` returning 6 results. THE BODY IS COMPARED BEFORE THE
        STATE: a stored receipt whose payload hash differs is not a duplicate at
        all, it is one event id carrying two events, and answering it with the
        stored outcome reports success for something that never ran. A DUPLICATE
        IS ANSWERED WITH THE PRIOR OUTCOME, never with "already processed" — a
        consumer that acknowledges without knowing what happened cannot tell an
        applied event from an ignored one, and those want different acks. A
        `processed` receipt carrying no outcome is refused rather than answered.
        Real engine (`pnpm nx inbox-idempotency`, phase 1): 24 simultaneous
        deliveries of one event applied it ONCE through a unique constraint and
        exactly one caller believed it ran the work. THE CONTROL — the
        read-then-write dedup everybody writes first — applied it 24 times. -->
  - [x] S3.7.d Define per-subject sequence, gap, late, out-of-order, and
        commutative-event handling. Evidence: ordering fixture matrix.
        <!-- `decideOrdering` over 5 dispositions + `releasable` + `foldDeliveries`.
        A GAP IS NOT A LOSS UNTIL IT IS OLD: position 5 arriving with 4 unseen is
        almost always 4 still in flight, so it is HELD; treating the gap as a
        loss immediately turns every reordering into an incident, and holding
        forever is a projection that silently stops advancing — so past a
        tolerance it becomes `gap-is-a-loss`, a third answer that is neither.
        WHETHER A KIND COMMUTES IS DECLARED, NEVER INFERRED, with a reason: a tag
        added and a tag removed commute, a balance credited and a balance
        multiplied do not, and the difference is not visible in the payload.
        The fold's high-water mark is the largest CONTIGUOUS position and never
        the largest applied — for a commutative kind those are different numbers,
        and using the wrong one drops a late-arriving 1 and 2 as superseded.
        A mutation pass caught exactly that, and the case that kills it is now in
        the suite. -->
  - [x] S3.7.e Apply inbox receipt and projection/domain mutation in one
        transaction or documented saga. Evidence: crash atomicity tests.
        <!-- `BINDING_STRATEGIES` HAS NO `receipt-first` MEMBER, and that is the
        sharpest thing in the cell. On the producing side, writing the receipt
        before the effect costs a duplicate; HERE IT COSTS THE EVENT
        PERMANENTLY, because the receipt is what suppresses the redelivery that
        would have repaired it — the deduplication is what makes the loss
        permanent. Leaving the option out of the vocabulary is the only way to
        make it unavailable rather than merely discouraged. A saga is allowed and
        must name BOTH its compensation and its reconciliation; a compensation
        beside a real transaction is refused as code that will never run and will
        be trusted anyway.
        Real engine, phase 3: one transaction, crash, redelivery → the effect
        landed exactly once. THE CONTROL, receipt-first → the effect landed 0
        times AND the redelivery was suppressed. The event is simply gone. -->
  - [x] S3.7.f Persist processing version/result/error/attempt and support safe
        retry after transient failure. Evidence: retry tests.
        <!-- The receipt carries `processingVersion`, `attempt`, `outcome` and
        `lastError`; `decideRetry` reads them. THE PROCESSING VERSION IS PART OF
        THE RECEIPT: a receipt written by v1 of a projection does not mean the
        event has been processed by v2, and treating it as one is how a fixed
        reducer never runs on the history it was fixed for — `decideClaim`
        returns `stale-processing-version` for a newer consumer and, deliberately,
        does NOT reprocess for an older one. A non-transient failure is
        quarantined on the first attempt, because the same reducer on the same
        body fails the same way and more attempts produce a longer log and
        nothing else. -->
  - [x] S3.7.g Rebuild a projection from replayed duplicate/out-of-order history
        and match authoritative expected state. Evidence: full replay parity.
        <!-- `replayParityFaults` compares FIELD BY FIELD, never by a digest —
        S2.14's rule, for the same reason: a checksum answers "do they differ",
        and the question worth asking is which field, because a drifted reducer, a
        lost event and a row somebody wrote to directly all produce one bit and
        three different investigations. It distinguishes a field the replay lost
        from one the store never had, which are opposite diagnoses.
        `foldDeliveries` is the reference, and the suite asserts the property the
        whole module is for: five shufflings of one history, duplicates included,
        all produce the same projection. Real engine, phase 4: the shuffled
        delivery [3,1,1,5,2,4,3] left the projection at [1,2,3,4,5] and the
        control that applies on arrival left it at [3,1,5,2,4]; the kit's fold and
        the engine's projection were then compared field by field. -->
- [x] S3.8 Implement poison-event quarantine and replay. Evidence: operator
      workflow test preserves cause and audit.
      <!-- `src/quarantine.ts` (39th entry point) + `tools/quarantine-blast-radius.mjs`
      (18th Nx target). 39 tests in the cell, 1,983 in the kit, 22 mutations
      killed with no survivors.
      This cell is where S3.6 and S3.7 both stop. The reusable shape is the
      THREE-WAY comparison: blast radius is a property where both errors are
      expensive and only one of them is loud, so a harness with a single control
      cannot show it — too wide is an outage somebody notices in minutes, too
      narrow is a projection that silently applied position 4 without 3. -->
  - [x] S3.8.a Classify malformed, unsupported, unauthorized, invariant,
        dependency, deterministic-handler, repeated transient, and unknown
        poison conditions. Evidence: classification tests.
        <!-- `classifyPoison` over all 8 conditions in
        libs/oshun/workbench-kit/src/quarantine.ts, each fed signals that produce
        it. ORDER IS CHEAPEST AND MOST CERTAIN FIRST: a message that would not
        parse cannot have violated an invariant, and an UNAUTHORIZED event is
        refused WITHOUT being inspected for what it would have done.
        REPEATED TRANSIENT IS ITS OWN CONDITION — a timeout that has failed twelve
        times is not a timeout, and filing it under `unknown` hides the one fact
        that matters: the network was fine and the handler is not. UNKNOWN IS NOT
        A BUCKET: it carries `unclassified: true`, because a growing unknown pile
        is a taxonomy that has stopped describing reality and nothing about it
        looks wrong on a dashboard. `dependency-missing` is kept separate from
        `deterministic-handler-failure` — one is usually an ordering problem and
        the other is a bug, and folding them applies one's fix to the other. -->
  - [x] S3.8.b Quarantine original immutable event reference/hash, safe payload
        access, consumer/version, attempts, cause chain, trace, and timestamps.
        Evidence: quarantine schema tests.
        <!-- `QuarantineRecord` + `recordFaults` (8 kinds). THE RECORD HOLDS A
        REFERENCE AND A HASH, NEVER A SECOND COPY OF THE TRUTH: a copy diverges
        and then two records disagree about what happened with no way to tell
        which is the event; the hash is what makes the reference checkable, and
        S3.8.e uses it to refuse a replay of something that has since moved.
        THE PAYLOAD IS NOT RAW BY DEFAULT. A quarantine console is where every
        failed message in the system ends up, which makes it the one screen where
        personal data from everywhere is displayed together, to whoever is on
        call — so `authorized-raw` requires a named assignee and the reading is
        attributable. The cause CHAIN is stored newest-first and checked for it:
        the last error is the symptom. -->
  - [x] S3.8.c Stop hot-loop delivery while isolating only the affected event,
        subject, partition, or consumer according to ordering semantics.
        Evidence: blast-radius tests.
        <!-- `isolationFor` + `deliveryIsBlocked`, and `pnpm nx
        quarantine-blast-radius`. THE BLAST RADIUS IS THE SMALLEST SCOPE THAT
        PRESERVES THE ORDER, AND BOTH DIRECTIONS ARE WRONG — which is why this is
        the rare harness with THREE configurations and two of them required to be
        visibly wrong rather than one control:
          event scope    left the projection at [1,2,4,5] — position 4 applied
                         over a missing 3. Nothing downstream reports it. This is
                         the dangerous direction because it is the quiet one.
          consumer scope left an UNRELATED subject with nothing applied at all:
                         an outage with a different name.
          subject scope  left the poisoned subject waiting and the unrelated one
                         moving. The only answer that is both correct and
                         available.
        The scope is DERIVED from the kind's commutativity (S3.7.d's declaration)
        rather than chosen per incident, and the harness checks the kit derives
        the middle answer. `partition` is in the vocabulary because an isolation
        can be declared there, and nothing DERIVES it — a partition holds many
        subjects, so choosing it stops streams that were never affected. -->
  - [x] S3.8.d Expose authorized inspect, assign, comment, link incident/fix,
        suppress, transform/migrate, replay, skip, and archive actions.
        Evidence: operator API/UI journey.
        <!-- `OPERATOR_ACTIONS` (9) + `authorizeAction` with 7 refusals, each
        fed a request that produces it. SKIP AND SUPPRESS ARE DIFFERENT ACTIONS:
        skipping abandons ONE event and the position it held, suppressing stops a
        CLASS of them, and a suppression scoped to a single event is REFUSED —
        because calling a one-off decision a standing rule is how it stops being
        reviewed. A skip needs a reason, because the gap it leaves in the
        sequence will outlive the person who made the decision. A TRANSFORM MUST
        RETAIN THE ORIGINAL: a repaired event replayed with no record of what it
        was is a forgery, however well-intentioned the repair. A closed record
        may be read and commented on and nothing else — reopening is a new
        decision, not a continuation. -->
  - [x] S3.8.e Revalidate event, handler version, dependencies, authorization,
        sequence, and dry-run impact before replay. Evidence: replay guard
        tests.
        <!-- `replayGuards` runs all 7 and `failedGuards` names the ones that
        did not pass. The one that surprises people:
        REPLAYING WITHOUT A CHANGE REPEATS THE FAILURE. If the event is the same,
        the handler is the same and nothing around it moved, the replay is a
        retry with a longer audit trail — so it is refused unless the handler
        version moved OR the cause was environmental. The others: the reference
        still resolves; the HASH is unchanged (otherwise the replay runs
        something other than what was quarantined); dependencies are present;
        the producer is still authorized (a quarantined event does not carry a
        grant forward); and the SEQUENCE POSITION IS STILL MEANINGFUL — if the
        subject has applied past it, replaying now applies an old state over a
        newer one. -->
  - [x] S3.8.f Replay idempotently with new attempt identity while preserving
        original event identity and complete audit. Evidence: replay integration
        tests.
        <!-- `planReplay`. THE EVENT IDENTITY IS PRESERVED AND THE ATTEMPT
        IDENTITY IS NEW, and both halves are load-bearing: preserved so S3.7's
        receipt recognises it — which makes replaying something that DID apply a
        harmless no-op — and new so the audit has two runs to point at. An
        attempt id equal to the event id is refused, as is a replay on a failed
        guard or on a PARTIAL checklist (a replay authorized on fewer guard
        results than there are guards is authorized on the ones somebody
        remembered).
        Proved at a real engine: republished under its own id the receipt
        recognised it and the effect stayed at 1; re-minted as a new event — the
        shape "just re-emit it" takes at 3am — the receipt missed it and the
        effect was applied twice. -->
  - [x] S3.8.g Alert and reconcile growing quarantine, blocked partitions, stale
        owner, and unresolved age. Evidence: poison operations tests.
        <!-- `quarantineHealth` + `quarantineAlerts` over 5 alert kinds.
        A BLOCKED PARTITION IS AN OUTAGE WITH A DIFFERENT NAME: the quarantine
        count is a workload number and the blocked-partition AGE is an
        availability one, and a console showing only the first is one where a
        stopped stream looks like a busy week. `taxonomy-drifting` fires on the
        FRACTION of open records that are unclassified rather than the count,
        because the question is whether the conditions still describe what is
        failing. `staleOwner` counts assigned-and-untouched separately from
        unassigned — the second is a queue and the first is a false sense that
        somebody has it. -->
- [x] S3.9 Implement durable jobs with lease, heartbeat, progress, checkpoint,
      cancellation, retry, and dead-letter state. Evidence: clock-controlled
      integration tests.
      <!-- `src/jobs.ts` (34th production entry point) + `tools/job-fencing.mjs`
      (20th Nx target). 112 tests in the cell, 2,096 in the kit, 43 mutations
      killed with no survivors.
      S3.6 and S3.7 move an event between two processes that are both ALIVE.
      This cell is the case where one of them is not — and, worse, does not know
      it yet. Every bug here reads as correct code: expiry reads as safety, a
      checkpoint reads as progress, a reconciler reads as repair. So the harness
      carries FOUR controls rather than one, and every one of them has to come
      back visibly wrong before the answers beside them mean anything. -->
  - [x] S3.9.a Persist job identity/type/version, tenant/actor, immutable
        inputs, priority/queue, idempotency, budget, deadline, and initial
        queued state. Evidence: job creation tests.
        <!-- `JobRecord` + `jobCreationFaults` (14 kinds), all 14 produced by a
        test. THE TWO THAT ACTUALLY BITE: `idempotency-key-not-tenant-scoped` —
        a key namespace shared across tenants means tenant B's enqueue can be
        answered with tenant A's job, silently, and the first symptom is a
        result in the wrong account; and `deadline-inside-one-attempt`, where
        the job is enqueued ALREADY unable to finish, so it burns an attempt, a
        lease and a slot to discover what was knowable at creation.
        `JOB_STATES` has no `failed`: a failure with attempts left is `queued`
        with a later `runnableAt` and one without them is `dead-lettered`; a
        state in between is a shelf where jobs sit while nobody is either
        retrying them or triaging them. Priority is a declared BAND rather than
        a free integer because the failure is social — with an open range the
        first caller in a hurry passes 1000 and the queue has a private VIP lane
        nobody agreed to. The inputs are a REFERENCE plus a hash (S3.8.b's
        rule): "immutable" is a promise until something checks it, and S3.9.h's
        replay is what checks it, months later. -->
  - [x] S3.9.b Claim with exclusive time-bounded lease, worker ID, attempt, and
        fencing token. Evidence: competing-worker tests.
        <!-- `decideJobClaim` (10 refusals) + `JobLease` + `FENCING_SOURCES`, and the first
        two controls of `pnpm nx job-fencing`. A LEASE IS NOT A LOCK AND WHAT
        MAKES IT SAFE IS THE FENCING TOKEN. Exclusivity belongs to the store's
        conditional UPDATE, and the harness shows the difference: read-then-write
        let BOTH workers believe they had claimed the job; the conditional
        UPDATE produced one winner and minted the token in the same statement.
        `FENCING_SOURCES` has no `wall-clock` member and that absence is the
        point — two claims inside one millisecond produce EQUAL tokens and a
        stepped-back clock produces one the store has already beaten, and in
        both cases `token > highest` passes a zombie write through while looking
        exactly like a correct implementation. `attempt-counter` qualifies only
        because the attempt is incremented inside the same atomic claim — and the
        claim DECLARES its source and is held to it, so a token claiming to come
        from the counter that is not the incremented attempt is refused rather
        than trusted.
        The claim also refuses the three things a worker would otherwise
        discover by doing the work: a deadline that will pass mid-attempt, a
        budget that cannot afford the attempt, and a token that does not beat
        the last one. -->
  - [x] S3.9.c Renew heartbeat and lease on a controlled cadence and prevent
        expired workers from committing. Evidence: lease-loss tests.
        <!-- `heartbeatFaults` (4 kinds), `workerHoldsLease`,
        `leaseReclaimableAt`, `fenceWrite` -> `FencedWrite`.
        THE MARGINS POINT AWAY FROM EACH OTHER: the worker gives the lease up
        EARLY (expiresAt - skew) and the reconciler takes it away LATE
        (expiresAt + skew), so the window between them belongs to nobody. A test
        walks every second across the lease and asserts there is no instant
        where both believe they hold it. Symmetric margins would leave one, and
        everything written inside it is a zombie write.
        `one-missed-beat-loses-the-lease` is what keeps a healthy worker alive:
        with under three beats per lease a single dropped packet evicts a worker
        that is doing fine, and the operator's conclusion is "leases are flaky".
        `fenceWrite` is the ONLY producer of `FencedWrite`, so a commit that did
        not check the fence cannot be written down. It refuses a superseded
        token EVEN WHILE THE LEASE LOOKS LIVE, which is the case a lease alone
        cannot see and CONTROL 2 of the harness demonstrates: with expiry and no
        fence at the write, the paused worker's result overwrote the live one's;
        with the token compared at the write, the same UPDATE matched 0 rows. -->
  - [x] S3.9.d Persist stage-specific completed/total progress, message key,
        output refs, and monotonic timestamp without false completion. Evidence:
        progress invariant tests.
        <!-- `StageProgress` + `progressFaults` (8 kinds) + `jobProgress` +
        `completionFaults` (3 kinds). PROGRESS IS THE ONE FIELD WHOSE FAILURE
        MODE READS AS SUCCESS, so three separate rules point the same way.
        (1) `jobProgress` returns NO `done` — a test asserts the exact key set —
        because completion is a STATE decided by whatever commits the last
        effect; derive it from the progress table and a worker that dies after
        its final report has "finished".
        (2) An unknown total is `null` and never `0`: a total of zero renders as
        a FULL bar in every progress widget ever written.
        (3) A denominator with a hole in it is invented, so one indeterminate
        stage makes the whole fold indeterminate rather than summing what is
        known and reporting a percentage of it.
        And the direction that is explicitly NOT a fault: every stage at its
        total while the job is still `running` is normal — the work is done and
        the result is not committed. Flagging it is how a system ends up wiring
        completion to the bar. `observation-went-backwards` exists because two
        reports that cross in flight land in the order they land, and without a
        monotonic observation the older one overwrites the newer. -->
  - [x] S3.9.e Persist versioned resumable checkpoints transactionally with
        authoritative stage effects. Evidence: crash/resume tests.
        <!-- `CHECKPOINT_ORDERINGS` + `planStageCommit` + `decideResume`, and
        CONTROL 3 of the harness. A CHECKPOINT WRITTEN BEFORE ITS EFFECT LOSES
        THE EFFECT FOREVER — the same asymmetry as S3.6/S3.7 and the same
        answer: there is no `checkpoint-first` member. Effect-first can only
        REPEAT, which idempotence absorbs; checkpoint-first SKIPS, and nothing
        anywhere reports a stage that was never run. The harness kills a
        five-unit stage during unit 3: checkpoint-first resumed from 3 and
        finished [1,2,4,5] with unit 3 never applied by ANYBODY and the job
        reporting success; effect-first resumed from 2, redid unit 3, and the
        key made it 5 effects for 5 units.
        `planStageCommit` refuses the two combinations that read as correct: a
        NON-transactional effect declared part of the store transaction (the
        code looks right and the external call sits outside the transaction
        supposedly protecting it), and effect-first on a non-idempotent effect
        (effect-first exists precisely so the resume may redo it).
        `decideResume`'s unobvious answer is `refuse` for an unreadable
        checkpoint on a non-idempotent stage: restarting looks helpful and
        redoes effects the checkpoint knows about and this worker cannot see —
        "how far did it get" is exactly the question the unreadable cursor
        holds. A checkpoint at a HIGHER fence than your lease means you are the
        zombie, not the resumer. -->
  - [x] S3.9.f Implement requested/cancelling/cancelled semantics, cooperative
        checkpoints, noncancellable regions, external cancel, and compensation.
        Evidence: stage cancellation tests.
        <!-- `requestCancel` (4 outcomes), `regionFaults` (3 kinds),
        `cancellationPoint` (3 actions), `compensationPlan`.
        CANCELLATION IS A REQUEST AND A JOB IS NOT CANCELLED BECAUSE SOMEBODY
        ASKED. A QUEUED job has nobody to ask and is cancelled here and now; a
        RUNNING one enters `cancelling` and stays there until the worker
        acknowledges. Collapsing the two tells the operator the effects have
        stopped at the exact moment they have not, and they act on that.
        `region-longer-than-the-lease` turns the safety mechanism INTO the
        failure it was protecting against: inside a region longer than the
        lease the worker is guaranteed to become a zombie before it comes out,
        so the uninterruptible section is precisely the window in which somebody
        else claims the job. `escalate-and-fence` is the third answer that stops
        `cancelling` being a state jobs live in — a region past its own bound is
        a cooperative cancel that has ALREADY failed, and waiting longer for it
        is what leaves a queue full of half-cancelled jobs.
        `compensationPlan` undoes in reverse order and reports
        `partiallyApplied`: a cancelled job with an uncompensable effect behind
        it left real state in the world, and reporting `cancelled` with nothing
        beside it describes a world that does not exist. -->
  - [x] S3.9.g Classify retryability and apply bounded attempts, exponential
        backoff/jitter, maximum age, capacity, and spend policy. Evidence:
        deterministic retry tests.
        <!-- `JOB_FAILURE_KINDS` + `classifyJobFailure` + `jobBackoffSeconds` +
        `decideJobRetry` (3 decisions). Reuses S1.10's `Retryability` vocabulary
        rather than inventing a parallel one — these are the same four answers a
        public error already has to give.
        `cancelled` is deliberately NOT a failure kind: the moment it joins the
        taxonomy some branch of the retry decision picks it up and retries a job
        somebody asked to stop.
        THE DEADLINE AND THE BUDGET ARE DIFFERENT REFUSALS. Time may have been
        the network; money was spent and spending it again is a decision a
        person makes. The spend check runs BEFORE the attempt against the
        attempt's own estimate, because one that runs after is an accounting
        entry rather than a control.
        `unknown-outcome`'s answer DEPENDS ON THE EFFECT and carries its own
        LOWER attempt ceiling, since each attempt may be a second effect — a
        test pins that the third attempt is refused by the unknown-outcome
        ceiling and not by the ordinary one wearing its coat.
        `defer` is a third answer and not a retry: a queue at capacity has not
        refused the attempt, it has not OFFERED one, so a deferred job burns
        neither an attempt nor a budget reservation. Folding capacity into
        `retry` is how a busy hour quietly consumes every job's allowance.
        Jitter is a FRACTION of the delay (S3.6's rule): a constant window stops
        spreading exactly when every worker's backoff has converged on the cap,
        which is when the spread is the only thing preventing a synchronized
        retry. -->
  - [x] S3.9.h Dead-letter exhausted/terminal jobs with safe diagnostics, owner,
        retained inputs, remediation, and authorized replay/abandon. Evidence:
        dead-letter workflow test.
        <!-- `DeadLetter` + `deadLetterFaults` (9 kinds) +
        `authorizeDeadLetterAction` (7 refusals) + `planDeadLetterReplay`.
        `diagnostic-is-a-serialized-payload` is S3.8's problem arriving by a
        different road: a dead-letter console shows every failed job from every
        part of the product to whoever is on call, and the easiest way to write
        a useful diagnostic is to serialize the thing that failed — which puts
        the inputs, and whatever personal data was in them, on that screen. The
        inputs are already retained BY REFERENCE for the replay; the diagnostic
        does not need a second copy. Detected syntactically (a JSON signature
        and a length bound), not by a `redacted: true` promise.
        THE JOB IDENTITY IS PRESERVED — the mirror of S3.8.f, and the argument
        is the idempotency key. A new job id forces one of two wrong moves:
        reuse the key and the enqueue is answered with the dead job's outcome,
        or drop it and two runs of the same work exist with nothing relating
        them. `planDeadLetterReplay` CONSTRUCTS the continuation, so a caller
        cannot mint a new id, and refuses an approval granted for a different
        action. `inputs-moved` is the jobs-specific half of "nothing has
        changed": the inputs are declared immutable, so a reference that now
        hashes to something else is not a changed job — it is a DIFFERENT job
        wearing this one's id. `purge-would-strand-an-open-record` refuses to
        remove the only remaining action on a record nobody has closed. -->
  - [x] S3.9.i Reconcile orphan leases and external outcomes unknown after
        worker death. Evidence: fault-injection repair test.
        <!-- `reconcileOrphan` (6 repairs) + `orphanSweep`, and CONTROL 4 of the
        harness. AN ORPHANED LEASE IS NOT A FAILED JOB. When a worker dies, what
        happened to its external effect is UNKNOWN, and unknown is not failed:
        calling it failed and retrying doubles the payment, calling it done
        drops the work. CONTROL 4 makes the first one visible — reconciling an
        unknown at-most-once outcome as a failure and requeuing left TWO charges
        for one job — and the kit's answer left one, with the job parked for a
        person. With a probe available the same input is ASKED rather than
        assumed (`probe-the-external-system`), and a probe that comes back
        `committed` marks the job succeeded rather than redoing the work.
        The reconciler waits the SKEW out (S3.9.c) before touching anything:
        reclaiming at the lease's own expiry races the holder's last heartbeat.
        A checkpoint at a fence above the lease means a newer holder already has
        the job and this lease is the stale record, so the repair is to leave it
        alone. Every repair that takes the lease away sets `fenceOut`, because
        the previous holder may be alive and about to write. -->
- [x] S3.10 Implement job parent/child graphs and aggregate progress. Evidence:
      partial failure semantics are deterministic.
      <!-- `src/job-graphs.ts` (35th production entry point) +
      `tools/graph-admission.mjs` (21st Nx target). 42 tests in the cell, 2,139
      in the kit, 38 mutations killed with no survivors.
      S3.9 is one job and one worker. This is the shape real work has, and the
      whole cell follows from one thing: A PARENT'S ANSWER IS NOT ANY SINGLE
      CHILD'S ANSWER. Cycle detection is the S2.1 stage engine's Tarjan REUSED
      over a lifted edge set rather than a second implementation. -->
  - [x] S3.10.a Define parent/child edge type, ordering, dependency,
        optional/required, weight/unit, failure policy, and cancellation
        propagation. Evidence: graph schema.
        <!-- `JobEdge` + `FAILURE_POLICIES` + `CANCELLATION_PROPAGATIONS`.
        TWO ABSENT MEMBERS. `FAILURE_POLICIES` has no `ignore`: a policy that
        makes a failure invisible is the one thing a graph must not offer,
        because the child that is safe to ignore today is the one whose silent
        failure is diagnosed six weeks later as "the thumbnails stopped working
        at some point". `CANCELLATION_PROPAGATIONS` has no `orphan-children`:
        stopping the parent and leaving the children running is how a cancelled
        job goes on spending with nobody reading the results, and
        `let-children-finish` is the same intent stated honestly.
        REQUIRED/OPTIONAL IS THE FAILURE POLICY rather than a second field beside
        it — two fields that must agree are two places to get it wrong, and this
        is the one that decides anything. `isRequired` derives it. Ordering is a
        declared `ordinal`, so it does not depend on insertion. -->
  - [x] S3.10.b Validate known jobs, same tenant/workbench, acyclic graph,
        maximum depth/fan-out, and immutable edges after execution begins.
        Evidence: graph constraint tests.
        <!-- `graphFaults` (14 kinds, all 14 produced by a test) + `asEdgeSet` +
        `graphDepth` + `graphFanOut`. THE REUSE: `cyclesIn`/`unreachableFrom`
        from `src/stages.ts` (S2.1) over a lifted edge set. It carries BOTH edge
        kinds — parent-to-child AND dependency-to-dependent — because a cycle
        among SIBLINGS is the one a parent/child check alone would miss and the
        easy one to write. `initial` is the root, which `cyclesIn` ignores and
        `unreachableFrom` uses, so a node no parent edge reaches is reported.
        `edges-changed-after-execution-began` is the immutability rule: once a
        node has left `queued`, a new dependency describes an execution that did
        not happen, because the child it constrains may have already run without
        waiting for it. `zero-weight-child` is the quiet one — a child with no
        weight contributes nothing to the bar and can still fail the parent, so
        the bar reads 100% on a graph that cannot finish. `mixed-weight-units`
        refuses adding seconds to megabytes. -->
  - [x] S3.10.c Schedule ready children only after required dependencies and
        capacity/budget gates. Evidence: dependency scheduling tests.
        <!-- `readyChildren` -> {ready, blocked, deferred, unreachable}, and
        `pnpm nx graph-admission`. THE BUDGET IS CHECKED OVER THE ADMITTED SET,
        NOT PER CHILD: five children each individually affordable are not five
        children the budget can afford, and a per-child check passes all five and
        finds out afterwards — S3.9's "an accounting entry rather than a control"
        one level up. The harness shows it costs 600 against a ceiling of 250
        WITH NO CONCURRENCY AT ALL.
        `unreachable` is separated from `blocked` because they need opposite
        actions: a blocked child is fine and waiting, and one whose dependency
        dead-lettered is a graph that cannot complete and will otherwise sit in
        `waiting` forever with nothing left to wait for. A child whose cost
        nobody estimated is deferred rather than started. -->
  - [x] S3.10.d Aggregate heterogeneous progress through explicit weights/units
        and preserve unknown/indeterminate work honestly. Evidence: known-value
        aggregate tests.
        <!-- `aggregateProgress` -> {totalWeight, knownWeight, unknownWeight,
        lowerBound, upperBound, fraction}. AN UNKNOWN CHILD IS A RANGE, NOT A
        ZERO — and this is where the parent knows MORE than S3.9.d did about a
        single job. S3.9 could only answer `null` for an indeterminate stage; a
        parent knows the unknown child's WEIGHT, so it can state exactly how much
        of the answer is missing. Known-value test: weights 3600/60/300 at
        0.5/null/1 gives [0.530303, 0.545455] and a `fraction` of null, because a
        single number here would be a guess dressed as a measurement.
        A child with NO report is unknown rather than zero: zero is a claim about
        the work, absent is a claim about the report. And a parent with no weight
        reports null rather than a full bar. -->
  - [x] S3.10.e Define parent queued/running/waiting/partial/failed/cancelled/
        succeeded outcome for every required/optional child combination.
        Evidence: exhaustive state truth table.
        <!-- `parentOutcome` over `PARENT_OUTCOMES`, with a test that enumerates
        ALL 144 combinations of two children across 6 job states x 2 failure
        policies and asserts the properties on every one, including both
        directions of "succeeded means exactly one thing" and "the loss list
        exists iff there is a loss".
        `partial` EXISTS SO THE FOLD NEVER HAS TO CHOOSE BETWEEN TWO LIES:
        `failed` throws away required work that did finish, `succeeded` records
        the loss nowhere. ORDER: `failed` is decided before `cancelled`, because
        a dead-lettered required child needs somebody and a cancellation is a
        decision already made — reporting the cancellation first buries the fault
        under an intention. `waiting` vs `running` distinguishes "in flight with
        nothing executing" from "something executing", and its detail separates a
        STARVED QUEUE (children could start, none is) from a dependency.
        `failed` exists here and deliberately NOT in `JOB_STATES` (S3.9.a): a
        leaf's failure is an attempt to retry or triage, and a parent's is a FACT
        about its children. -->
  - [x] S3.10.f Propagate cancellation and retry without duplicating completed
        children or invalidating reusable output incorrectly. Evidence:
        cancellation/retry graph tests.
        <!-- `propagateCancellation` + `planGraphRetry`. BOTH DEFAULTS ARE WRONG,
        SO NEITHER IS THE DEFAULT: re-running everything duplicates work that
        succeeded, and reusing everything that succeeded reuses output produced
        by the code that has just been fixed — which is WORSE, because it looks
        like a successful retry. The input hash and the handler version decide,
        per child.
        AND REUSE IS TRANSITIVE. A child downstream of one being re-run is
        re-run too, however unchanged its own inputs look, because its inputs
        were the upstream child's output; reusing it produces a result that is
        half old and half new with nothing saying so. Computed as a closure over
        `dependsOn`, and a test pins that one changed handler version re-runs the
        whole chain behind it.
        Cancellation stops the NEWEST work first and never touches a finished
        child — "cancel the graph" is a statement about work that has not
        happened, and applying it to work that HAS gives a completed child an
        outcome it never had. -->
  - [x] S3.10.g Expose traversable graph, critical path, child results,
        blockers, costs, and safe diagnostics. Evidence: graph query/UI tests.
        <!-- `graphView` + `rootBlockers` + `criticalPath`.
        ROOT BLOCKERS, NOT IMMEDIATE ONES. A parent waiting on a child that is
        itself waiting on two others has three names it could report, and only
        the ones not waiting on anything are worth an operator's attention — the
        rest are consequences, and reporting them is how an incident spends
        twenty minutes walking a dependency chain by hand.
        THE CRITICAL PATH IS OVER DECLARED DEPENDENCIES AND EXPECTED DURATIONS,
        never over what happened to be slow: a child that took three hours
        because the queue was full is not on the critical path, and putting it
        there sends people to optimise a scheduling problem as though it were a
        work problem. Enforced by construction — `criticalPath` takes only the
        graph, so an actual elapsed time cannot reach it.
        The view carries a `failureCode` and has NO message field: a graph view
        shows every child of every tenant's job to whoever is on call, which
        makes it the same screen as S3.8's quarantine console and S3.9's dead
        letter, and a child's failure MESSAGE is exactly where a serialized input
        rides in. A test pins the node's key set so a message cannot be added
        without somebody deciding to. -->
- [x] S3.11 Implement blob references with size, media type, checksum,
      encryption, retention, and provenance metadata. Evidence: stored-byte
      rehash integration test.
      <!-- `src/blobs.ts` (36th production entry point) +
      `tools/blob-storage.mjs` (22nd Nx target). 61 tests in the cell, 2,200 in
      the kit, 51 mutations killed with no survivors, plus 7 mutations of the
      BUILT file that the storage harness had to catch.
      Every other module in this kit describes something this system decided.
      This one describes bytes SOMEBODY ELSE produced, in a store SOMEBODY ELSE
      runs, and almost every rule follows from not believing either of them. -->
  - [x] S3.11.a Define blob reference ID, tenant/scope, immutable storage
        locator/version, state, created actor/time, and owning references.
        Evidence: blob schema.
        <!-- `BlobReference` + `blobFaults` (9 kinds, all 9 produced) +
        `locatorKey`. THE LOCATOR IS IMMUTABLE AND THE VERSION IS PART OF IT
        rather than a field beside it: overwriting the bytes under a locator
        silently re-points every reference that pinned it, which turns a
        content-addressed reference (S1.4.c) back into a floating one without
        changing its type. `version-not-recorded` in S3.11.g is the same rule
        defeated by an omission instead of an overwrite.
        The three `active-without-…` faults are separate rather than one, because
        they fail for different reasons — the scanner was down, the read-back was
        skipped, the owning row was rolled back — and each describes a blob a
        reader would treat as real. -->
  - [x] S3.11.b Record server-observed byte size and sniffed media type
        separately from untrusted supplied metadata. Evidence: spoofing tests.
        <!-- `DeclaredMetadata` + `ObservedMetadata` + `sniffMediaType` +
        `spoofFaults` (6 kinds). THE DECLARED AND THE OBSERVED NEVER MERGE:
        there is deliberately no single `mediaType` or `byteSize` on the record,
        and a test pins the key sets, because the moment there is one field the
        uploader's claim quietly becomes the server's observation and an
        executable is served as an image with everything in the log looking
        correct.
        THE SNIFFER RETURNS EVERY MATCH, NOT THE FIRST. A real magic-number table
        (PNG/JPEG/GIF/WebP at offset 8/MP4 ftyp at offset 4/PDF/ZIP/GZIP/OGG plus
        ELF, Mach-O, PE and a shebang), and the zip END-OF-CENTRAL-DIRECTORY is
        scanned in the TAIL — which is what makes a GIF/zip polyglot detectable
        at all. Two decoders will disagree about that file and both will be
        right, so returning one answer hands each of them a different file while
        reporting one. Known-byte tests, not shape tests.
        A disagreement is not an error: none of these faults means the bytes are
        bad, they mean the DESCRIPTION was wrong — and a system that reconciles
        them by overwriting the description has destroyed the evidence that
        anything was ever wrong. -->
  - [x] S3.11.c Compute approved hash from stored bytes and persist algorithm,
        digest, and verification time. Evidence: known-byte and tamper tests.
        <!-- `APPROVED_DIGEST_ALGORITHMS` + `DIGEST_VERIFICATION_SOURCES` +
        `verifyDigest` (5 outcomes) + `verificationIsStale`, and `pnpm nx
        blob-storage`. THE DIGEST IS OF THE STORED BYTES READ BACK. Hashing the
        inbound stream proves what you were SENT; hashing what the store returns
        proves what you HAVE, and the two differ in exactly the cases that
        matter.
        TWO NAMED WAYS TO PRODUCE A GREEN TICK OVER NOTHING, both of which the
        harness demonstrates against a real object store: `metadata` compares the
        stored digest to ITSELF and passed while the bytes underneath had been
        replaced; a prefix read passed because the corruption was in the last 16
        of 2,248 bytes, which is where a truncated write leaves it. Both are
        outcomes with names rather than silent passes.
        `md5` and `sha1` are absent and stay absent: S3 hands back an MD5 as the
        ETag, an ETag is a cache token, and the way that mistake enters a
        codebase is a field that will hold either. -->
  - [x] S3.11.d Define encryption mode/key reference/version, region, transport,
        rotation, and decrypt authorization without storing keys. Evidence:
        encryption metadata tests.
        <!-- `EncryptionMetadata` + `encryptionFaults` (7 kinds) +
        `authorizeDecrypt` (4 refusals). `KEY_MATERIAL_LOCATIONS` HAS NO `inline`:
        a record that CAN hold a key eventually does — at the moment somebody is
        debugging why decryption fails, in a row that is then backed up,
        replicated and shipped to a support console. The absence is the control;
        a policy saying "do not put keys here" is not.
        `no-key-version` is the one that costs a weekend: an unversioned key
        reference resolves to whatever is current, so the FIRST ROTATION makes
        every object encrypted before it undecryptable — and the failure appears
        only when somebody reads an old blob, which is never near the rotation in
        a log. `key-region-differs-from-blob-region` is not a storage detail: a
        key that crosses a region crosses a jurisdiction.
        `authorizeDecrypt` returns a NAME and a VERSION and a test pins the key
        set. Whoever holds the decision still has to go to the key service and be
        told no again; a function that returned material would be making that
        service's decision on its behalf. -->
  - [x] S3.11.e Define quarantine/active/rejected/retained/held/purge states,
        retention source/expiry, legal hold, and deletion semantics. Evidence:
        blob lifecycle tests.
        <!-- `BLOB_STATES` + `admitBlob` + `deletionStateFor` + `purgeRefusals`.
        THIS MODULE DOES NOT OWN A SECOND RETENTION MACHINE. Retention sources,
        the strictest-floor/strictest-ceiling resolution, legal holds, purge
        steps, tombstones and residue are all S3.3's (`src/deletion.ts`), and
        `deletionStateFor` is the ENTIRE interface between the two — a mapping,
        not a reimplementation. Two retention implementations is two answers to
        "may this be deleted", and the one that gets asked is whichever module
        the caller happened to import. A test walks every blob state and asserts
        the mapping lands in a state `DELETION_TRANSITIONS` actually has.
        A BLOB IS ADMITTED TO NOTHING BY DEFAULT: `unavailable` is not `passed`,
        so a check that could not answer holds the blob in QUARANTINE while one
        that answered no REJECTS it. The difference between "nothing objected"
        and "everything approved" is invisible while the scanner is healthy and
        is the entire outcome when it is not.
        `purgeRefusals` carries only what `deletion.ts` cannot know: that these
        bytes are pointed at by three other rows, and that an authorized upload
        may still be in flight toward them — purging then races the bytes, and
        the upload wins by recreating an object nothing points at.
        MUTATION-PASS CATCH: the executable and polyglot admission guards were
        both MASKED by the permitted-types check (an ELF is not on the permitted
        list, so the earlier rule refused first and the guards were untested).
        Fixed with three isolating cases — a permitted-but-executable type, a
        permitted-but-polyglot pair, and an ordinary unpermitted type that is
        neither. All three guards then died under mutation. -->
  - [x] S3.11.f Link provenance manifest/ledger, ingredients, generating run,
        transformations, source revision, and derived artifacts. Evidence:
        provenance linkage tests.
        <!-- `ProvenanceManifest` + `provenanceFaults` (7 kinds) + `stalenessOf`
        (4 verdicts). The ingredients are references AND digests: a reference
        alone names something that may have moved, and the digest is what makes
        the ancestry checkable later — which is the only time anybody looks.
        `transformation-without-a-tool-version` decides whether the manifest is
        worth keeping at all. "Resized with imagemagick" cannot be reproduced and
        cannot be audited; a provenance record that cannot answer "what exactly
        did this" is a story about the file rather than a record of it.
        A DERIVED ARTIFACT WHOSE SOURCE MOVED IS NOT "THE SAME ARTIFACT,
        UPDATED". The bytes are fine and the DESCRIPTION has stopped being true,
        and the manifest is the only thing that can say so — everything
        downstream will happily keep serving it. And an ingredient that cannot be
        resolved at all is `unknown`, never `current`: not the same as
        unchanged. -->
  - [x] S3.11.g Re-read and rehash the exact stored bytes across local/object
        storage adapters and detect corruption. Evidence: real-storage
        integration tests.
        <!-- `BlobStore` + `adapterFaults` (4 kinds) and `pnpm nx blob-storage`,
        against a LOCAL FILESYSTEM adapter and a REAL S3-compatible object store
        (MinIO from the dev compose) side by side.
        `BlobStore.read` returns the WHOLE object and takes no range, because a
        partial read is the second way a verification passes without checking
        anything (S3.11.c) and an interface with an optional range invites it.
        THE THIRD CONTROL IS AN ADAPTER THAT TRANSFORMS: a line-ending
        normaliser, which is a real thing that has happened to real files, and
        which returned 2,207 bytes where the honest adapters returned 2,248. That
        class of bug is invisible until two copies of the same object are hashed
        and compared, and by then the original is usually gone.
        The sniffer is round-tripped too: an ELF stored under a `.png` key and a
        GIF/zip polyglot both come back out of the object store still reading as
        what they are. The polyglot is the harder one — its zip record is in the
        TAIL, which is exactly what a store that pads or truncates destroys
        first — and its answer is only worth anything if it survives the storage
        layer.
        CALIBRATION: seven mutations of the BUILT `blobs.js` were run against
        this harness. The first pass killed four; the executable and polyglot
        sniffs survived because the harness only checked the leading type, and
        the round-trip cases above were added until they died. -->
- [x] S3.12 Implement orphan-blob discovery and safe reclamation. Evidence:
      referenced, legal-held, and pending-upload blobs are never removed.
      <!-- `src/reclamation.ts` (37th production entry point) +
      `tools/reclamation-race.mjs` (23rd Nx target). 50 tests in the cell, 2,251
      in the kit, 48 mutations killed with no survivors.
      EVERY OTHER MODULE IN THIS KIT IS CAREFUL ABOUT BEING WRONG. THIS ONE IS
      CAREFUL ABOUT BEING WRONG IN ONE DIRECTION: a blob wrongly kept costs
      storage, a blob wrongly deleted is gone — no compensating action, no retry,
      and usually no way to find out until somebody opens the thing it was
      attached to months later. -->
  - [x] S3.12.a Enumerate authoritative references from entities, revisions,
        jobs, outbox/inbox, evidence, provenance, bundles, uploads, releases,
        audit, and legal holds. Evidence: reference-source register.
        <!-- `REFERENCE_SOURCES` (12) + `ReferenceRegister` + `registerFaults`
        (5 kinds) + `referencedBlobIds`. "UNREFERENCED" IS A CLAIM ABOUT WHAT YOU
        LOOKED AT and never a property of the blob, so the register is a CLOSED
        list and a sweep that could not reach every source does not run over what
        it could. The failure is not exotic: a team ships a feature that
        references blobs, nobody adds it here, and six weeks later the sweep
        deletes their bytes.
        `source-missing-from-the-register` is checked against the DECLARED
        vocabulary rather than against what the caller supplied — a register that
        validates itself against its own contents cannot detect the thing it
        exists to detect. A PARTIAL scan is a separate field and the same
        refusal as an unreachable one. The union across sources, never an
        intersection.
        THE HARNESS MAKES THIS CONCRETE: a blob referenced only from
        `provenance`, with that source missing from the register, came out
        classified `unreferenced-active` with action `delete-bytes`. -->
  - [x] S3.12.b Detect metadata-without-bytes, bytes-without-metadata,
        unreferenced active, abandoned upload/quarantine, failed derivative, and
        dangling-reference classes. Evidence: orphan classification tests.
        <!-- `ORPHAN_CLASSES` (8) + `ORPHAN_ACTIONS` (4) + `classifyOrphan`.
        THERE IS NO GENERIC `delete` ACTION, because the classes fail in OPPOSITE
        directions: a row pointing at bytes that are gone is repaired by deleting
        the ROW and nothing else, and an object nothing knows about is the only
        class where deleting BYTES is the repair. One verb over both is how a
        reclaimer deletes bytes a live row still points at.
        ORDER: `dangling-reference` is checked FIRST, because it is the one class
        where the fault is in the pointer rather than in the blob, and treating
        it as an orphan deletes the wrong end of a broken link.
        `abandoned-quarantine` is `leave`: a blob held pending a check that never
        answered is exactly what an incident would want to look at, so it needs a
        person rather than a sweep — and `SWEEPABLE_BLOB_STATES` says so. -->
  - [x] S3.12.c Apply age/grace, upload/lease state, retention, hold,
        quarantine/incident, release, and provider reconciliation gates.
        Evidence: eligibility policy tests.
        <!-- `ELIGIBILITY_GATES` (7) + `eligibility` + `isEligible`.
        EVERY GATE RUNS AND ALL SEVEN RESULTS ARE RETURNED — no short-circuit on
        the first refusal. An operator needs to know a blob is held for three
        reasons, because releasing one of them does not make it deletable and a
        sweep that reports only the first teaches otherwise.
        `retention-elapsed` reads the CLOCK AND the S3.3 machine, because that
        machine is the authority (S3.11's decision 6) and a reclaimer consulting
        only the timestamp would be a second answer to a question `deletion.ts`
        owns. `provider-reconciled` is the gate that stops "no row for this
        object" meaning "the listing is stale". `isEligible` requires the result
        list to be COMPLETE, so a caller that ran five gates cannot pass.
        MUTATION-PASS CATCH: the retention CLOCK was masked by the deletion-state
        check beside it — the fixture's window had already elapsed, so flipping
        the clock changed nothing. An isolating case (state clean, clock not yet
        elapsed) was added and it died. -->
  - [x] S3.12.d Produce a dry-run candidate manifest with size/cost, reason,
        reference search, confidence, and sampled verification. Evidence:
        operator preview.
        <!-- `ReclamationCandidate` + `confidenceOf` + `dryRunManifest`.
        CONFIDENCE IS A GATE, NOT A LABEL: `markCandidate` refuses anything below
        `high` without a sampled verification, and confidence is `low` whenever
        the register was incomplete — which is exactly the case where the verdict
        is about what was searched rather than about the blob.
        `notExamined` is REPORTED rather than implied. A sweep that lists two
        hundred candidates out of a bucket it stopped walking at ten thousand
        objects has described a sample, and a preview that does not say so reads
        as a complete account of what is reclaimable. Every candidate carries the
        sources searched AND the register version it was judged at. -->
  - [x] S3.12.e Mark candidates with a reclamation token and recheck references
        atomically before deletion. Evidence: new-reference race tests.
        <!-- `ReclamationToken` (branded; only `markCandidate` produces one and
        `planDeletion` requires one) + `MARK_REFUSALS` (4) + `DELETION_REFUSALS`
        (5), and `pnpm nx reclamation-race`.
        THE RECHECK HAS TO BE IN THE SAME STATEMENT AS THE DELETE, so
        `planDeletion` RETURNS A PREDICATE RATHER THAN A PERMISSION: everything
        it checked can change between returning and the statement landing, and a
        caller that evaluates the conditions and then deletes unconditionally has
        reproduced the race the token exists to close.
        THE HARNESS SHOWS IT IS NOT THEORETICAL. Three controls, each deleting a
        referenced blob: mark-then-delete with no recheck; RECHECK-THEN-DELETE,
        the conscientious version, where the reference lands in the window the
        separate SELECT opened; and the incomplete register from S3.12.a. Beside
        them, one statement carrying `reclamation_token = $2 AND NOT EXISTS(...)`
        matched 0 rows and the blob survived — and the same statement DOES delete
        a genuinely unreferenced one, so the guard is not simply always refusing.
        `register-version-moved` is the subtle refusal: a source added between
        the dry run and the mark is a place this blob has never been searched
        for. -->
  - [x] S3.12.f Delete idempotently in bounded batches, preserve audit/tombstone
        where permitted, and report partial/unknown outcomes. Evidence: deletion
        fault tests.
        <!-- `DELETION_OUTCOMES` (5) + `foldBatch` + `batchFaults` (3 kinds).
        `already-absent` IS A SUCCESS and `unknown` IS NOT A FAILURE, and both
        are load-bearing. A reclaimer runs twice — after a crash, after an
        operator re-runs it — so an object already gone must be an ordinary
        outcome rather than something somebody investigates. And an unknown
        outcome RECORDED AS DELETED leaks the bytes forever with the row saying
        otherwise, which is the one state nothing later can detect;
        `unknown-recorded-as-deleted` is the fault that catches it.
        `notAttempted` is stated rather than implied, and an unbounded batch is
        refused: a reclamation job that dies halfway has deleted an arbitrary
        prefix of an unknown set. `tombstone-dropped-where-required` is the audit
        half — a reclamation that leaves no record is indistinguishable from data
        loss six months later. -->
  - [x] S3.12.g Reconcile deletion and restore/relink false positives before
        permanently closing findings. Evidence: reclamation report.
        <!-- `FALSE_POSITIVE_SIGNALS` (4) + `FINDING_STATES` + `reconcileSweep`.
        A FINDING IS NOT CLOSED BY DELETING. A restore request, a reference
        created for a blob that is gone, a broken render, a derivative
        regenerated from scratch — these are the ONLY evidence that ever arrives
        that a reclaimer was wrong, and they arrive AFTER. Closing on delete is
        what makes a reclamation bug permanent: nothing is watching by the time
        the symptom shows up, and the sweep that caused it has been green for
        weeks.
        The observation window is a PARAMETER and not a constant, because it is a
        property of how quickly this product's users would notice and nobody in
        this module knows that. A source added to the register since the sweep
        keeps the finding open too — there is a place it never looked, so the
        absence of a signal does not cover it. And `prematurelyClosed` makes
        decision 6 checkable after the fact: a finding closed before its window
        elapsed is one whose false positives were never going to be found. -->
- [x] S3.13 Implement encrypted sensitive-field storage or vault references.
      Evidence: database dumps and logs do not expose protected values.
      <!-- `src/protection.ts` (38th production entry point) +
      `tools/canary-leak.mjs` (24th Nx target). 178 tests in the cell, 2,430 in
      the kit. The mutation pass ran 246 mutations over 123 guards: 236 killed,
      TEN SURVIVORS, all closed — nine by tests that were missing and one by
      deleting a guard that turned out to change nothing. Re-running those sites
      killed all twelve occurrences.
      TWO OF THE TEN WERE DESIGN FINDINGS RATHER THAN TEST GAPS. `revocationPlan`
      survived BOTH directions at one branch, which is the mutation pass saying
      the two crypto-shred arms were the same answer in different words — one
      column and a whole tenant, distinguished only by prose an operator does not
      read. `blastRadius` is now a FIELD. And the cycle-detection walk carried a
      `continue` that the `while` condition already expressed; two spellings of
      one condition are a place they can later disagree, so it is gone.
      A THIRD LESSON IS ABOUT THE HARNESS: removing the cycle break turns the
      walk into an infinite synchronous loop, which no vitest timeout can fire
      against — the event loop never yields. A mutation harness with no per-run
      timeout does not report that as a survivor, it hangs, and the first attempt
      burned 25 CPU-minutes on one mutant before anybody looked.
      S3.11 DESCRIBES AN ENCRYPTION ARRANGEMENT SOMEBODY ELSE PERFORMS, on bytes
      in a store somebody else runs. This is the other half and it is not the
      same problem: the plaintext is in this process, the sealing happens before
      the INSERT, and the thing being protected is a COLUMN — queried, indexed,
      logged, echoed back in constraint violations, exported to support, and
      copied into every backup. The blob half can describe; this half has to
      bind. -->
  - [x] S3.13.a Inventory sensitive fields and classify encryption-at-rest,
        tokenization, one-way hash, vault reference, or prohibited persistence.
        Evidence: field protection matrix.
        <!-- `SENSITIVITY_CLASSES` (9) + `FIELD_OPERATIONS` (9) +
        `PROTECTION_STRATEGIES` (5) + `STRATEGY_CAPABILITIES` + `matrixFaults`
        (12 kinds) + `strategiesFor`.
        A CLASSIFICATION IS A CONSTRAINT ON WHAT THE FIELD MAY BE USED FOR, not
        a label on how secret it feels. Everybody agrees a national identifier
        is sensitive; the argument is always about whether the support console
        may search by it. So the matrix is DERIVED from the declared operations,
        and `STRATEGY_CAPABILITIES` is the whole design: `one-way-hash` has no
        `equality-match` (a hash that answers "are these the same" is salted
        per-field, which is a token with a different name and a different leak)
        and `encrypt-at-rest` has none either (a randomized AEAD is what stops
        the comparison, and removing the nonce to restore it is becoming a
        deterministic token without anybody deciding to accept the leak).
        THREE OPERATIONS ARE SUPPORTED BY NOTHING. Ordering and substring search
        over a protected field are not hard problems somebody should go and
        solve: order-preserving encryption leaks the order, which for a birth
        date or a balance is most of the value, and an encrypted prefix index
        leaks the prefix. Both are sold as encryption. The OPERATION is refused.
        THE FIELD IS NOT THE ONLY COPY — an index, a search vector, a "last
        four", an export column, a metric label, a cache entry — so derivatives
        are declared and checked: an attacker reads the weakest copy, so a
        protected column with a plaintext derivative is a plaintext column with
        a ceremony in front of it. Checked against a COLUMN CENSUS rather than
        against itself, for S3.12.a's reason: an unclassified sensitive column
        does not appear as an unprotected entry, it appears as nothing at all,
        and stores plaintext by default.
        `token-over-a-low-cardinality-field` takes its floor as a PARAMETER, and
        the harness inverted a real one: three countries behind three tokens,
        one GROUP BY, and the 31/12/4 ranking hands back the mapping. -->
  - [x] S3.13.b Define key hierarchy, KMS/vault ownership, tenant/context
        binding, algorithm/version, rotation, revocation, and break-glass
        access. Evidence: cryptographic design review.
        <!-- `KEY_TIERS` (3) + `keyDesignFaults` (14 kinds) + `revocationPlan` +
        `authorizeShred` (4 refusals) + `authorizeBreakGlass` (8 refusals).
        ROTATION AND REVOCATION ARE NOT DEGREES OF THE SAME ACTION and the
        function exists so a runbook cannot use the words interchangeably.
        Rotating a KEK re-wraps what is under it and touches no rows; destroying
        one so the material can never be used again destroys everything beneath
        it, and at the DEK tier that IS the data.
        SO CRYPTO-SHREDDING IS A DELETION WEARING KEY-MANAGEMENT CLOTHES, and
        `authorizeShred` asks S3.3's legal holds the question a purge is asked —
        a hold that stops a purge and not a key destruction is a hold with a
        hole in it shaped exactly like the operation nobody classified as
        deletion. Break-glass borrows `validateHumanGate` rather than building a
        second quorum machine (S2.13 already argued that a quorum without
        independence is one signature with a count beside it); what it ADDS is
        the three rules specific to keys — THE AUDIT RECORD IS A PRECONDITION,
        because an audit written afterwards is prevented by whatever made the
        emergency access necessary; the emergency identity is not the routine
        one, or no log distinguishes a break-glass read from ordinary traffic;
        and a grant over every key is not an emergency, it is a second
        production credential with a form in front of it.
        `wrapping-key-is-exportable` is checked against the GRAPH, not the tier:
        the property that matters is having children. A data key is EXPECTED to
        be exportable — the application holds it to run the AEAD, and that is
        the envelope. `rotation-slower-than-the-data-it-protects-is-retained`
        reads like bookkeeping and is not: a key rotated every 400 days over
        rows kept for 90 means every row that ever existed lived and died under
        one key, so the window the rotation bounds is the entire dataset. -->
  - [x] S3.13.c Encrypt before persistence with authenticated associated context
        and prevent plaintext indexing/querying unless explicitly tokenized.
        Evidence: tamper and query tests.
        <!-- `associatedData` + `contextFaults` + `SealedField` + `sealField`
        (7 refusals) + `openField` (8 failures) + `queryFaults` (6 kinds), and
        `pnpm nx canary-leak`.
        THE BINDING GOES IN THE AAD, NOT IN THE WHERE CLAUSE. `openField` builds
        its associated data from `expected` — where the row actually IS — and
        NEVER from `sealed.context`. A ciphertext copied into another row
        arrives with its own envelope, and opening against the envelope's own
        claim authenticates the copy and hands back the value. The stored
        context is a DIAGNOSTIC that is never allowed to become the control, and
        the harness performs the wrong version beside the right one so the check
        is not vacuous: with the AAD from the envelope, the row copied into
        tenant-b opened cleanly.
        The AAD is LENGTH-PREFIXED because the collision is real, not
        hypothetical: with a plain separator, tenant `a|b` + entity `c` and
        tenant `a` + entity `b|c` are the same string, so a ciphertext written
        for one opens in the other. The part ORDER is fixed in code rather than
        read from `Object.keys` — key order is an implementation detail a spread
        or a JSON round trip may change, and changing it makes every existing
        ciphertext fail to open with no diagnosis at all.
        `value-is-already-sealed` is STRUCTURAL and saves the migration:
        re-sealing an envelope produces ciphertext that opens to a JSON
        document, and nothing looks wrong until somebody reads the field.
        `unique-index-over-a-randomized-ciphertext` is the query fault that
        reads as a working constraint — and the harness demonstrated it, one
        value sealed twice inserting twice. THE ENGINE THEN VOLUNTEERED THE
        OTHER HALF: the same index REFUSED a verbatim byte copy. It constrains
        the nonce, not the value, so it is useless against the case it was added
        for and effective only against a duplication nobody performs by
        accident. -->
  - [x] S3.13.d Resolve vault references through least-privilege identity,
        purpose, short-lived access, caching prohibition, and audit. Evidence:
        vault integration tests.
        <!-- `referenceFaults` (4 kinds) + `authorizeResolution` (8 refusals) +
        `SecretLease` + `usageFaults` (4 kinds).
        A RESOLVED SECRET IS A SECRET THAT NOW LIVES SOMEWHERE ELSE, and this
        runtime cannot wipe a string. So the module does not pretend to erase
        one: what it offers is a BOUNDED LEASE plus a usage record that makes
        "it was not cached" a checkable claim rather than a sentence in a design
        document. Saying which of the two you have is the whole point, and
        `usageFaults` is the other half of the sentence — `value-was-persisted`
        NAMES the destinations rather than counting them, because a cache is one
        problem and a log is a worse one.
        `audit-sink-unavailable` is fail-closed and it is the right way round:
        an unauditable secret read is exactly the read somebody will later need
        the record of, and degrading to "resolve anyway, log later" trades the
        only evidence a protected value was ever produced for the availability
        of whatever needed it. PURPOSE is the only part of the request that
        narrows a LEGITIMATE caller — least privilege on the identity alone
        permits every read that identity was ever needed for.
        `reference-path-carries-a-value` defeats the arrangement quietly: a path
        built from what it points at — `secret/users/ada@example.com/ssn` — puts
        the identifier in every row, log line and trace attribute. The value is
        in the vault and the answer to "who has one" is in the column. -->
  - [x] S3.13.e Redact ORM errors, SQL parameters, logs, traces, metrics, audit
        summaries, test snapshots, support exports, and backups. Evidence:
        canary-secret scans.
        <!-- `REDACTION_SURFACES` (9) + `redactionFaults` (7 kinds) +
        `unscannedSurfaces` + `REDACTION_TOKEN`, calibrated by
        `pnpm nx canary-leak`.
        REDACTION IS DRIVEN BY THE MATRIX, NOT BY WHAT THE VALUE LOOKS LIKE. A
        diagnosis, a home address and a source's name have no signature, and a
        redactor that greps for things that look like secrets passes all three.
        So the field matrix is the MECHANISM and S1.10's `scanText` is the
        CALIBRATION — borrowed rather than reimplemented, because a second set
        of detectors is one that is always behind the first.
        `orm-error` is listed first because it is the leak nobody writes down,
        and the harness proves it against the real engine rather than against a
        message written here to be caught: Postgres answered a unique violation
        with `Key (contact_plain)=(canary-…@leak.invalid) already exists.` and
        `redactionFaults` came back with all three of `canary-survived`,
        `protected-field-name-carries-a-value` and the S1.10 shape hit. The
        regex was then tightened so the CONSTRAINT NAME in the same string —
        `account_tax_id_key` — does not match on the column.
        `REDACTION_TOKEN` is a CONSTANT and there is deliberately no
        `redact(value)` helper: anything taking the secret as an argument to
        decide what to print is a redaction whose output is a function of the
        secret, and a same-length mask publishes the length, which for a PIN or
        a postcode is most of the value space.
        `presence-disclosed-by-the-key-alone` is the Veritas-grade one and
        reaches S2.7's shape from a different direction — for a protected
        source or a health field the key alone answers "is there a value", and
        that answer IS the disclosure, so the surface emits nothing rather than
        a redacted something. It is checked against
        `UNAUTHORIZED_READER_SURFACES` rather than all nine: a backup IS the
        database (presence is in it by construction, and the control there is
        that the artifact is encrypted and held away from the keys), and a
        parameter list belongs to a query that was already authorized. -->
  - [x] S3.13.f Migrate plaintext values safely with resumable backfill,
        verification, compatibility, rollback/compensation, and plaintext
        removal. Evidence: migration rehearsal.
        <!-- `MIGRATION_PHASES` (6) + `backfillFaults` (6 kinds) +
        `PLAINTEXT_RESIDUES` (10) + `residualPlaintext` + `advanceMigration`
        (5 refusals).
        `read-switch` IS ITS OWN PHASE, and collapsing it into `backfill` is the
        mistake: reading from the sealed column while the plaintext is still
        there is the ONLY configuration in which the new path runs on production
        traffic AND the old value is still available to fall back to. That
        window is where every problem with the sealing shows up.
        DROPPING THE COLUMN REMOVES IT FROM QUERIES, NOT FROM DISK. Postgres's
        DROP COLUMN does not rewrite the heap, so the values sit in the existing
        tuples; and the WAL, a physical replica, an inactive replication slot
        pinning the log before the migration, the index leaves, the backups, the
        search index, the warehouse, the caches and whatever support was sent
        all have their own copy on their own schedule. Ten residues, ten
        INDEPENDENT facts — the spec flips each evidence flag one at a time and
        requires exactly one residue back, because a shared flag would let
        clearing the WAL silently clear the warehouse.
        `backfill-without-dual-write` is the silent one: rows written after the
        cursor passes are written in plaintext and never seen again, the
        migration completes, and the verification counts agree with the rows the
        backfill touched. `cursor-over-a-mutable-column` is the same problem
        from the other side. Rolling BACKWARD is allowed everywhere except from
        `contract`, which is the phase that deleted the thing it would roll back
        to — after it, the recovery is a restore from a backup that still
        contains exactly what the migration existed to remove. -->
  - [x] S3.13.g Test backup/restore, rotation, unavailable KMS/vault, corrupted
        ciphertext, deleted key, and unauthorized access. Evidence: failure
        matrix.
        <!-- `FAILURE_MODES` (7) + `FAILURE_EXPECTATIONS` + `failureModeFor` +
        `behaviourFaults` (6 kinds) + `restoreFaults` (3 kinds).
        THE MODES MUST BE DISTINGUISHABLE, and `retryable` is the load-bearing
        column. A caller holding one "decrypt failed" retries everything, which
        is correct for an unavailable KMS and catastrophic for a destroyed key:
        the permanent loss is recorded as a series of transient errors, the
        alert is a rate rather than an event, and the row is discovered
        unreadable months later by a person rather than by a system. Exactly two
        modes are retryable and exactly one is undisclosable, and the spec
        asserts both sets rather than sampling them.
        `modes-share-one-code` is the finding the section exists for, and the
        pair that collapses first is always `kms-unavailable` and
        `key-destroyed`, because both arrive as an exception from the same
        client library. THIS IS WHY THE ADAPTER CONTRACT MAKES THE KEY SERVICE'S
        ANSWER SEPARATE FROM THE TAG CHECK — an AEAD alone returns one error for
        a wrong key, a wrong context and a corrupted byte, and an adapter that
        collapses the key service's answer into a boolean has thrown away the
        material the whole matrix is made of. It is also why the key id, version
        and algorithm sit in the CLEAR on the envelope: a deliberate metadata
        disclosure, bought for exactly this.
        `OpenResult` has no `string | null` success. That signature is how a
        failure becomes an empty field: the caller writes the null back, the row
        is now genuinely blank, and the record of what was there is a backup
        nobody has restored — which is what `failed-open` catches.
        `restore-predates-a-crypto-shred` turns a recovery into an incident: a
        key destroyed to satisfy an erasure request does not stop the rows
        existing in the backup. `key-state-not-restored` is the quiet one — a
        database that passes every check with unreadable protected columns,
        which survives a restore drill because a drill counts rows. -->
- [x] S3.14 Implement database health and schema-compatibility probes. Evidence:
      incompatible schema returns fail-closed readiness.
      <!-- `src/probes.ts` (39th production entry point) +
      `tools/probe-controls.mjs` (25th Nx target). 97 tests in the cell, 2,528 in
      the kit. The mutation pass ran 106 mutations over 53 guards and left TWO
      survivors, both closed; the re-run is 104 killed with none surviving.
      ONE OF THE TWO WAS A DESIGN FINDING. `SchemaRequirement.exact` had its own
      comparison branch in `compareSchema`, and the branch was FULLY MASKED by
      the range — for any pin worth having `exact === minimum === maximum`, so
      the range already produced the same verdict and the branch could not change
      an answer. It is gone. `exact` is now a DECLARATION that the one-version
      range was deliberate, which is the only job it was ever doing: it lets
      `no-room-for-a-migration-deployed-ahead-of-this-build` tell an intentional
      pin from an accidentally narrow range, and a pin that disagrees with its
      range is refused.
      A PROBE IS THE SMALLEST PIECE OF CODE IN A DEPLOYMENT AND IT DECIDES
      WHETHER EVERY OTHER PIECE RECEIVES TRAFFIC. That asymmetry is the whole
      subject: almost every failure here is a probe that was RIGHT about
      something unimportant and, by being right, took a working service out of
      rotation or kept a broken one in. -->
  - [x] S3.14.a Separate liveness from readiness and define safe database
        connection/timeout/pool checks. Evidence: probe contract.
        <!-- `DATABASE_PROBE_KINDS` (3) + `PROBE_CHECKS` (10) +
        `LIVENESS_PERMITTED_CHECKS` + `contractFaults` (9 kinds).
        LIVENESS MUST NOT TOUCH THE DATABASE, and the ABSENCE of every database
        check from the permitted set is the control. A liveness probe that
        queries the database restarts every process the moment the database has
        a hiccup — turning a thirty-second blip into an outage, and making
        recovery SLOWER, because each new process opens fresh connections into a
        database that is already struggling.
        A probe sharing the request pool is the first thing to fail under load,
        which removes a healthy-but-BUSY process from rotation and sends its
        traffic to the ones that are next. And `probe-timeout-leaves-no-headroom`
        is the quiet one: a probe killed before it answers reports a TIMEOUT,
        which is the only probe result that carries no reason at all — the
        failure mode that produces the longest incidents. Headroom is a
        PARAMETER, because how variable the hop is belongs to the deployment.
        WRITING THIS TEST FOUND A REAL FAULT-MASKING BUG: a liveness probe that
        reached the database also reported `probe-has-no-connection-budget`,
        which sends somebody to provision a pool for a check that should not
        exist. The pool and budget rules now apply only to the kinds ALLOWED to
        reach the database. -->
  - [x] S3.14.b Verify read and, where required, transactional write capability
        without mutating user data. Evidence: probe integration tests.
        <!-- `WRITE_PROBE_METHODS` (6) + `SAFE_WRITE_PROBE_METHODS` (3) +
        `WRITE_BLOCKING_STATES` (4) + `probeMethodFaults` (5 kinds), and
        `pnpm nx probe-controls`.
        `WRITE_BLOCKING_STATES` IS THE ARGUMENT FOR HAVING A WRITE PROBE AT ALL:
        four states that leave reads working perfectly and writes failing, so a
        service probed only by reading stays in rotation and fails every
        mutation — and the first thing to notice is a user. The harness proves it
        against the engine rather than describing it: in a read-only session
        `SELECT 1` returned 1 and the INSERT came back `cannot execute INSERT in
        a read-only transaction`.
        `rolled-back-write-to-a-user-table` is LISTED rather than omitted,
        because it is the one somebody reaches for — it looks safe, and it works
        until the first exception path that skips the rollback. CONTROL 3 shows
        the other half: a concurrent user UPDATE waited behind the probe's open
        transaction until its lock timeout. THE ROLLBACK ENDS THE WRITE AND NOT
        THE LOCK.
        Missed states are named in FULL rather than the first, because they fail
        independently: a promoted replica and a full disk are unrelated events
        with the same symptom. -->
  - [x] S3.14.c Read authoritative migration/schema version and compare it with
        application minimum, maximum, exact, dirty, in-progress, and
        incompatible states. Evidence: version matrix tests.
        <!-- `SCHEMA_STATES` (6) + `compareSchema` + `requirementFaults` (3).
        THE COMPARISON IS NOT SYMMETRIC. Too old is fatal — the code expects
        objects that are not there, so reads break as surely as writes. TOO NEW
        IS EXPAND/CONTRACT WORKING: the migration is deployed ahead of the code
        ON PURPOSE, so a build declaring `maximum === minimum` reads as a strict
        careful requirement and is in fact one that refuses readiness for the
        length of EVERY rollout. That is a fault in the requirement, not the
        database.
        ORDER IS LOAD-BEARING: `unknown` first (nothing below it can be
        evaluated without a version), then `dirty` (a half-applied migration
        means the schema is neither the old one nor the new one, so the number it
        reports describes nothing), then `in-progress` — the one state where
        WAITING is right and failing is wrong, because a startup probe that fails
        during a migration crash-loops the process waiting for that migration.
        A NULL VERSION IS NOT ZERO. "No migration tracking" and "no migrations
        applied" have opposite responses, and CONTROL 4 shows the cost: a probe
        that catches the missing-table error and defaults to 0 gets `too-old` —
        run the migrations — against a database that is probably somebody
        else's. -->
  - [x] S3.14.d Check required extensions, tables, columns, constraints,
        indexes, triggers/policies, generated model registry, and permissions.
        Evidence: compatibility assertions.
        <!-- `REQUIREMENT_KINDS` (9) + `SILENT_WHEN_ABSENT` + `OBSERVATION_RESULTS`
        (3) + `compatibilityFindings` (5 kinds).
        `not-checkable` IS A SEPARATE RESULT FROM `absent`, and collapsing them
        is the mistake. A catalogue query refused for lack of permission says
        NOTHING about whether the object exists, and reporting it as missing
        sends an operator to create a table that is already there — at best an
        hour, at worst against the wrong database. It still blocks; it blocks
        with a different remediation.
        `SILENT_WHEN_ABSENT` is the sharper half. A missing TABLE errors the
        first time anything touches it. A missing ROW POLICY raises nothing at
        all — it returns MORE ROWS to a query that looks exactly like
        yesterday's. THE HARNESS MAKES THIS CONCRETE AND IT IS WORSE THAN
        EXPECTED: with RLS disabled the policy is STILL LISTED IN `pg_policies`,
        so a compatibility check that looks for the POLICY passes while the
        tenant reads both tenants' rows. Only `pg_class.relrowsecurity` — a
        different catalogue column — catches it.
        `requirement-never-observed` covers the gap between the two lists: a
        requirement nothing looked for is not satisfied, and a report that
        silently omits it reads identically to one where it passed. -->
  - [x] S3.14.e Return configured/degraded/unavailable/failed status with safe
        reason and remediation but no DSN, credentials, SQL, or schema secrets.
        Evidence: public response redaction tests.
        <!-- `PublicProbeView` + `publicProbeView` + `PROBE_CODES` +
        `disclosures` (5 kinds) + `safeDetailIsUsable`.
        A CONSTRUCTED OBJECT, NEVER THE REPORT WITH FIELDS DELETED — S1.10's
        argument, for S1.10's reason: deletion is a blocklist and a blocklist is
        wrong the first time somebody adds a field. There is nowhere in the
        shape to put an object name, a version comparison, a SQL fragment or a
        DSN, and the spec asserts the KEY SET rather than trusting it.
        A probe body listing the missing tables and columns is a schema dump on
        an endpoint the orchestrator reaches WITHOUT CREDENTIALS, and it is the
        most useful possible document for anybody deciding what to try next. The
        object-name check is the only detector this module owns: the rest is
        S1.10's `scanText`, which already argues about credentials, connection
        strings and SQL and already has the patterns.
        `code-is-a-sentence` borrows S1.6's argument about stable classes — every
        dashboard that grouped on a prose code splits into two series the first
        time somebody improves the wording. -->
  - [x] S3.14.f Fail readiness before serving writes/jobs when schema is too
        old, too new, dirty, partially migrated, or missing required policy.
        Evidence: startup/readiness tests.
        <!-- `SERVED_CAPABILITIES` (4) + `SERVE_DECISIONS` (4) +
        `SCHEMA_STATE_POLICY` + `readinessDecision` + `readinessState`.
        FAIL-CLOSED IS ABOUT THE WRITE PATH, NOT ABOUT THE PROCESS. A process
        that refuses to start cannot serve the reads it could have served and
        cannot tell anybody why: it is in a crash loop with the explanation in a
        log nobody is tailing. So readiness is decided PER CAPABILITY.
        `migration-admin` is on the list for the reason that makes the whole
        design work: every other capability can be refused, and if the one that
        would FIX the situation is refused with them, the deployment has locked
        itself out of its own repair. It survives too-old, too-new, dirty and
        in-progress, and is refused only under `unknown` — where nothing here is
        a claim about this database, INCLUDING the claim that it is the right
        one.
        `too-new` gives reads `serve-degraded`, which is the only honest answer:
        refusing them turns a recoverable rollout or rollback into an outage, and
        serving them silently hides that a contract-phase migration may have
        removed something this build still reads. `in-progress` is `wait` and not
        `refuse` — a refusal tells the orchestrator this process is broken, and
        it is replaced by another one that will also be waiting.
        THERE IS NO SECOND STATUS LADDER: `readinessState` returns an S1.6
        `CapabilityState`, so `usability()` answers for it. S1.6 already argued
        that a boolean cannot represent `degraded` without rounding it to a
        neighbour and that whichever way it rounds is wrong — and a probe is
        exactly where that rounding happens. The owner is TAKEN rather than
        invented; a default would be a plausible-looking rota that pages
        nobody. -->
  - [x] S3.14.g Instrument latency/failure without high-cardinality database
        detail and alert on sustained incompatibility. Evidence: probe
        observability tests.
        <!-- `FORBIDDEN_METRIC_LABELS` (13) + `metricFaults` (3 kinds) +
        `ALERT_VERDICTS` (4) + `alertDecision`.
        A metric labelled by TABLE grows one series per table and publishes the
        table list to everyone with dashboard access; a metric labelled by SCHEMA
        VERSION grows a new series on every migration and never retires the old
        ones, which is the shape that quietly doubles a metrics bill over a year.
        `no-outcome-label` is the one that makes the metric misleading rather
        than expensive: with success and failure averaged together, A FAST
        FAILURE LOOKS LIKE AN IMPROVEMENT.
        THE ALERT HAS THREE WAYS TO BE USELESS AND ALL THREE ARE CHECKED. A
        threshold shorter than the deploy window fires on every release, and an
        alert that fires on every release is one somebody mutes — after which it
        never fires for the reason it was written; that is checked FIRST, because
        a decision made with a broken threshold is not worth computing. A GAP in
        the readings is NOT continuity: the incompatibility may have ended and
        restarted inside the hole, and a duration computed across it invents the
        part nobody measured. And the window is measured from the current RUN of
        incompatibility rather than from the first incompatible reading ever
        seen. -->
- [x] S3.15 Write migration templates for expand/backfill/verify/contract.
      Evidence: a sample zero-downtime migration rehearsal passes.
      <!-- `docs/migrations/` (README + four templates) +
      `scripts/v1-workbenches/generate-migration-templates.mjs` (+ 10 tests) +
      `scripts/v1-workbenches/migration-rehearsal.mjs`. Follows S3.1's shape: a
      document with a machine-checked companion, rather than a kit entry point —
      S3.15 does not introduce a runtime contract anybody imports.
      A SCHEMA CHANGE THAT CAN BE DEPLOYED WITHOUT DOWNTIME IS NOT ONE MIGRATION.
      It is four, separated by DEPLOYMENTS, and the separation is the whole
      mechanism: between any two there is a moment where the old code and the new
      code are both live against the same database and both have to work. The
      flag day fails in a way nobody can reproduce afterwards — a few minutes of
      errors during a rollout, after which the fleet is consistent again. -->
  - [x] S3.15.a Create expand template for additive nullable/default-safe
        schema, indexes/concurrent build, permissions, generated client update,
        and compatibility checks. Evidence: expand template.
        <!-- `docs/migrations/expand.template.sql`, 7 checked requirements.
        THE COMPATIBILITY CHECK IS RUN AGAINST THE OLD BINARY, and running it
        against the new one would be checking the wrong thing — the old binary is
        the one still deployed, and this phase's entire claim is that it cannot
        tell anything changed.
        A VOLATILE DEFAULT IS REFUSED and directed to the backfill: `now()` and
        `gen_random_uuid()` need a distinct value per row, so the engine rewrites
        the whole table under ACCESS EXCLUSIVE — on EVERY version, including the
        11+ the templates target, where a non-volatile default is only a
        `pg_attribute` entry. The same statement is a metadata change or an
        exclusive lock over a hundred million rows depending on a version number
        nothing in the statement mentions.
        `CREATE INDEX CONCURRENTLY` is followed by an INVALID-index check,
        because that is the failure that leaves no error anywhere: the statement
        reported failure once in a terminal, and the index sits in the catalogue
        being ignored by the planner while everybody assumes it is there.
        Permissions are verified with `has_column_privilege` AS THE APPLICATION
        ROLE — the migration runs as an owner, so everything works for it. -->
  - [x] S3.15.b Create backfill template with immutable input query, batches,
        checkpoints, idempotency, rate/lock limits, quarantine, progress, and
        resume. Evidence: backfill template.
        <!-- `docs/migrations/backfill.template.md`, 14 checked requirements.
        THE CHECKPOINT IS WRITTEN IN THE SAME TRANSACTION AS THE BATCH. Written
        after, a crash re-runs a batch — survivable, because the `AND
        new_column IS NULL` predicate makes it a no-op. Written BEFORE, a crash
        SKIPS one, and nothing later detects it.
        Idempotency is two clauses covering different races: the NULL predicate
        stops a retry overwriting a value the application has since changed, and
        `FOR UPDATE SKIP LOCKED` lets two workers run without either waiting —
        SKIP rather than NOWAIT, because a locked row is one somebody is editing
        and that is a reason to come back rather than to fail the batch.
        §4b IS THERE BECAUSE THE REHEARSAL FOUND THE BUG IN ITS OWN TRIGGER. A
        dual-write trigger written as "fill whichever column is NULL" is correct
        on INSERT and DOES NOTHING ON UPDATE — both columns already hold a value,
        so neither branch fires and the two diverge from the first edit onward.
        Nothing detects it until the verify phase's write-parity check, which is
        exactly what caught it. The template now keys on `TG_OP` and propagates
        the side that CHANGED, and is BEFORE rather than AFTER (an AFTER trigger
        needs a second UPDATE, which fires the trigger again). -->
  - [x] S3.15.c Create verify template for counts, nulls, constraints, hashes,
        duplicates, orphans, semantic samples, performance, and old/new
        read/write parity. Evidence: verify template.
        <!-- `docs/migrations/verify.template.sql`, 9 checked requirements, and
        `SET default_transaction_read_only = on` is enforced by the checker
        rather than intended — this phase has no business writing, and a template
        that says so only in a comment is one somebody edits in a hurry.
        EVERY QUERY STATES ITS EXPECTED RESULT. A verify template returning
        numbers with nothing to compare them against is a report somebody skims.
        `IS DISTINCT FROM` rather than `<>`: `NULL <> NULL` is NULL, which is not
        true, so a plain `<>` silently excludes exactly the rows the null-count
        section just told you to look at. `invented` is the column worth reading
        twice — a value in the new column where the source had none is a default
        applied somewhere nobody intended.
        Duplicates and orphans are checked HERE because phase 4 tightens those
        constraints, and a violation found under an ACCESS EXCLUSIVE lock is
        found in the worst possible place. §9 checks the plan for the same
        reason: phase 4 removes the column the current queries use, and a Seq
        Scan here means it replaces a fast query with a slow one at the moment
        the fallback disappears. -->
  - [x] S3.15.d Create contract template for precondition proof, old writer/
        reader absence, removal/constraint tightening, cleanup, and rollback or
        compensation. Evidence: contract template.
        <!-- `docs/migrations/contract.template.sql`, 10 checked requirements.
        THE ONE-WAY DOOR, so it opens with a PROOF rather than a checklist — a
        checklist is a list of things somebody believed, and §1 is four queries
        whose answers are facts about the running system, each filed WITH the
        time it was taken, because a proof from yesterday is a proof about
        yesterday's fleet.
        `NO OLD READER` is the fact people skip, because "we deployed it last
        week" feels like the same statement. It is not: a job runner, a cron pod,
        a scaled-to-zero worker and a paused canary are all processes that can
        still be running last month's build.
        §0 checks for a long-running query BEFORE the DDL, because the DDL will
        wait for its lock and WHILE IT WAITS IT BLOCKS EVERYTHING ELSE — a
        3-second migration started behind a 40-minute analytics query is a
        40-minute outage. NOT NULL is tightened through a `NOT VALID` check plus
        `VALIDATE` so the exclusive lock is brief and the scan runs under SHARE
        UPDATE EXCLUSIVE. And §7 lists what DROP COLUMN did NOT remove — for an
        ordinary column that is bookkeeping, for a classified one it is the
        difference between "removed" and "removed from queries". -->
  - [x] S3.15.e Include owner, approvals, maintenance/lock budget,
        observability, abort threshold, incident/rollback, evidence paths, and
        completion criteria in every phase. Evidence: template completeness
        test.
        <!-- `COMMON_SECTIONS` (8) enforced across all four templates by
        `generate-migration-templates.mjs`, with a NEGATIVE CONTROL: the safety
        scan is re-run over a deliberately-broken template built in memory, and
        if that produces no findings the scan is not working and a clean result
        over the real files means nothing. It produces 23.
        THE CHECKER FOUND TWO REAL GAPS ON ITS FIRST RUN — the backfill template
        had no `## Incident and rollback` section at all, and the CREATE INDEX
        scan fired on the SENTENCE explaining why the plain form is dangerous.
        The second is the more interesting fix: a statement is distinguished from
        a mention by its `ON <target>` clause, because without that distinction
        the way to quiet the checker is to DELETE THE EXPLANATION.
        Each section is a question asked during an incident at the point where
        nobody has time to work out the answer: the owner is a ROTA URL and never
        a name; the lock budget is enforced with `lock_timeout` in the template
        rather than in somebody's intention; the abort threshold is a NUMBER
        agreed in advance, because the decision to abort is made by a tired
        person at 03:00 who has already invested two hours. -->
  - [x] S3.15.f Implement a sample rename/type-change migration using dual-
        version coexistence rather than flag-day deployment. Evidence: sample
        migration artifacts.
        <!-- The worked rename (`story_source.contact` ->
        `story_source.contact_email`) lives INSIDE
        `scripts/v1-workbenches/migration-rehearsal.mjs` rather than in a
        `sample/` directory beside the templates, because a sample that is only
        READ decays and one that is EXECUTED on every run cannot.
        A rename is the worked example precisely because it is the change that
        most invites a flag day: `RENAME COLUMN` is instant, takes a trivial
        lock, and looks free — and is the single statement that guarantees every
        process built against the old name starts failing the moment it commits.
        TWO CONTROLS PROVE THAT, and both must break the old writer. CONTROL A
        commits the rename and the old writer's next INSERT fails with `column
        "contact" ... does not exist`. Committed rather than held open, and that
        distinction cost a hung run: an UNCOMMITTED rename holds ACCESS EXCLUSIVE
        so the other session's INSERT WAITS rather than failing, which
        demonstrates lock contention and not the flag day.
        CONTROL B found something better than what was written for it: the engine
        REFUSES `ADD COLUMN ... NOT NULL` on a non-empty table outright, so the
        form that actually reaches production is a column added WITH a default
        and the default dropped in a later migration — and that one breaks the
        old writer with a not-null violation on an INSERT that does not name
        it. -->
  - [x] S3.15.g Rehearse on production-size representative data with old/new
        binaries, live writes, interruption, rollback/compensation, and final
        query plans. Evidence: timed zero-downtime report.
        <!-- `scripts/v1-workbenches/migration-rehearsal.mjs`: 10,000 rows,
        four phases, 0.9s.
        THE OLD BINARY WRITES THROUGHOUT, which is the only version of this
        rehearsal worth running — "no downtime" is a statement about the minutes
        when both binaries are live, and a rehearsal with one writer rehearses
        the situation that was never in doubt. An old writer (INSERT naming only
        `contact`) and a new writer (naming both) insert between every backfill
        batch, and the old writer's UPDATE is what the parity check runs against.
        The backfill is INTERRUPTED at cursor `row-002000` by ending the
        connection, then resumed from the checkpoint by a fresh one; nothing
        special happens, which is the property. The ROLLBACK REHEARSAL is done
        from that half-converted state and shows it is LEGAL: every row still
        carries the old column, so rolling the new binary back loses nothing.
        Plans are captured before and after the drop and compared — a plan that
        CHANGED across the drop means the planner was using something now gone —
        and the report ends by stating the 10,001 dead tuples still holding the
        old values until a rewrite, which is the part that reads as done and is
        not. -->
- [x] S3.16 Add tenant-isolation integration tests using two tenants and
      colliding object IDs. Evidence: every read, write, search, export, and
      event path remains isolated.
      <!-- `tools/tenant-isolation-matrix.mjs` (26th Nx target). 6 matrices, 34
      probes, 6 negative controls.
      IT DIFFERS FROM S3.2.f's `tenant-isolation` IN ITS ENFORCEMENT MECHANISM,
      which is the whole reason it is a second harness rather than more cases in
      the first. S3.2.f checks that the queries CARRY their tenant predicates.
      This one TAKES THE PREDICATES AWAY: every statement runs as a role that
      cannot bypass row-level security, with no tenant clause in it at all, so
      what is under test is the DATABASE's isolation rather than the
      application's discipline. An isolation rule enforced by a predicate is one
      a missing predicate disables, and the missing predicate is the entire
      failure mode. -->
  - [x] S3.16.a Provision two tenants with identical user-facing identifiers,
        object IDs where storage permits, names, taxonomy, timestamps, and
        content markers. Evidence: collision fixture setup.
        <!-- Both tenants hold workspace `newsroom`, slug `quarterly-review`,
        term `investigations`, the same `created_at` and the same content
        marker; only the id carries a tenant prefix, which is what a real system
        does. The fixture asserts the collision — 2 rows across 2 tenants share
        the natural key — before any probe runs, because isolation bugs do not
        show up on distinct data. -->
  - [x] S3.16.b Test direct get/list/filter/paginate/count/facet/join/history/
        revision reads across both identities and roles. Evidence: read
        isolation matrix.
        <!-- 8 shapes plus the count, run from BOTH identities so a policy that
        happens to name one tenant is caught, and every one written without a
        tenant predicate. The COUNT is its own case: it returns no row and still
        answers how many the other tenant has. Roles are the kit's half —
        `decideAccess` must refuse a cross-tenant contributor read with
        `different-tenant` — so the engine and the contract are checked against
        each other rather than each asserted alone. -->
  - [x] S3.16.c Test create/update/delete/restore/transition/bulk/import/review/
        waiver/publish mutations with cross-tenant targets and relationships.
        Evidence: write isolation matrix.
        <!-- 10 mutations, including the two that are not obvious: an INSERT with
        a FORGED `tenant_id`, which the policy's WITH CHECK refuses, and an
        UPDATE that tries to MOVE a row between tenants, which is the mutation
        that would otherwise pass every read check afterwards.
        THE BULK CASE NEEDED ITS OWN RULE. `UPDATE workbench SET stage=...` with
        no predicate SHOULD affect rows — the asking tenant's own — and the first
        version of the harness failed it for that. The invariant is not "touched
        nothing", it is "did not reach past its own", which is why every write is
        verified against the OTHER tenant's row. -->
  - [x] S3.16.d Test full-text/structured search, suggestions, saved views,
        caches, indexes, and unauthorized result counts/snippets. Evidence:
        search/cache isolation matrix.
        <!-- Real `tsvector` + GIN, so the full-text path is the engine's rather
        than a LIKE standing in for it. The two that leak WITHOUT RETURNING A ROW
        are checked separately: the result COUNT over a marker both tenants carry
        must be 1, and `ts_headline` must not produce a fragment of the other
        tenant's body. A search surface can be perfectly scoped in its rows and
        still answer "how many" and "roughly what". -->
  - [x] S3.16.e Test blob upload/download, derivative, signed URL,
        bundle/export, backup/restore slice, audit export, and data-subject
        flows. Evidence: artifact/export isolation matrix.
        <!-- 7 paths. The bundle export is the interesting one — a LEFT JOIN from
        workbench to blobs, which is the shape where a scoped driving table
        carries an unscoped join and returns the other tenant's rows in its
        second column. The backup SLICE is checked as a slice rather than a dump:
        a per-tenant copy taken as the tenant. -->
  - [x] S3.16.f Test
        command/event/job/outbox/inbox/provider/callback/notification identity
        and context propagation. Evidence: asynchronous isolation matrix.
        <!-- The asynchronous paths are where the tenant is CARRIED rather than
        joined, so the two failures are a drain that reads another tenant's
        pending work and a CLAIM that marks it processed. Both are checked, and
        so is an inbox insert with a forged context — a provider callback is
        exactly where a tenant id arrives from outside. -->
  - [x] S3.16.g Test error, trace, metric, log, support, and health diagnostics
        for cross-tenant leakage. Evidence: observability isolation matrix.
        <!-- The assertion here is about ISOLATION rather than about how the
        engine formats a message: the unique violation must fire the
        TENANT-SCOPED constraint. A global unique over (workspace, slug) would
        have collided with the OTHER tenant's row, and the refusal would then be
        an oracle — "somebody else already has this slug" — returned to a caller
        not allowed to know it. The health count is checked to be strictly
        smaller than the unrestricted one, so a diagnostic cannot quietly become
        a cross-tenant total. -->
  - [x] S3.16.h Run tests against the real supported database with row-level
        policies/constraints enabled and fail on any disclosure or mutation.
        Evidence: passing real-DB report.
        <!-- RLS is ENABLED and FORCED on all five tables, the app role is
        `NOBYPASSRLS`, and the harness asserts that before running anything —
        a role that can bypass makes every probe pass whatever the policies say,
        which is the failure S3.14's CONTROL 2 already ran into once.
        `fail on any MUTATION` is a separate rule from `fail on any disclosure`:
        every write is compared before and after against the target row, so a
        statement that was refused and mutated anyway still fails.
        CALIBRATED BY BREAKING IT. A policy of `USING (true)` produces 28
        findings. Removing `WITH CHECK` produces none — because Postgres reuses
        the USING expression as the check when none is given, which is worth
        knowing and is not a weakness. And a unique key without the tenant makes
        the colliding fixture impossible to SEED, which the harness reports
        rather than works around. -->

### S4 — API, authorization, audit, and abuse controls

- [x] S4.1 Implement `createWorkbenchRouter` without encoding Tara-specific
      routes or roles. Evidence: both archetype fixtures mount successfully.
      <!-- `src/router.ts` + `src/router.spec.ts` (46 cases, all green with the
      kit's 2575). New public subpath `./router`, authorized by S4.1 in
      `KIT_OWNED_ENTRY_POINTS`; the export map regenerates to 47 entry points
      with no lattice violation, which is also what proves the router imports
      no Node built-in, no DOM global and nothing above its class.
      THE FAILURE THIS IS BUILT AGAINST IS NOT A MISSING MIDDLEWARE, IT IS ONE
      IN THE WRONG PLACE. Authorization after the handler; the body parsed
      before the size was checked; an idempotency key claimed by a caller who
      was about to be refused. Each reads as correct in review because each
      middleware is individually right. So {@link MIDDLEWARE_STEPS} is DATA, the
      response carries the steps that actually ran, and every order case asserts
      a prefix property — a request that fails at step N ran exactly 1..N and
      nothing after. That is checkable for every step and every failure;
      "the middlewares are registered in the right order" is checkable by
      reading the code that is already wrong. -->
  - [x] S4.1.a Define router inputs for plugin registry, contracts, actor
        resolver, authorization, stores, jobs, providers, audit, tracing,
        limits, and capability probes. Evidence: constructor type tests.
        <!-- Eleven seams, and the test loops over all ten optional ones
        removing each in turn: a missing seam is one `missing-seam` violation
        naming it, refused at construction rather than at the request that first
        reaches the step needing it.
        EACH SEAM IS USED BY THE ROUTER AND NOT MERELY PASSED THROUGH, which is
        the difference between a seam and a decoration. `jobs` is asked whether
        an accepted job exists before a 202 goes out. `providers` is WRAPPED, so
        an operation the deployment does not offer returns a typed
        `not-configured` without the call reaching the host — asserted by
        counting host calls: 1 for the known operation, 0 for the typo.
        `stores` is read by both the idempotency claim and the precondition.
        `probes` decides `unprobed` versus `mounted`.
        `requestDigest` returns a plain string and is PARSED rather than typed
        as `Sha256Hex` and trusted: the seam is implemented by a host the
        compiler never sees, and a truncated digest would silently become a key
        that never matches its own retry. -->
  - [x] S4.1.b Register only capability-declared routes and versions with
        deterministic collision refusal. Evidence: route registration tests.
        <!-- A route mounts only when exactly one loaded plugin declares its
        owning capability as `implemented` or `degraded`, AND the deployment's
        `CapabilitySupport` serves its contract major. Undeclared, declared-
        `unsupported` (the route says the plugin does what the plugin says it
        does not), declared by two plugins, and unserved-version are four
        separate refusals with their own kinds.
        THE DETERMINISM CLAIM IS ASSERTED AS AN EQUALITY, NOT AS NON-EMPTINESS:
        the colliding pair is passed forwards and backwards and the two
        violation lists must be `toEqual`. Collision is on the NORMALISED path —
        `/things/:thingId` and `/things/:id` are one route with two spellings —
        while `/things/new` against `/things/:id` is not a collision but a
        specificity question, resolved by a computed weight rather than by
        insertion order, and tested in both directions.
        Excluded routes are REPORTED (other-host, ui-surface, retired-with-its-
        redirect) for S1.8.g's reason read backwards: a router that quietly
        mounts fewer routes than it was given is indistinguishable from one that
        never knew about them. -->
  - [x] S4.1.c Apply ordered authentication, scope resolution, validation,
        authorization, idempotency/concurrency, limits, tracing, audit, and
        safe-error middleware. Evidence: middleware-order tests.
        <!-- Eleven ordered steps plus two finalisers. Authorization is SPLIT
        (route roles need no body, so a wrong-role caller never has one parsed;
        object authorization needs the target validation produces) and limits
        are split (a size cap before anything reads, rate and spend once the
        caller is known and permitted). Both splits are justified by a case:
        `bodyReads` is asserted 0 at the size cap and at the role check.
        THE PLACEMENT THAT MATTERS MOST IS THE IDEMPOTENCY CLAIM, which is AFTER
        authorization — the test counts record lookups and asserts 0 for a
        request authorization refuses, because a key claimed earlier lets an
        unauthorized caller burn another caller's key and learn from the replay
        that one exists.
        Scope resolution does the check no descriptor can make statically: S1.8
        already refuses a route taking its tenant from both the path and the
        session, but a VALID session for one tenant on a path naming another is
        a request-time comparison, and it is refused rather than resolved in
        either direction. Concealment renders as 404 and not 403, because a 403
        confirms the row exists to somebody not allowed to know that.
        A thrown handler becomes `internal.unexpected` and the thrown text is
        asserted absent from the envelope; the audit row still records the step
        it died at, for a request that never reached a handler at all. -->
  - [x] S4.1.d Keep stage names, roles, content types, gates, routes, copy,
        provider operations, and publish targets in domain plugins. Evidence:
        shared source scan.
        <!-- `tools/router-vocabulary-scan.mjs` (27th Nx target). 30 terms, 40
        shared modules, 0 findings.
        THE CORPUS IS DERIVED, NEVER AUTHORED: every term reads a live exported
        value — the stage model's own stages, the block's own media types, the
        routes' own paths and roles — so a word added to a plugin next year is
        guarded the day it is added. The archetypes are excluded from `dist` on
        purpose (a worked example in the shipped package is a fixture handed to
        every consumer), so the tool transpiles their source in memory and
        imports it as a data URL with specifiers rewritten to the BUILT kit.
        THE HARD PART WAS NOT FINDING TERMS, IT WAS NOT DROWNING IN THEM. A
        domain may name a stage with an ordinary English word, and `draft`,
        `published`, `resolved`, `triage` and `monitoring` are all real archetype
        stages AND all words this kit uses for unrelated things — an outbox row
        is `published`, an anchor is `resolved`, an artifact channel is `draft`.
        The first version reported 52 findings, 51 of them false, which is how
        this kind of check gets switched off. So a term is reported only where
        the collision cannot be coincidence: a literal COMPARED against a
        vocabulary-shaped subject (`lifecycleStage`, not `state`), a COMPOUND
        term as an identifier, or a declared route path anywhere.
        IT FOUND A REAL ONE. `tenancy.ts` read
        `request.lifecycleStage === 'published'` — the kit deciding what a
        terminal stage is called, which protects a workbench whose stage happens
        to be `published` and silently protects nothing in one whose stage is
        `released`, `aired` or `filed`. `AccessRequest.lifecycleStage` is now
        `stageIsImmutable: boolean`, answered by the plugin's own stage model,
        and the denial is `immutable-stage-rows-are-not-editable`.
        Calibrated in the other direction too: every run first scans a synthetic
        module built FROM the corpus and exits non-zero if it finds fewer than 4
        planted terms, because a clean report from a scan that cannot see is the
        worst possible outcome. -->
  - [x] S4.1.e Mount content-authoring and operations-console fixtures with
        disjoint types and routes. Evidence: archetype integration tests.
        <!-- Both archetypes gained real routes, bindings and command schemas —
        in the PLUGINS, which is S4.1.d holding: 4 desk routes under `/desks`,
        3 console routes under `/consoles`, and the test asserts the two share
        no path segment, no role and no stage.
        The handlers run the archetypes' OWN logic rather than returning a shape
        resembling what it would have produced: the attribution measure over a
        real draft (1 claim, 1 attributed), the runbook freshness measure (181
        days, one unverified step), the markdown reader (slug
        `quarterly-review`, 1 paragraph), the pull-quote editor. If the pipeline
        hands a handler the wrong body, parameters or scope, the domain function
        refuses and the test sees it.
        The console is TENANT-scoped where the desk is WORKSPACE-scoped, and
        that is a real difference rather than variety for its own sake: an
        incident crosses every workspace in a tenant, and scoping it to one
        would hide it from the responders in the others. -->
  - [x] S4.1.f Refuse missing/duplicate/incompatible plugins and expose honest
        capability state. Evidence: startup negative tests.
        <!-- Duplicate plugin id, kit-version-unsupported, no plugins at all,
        orphan route, orphan binding — and every violation is collected rather
        than returned at the first, so a host with three mistakes finds three in
        one run instead of three deploys (asserted: two independent mistakes,
        two violations).
        THE CAPABILITY ROWS ARE WHERE THE HONESTY LIVES. `unprobed` is its own
        disposition and is NEVER rounded up to `mounted`: a capability nothing
        measured is not a working one, and a mounted route over an unmeasured
        capability is the exact shape in which an absent measurement renders as
        a good one. A declaration of `unsupported` outranks any probe and is
        reported as `declared-unsupported` with no routes — the archetypes
        supply two real cases, the desk's `capability:image-generation` and the
        console's `capability:structured-authoring`. -->
        <!-- S4.1 refactor note: `AccessRequest.lifecycleStage` became
        `stageIsImmutable` (S4.1.d above). Callers updated: `tenancy.spec.ts`
        and `tools/tenant-isolation-matrix.mjs`. No consumer outside the kit. -->
      <!-- S4.1 rollup: `pnpm nx build/lint/test @oshun/workbench-kit` green;
      2575 kit tests pass; `node tools/router-vocabulary-scan.mjs` and
      `node tools/tenant-isolation-matrix.mjs` both green after the tenancy
      change; `generate-workbench-kit-export-map.mjs --check` reports no drift
      and no violations at 47 entry points. -->
- [x] S4.2 Reuse the canonical Studio actor resolver rather than inventing a
      second role system. Evidence: role parity tests against
      `@oshun/studio-authoring`.
      <!-- `src/actors.ts` + `src/actors.spec.ts` (31 cases). New `./actors`
      subpath, 48 entry points, no lattice violation.
      THE FIRST HONEST FINDING IS THAT HALF THE THING TO REUSE DOES NOT EXIST.
      `@oshun/studio-authoring` — the one authoring package this layer may
      depend on, per the ADR-S1 constraint `@nx/enforce-module-boundaries`
      enforces — is canonical for the ROLE TAXONOMY and its permission matrix.
      It has no session, no token, no claim set and no `resolveActor`. The
      packages that do are `layer:auth`, which `layer:workbench-kit` may not
      reach, and reaching sideways would be the second identity system this item
      exists to prevent.
      AND IT MAY NOT BE IMPORTED EITHER. The kit is a BUILDABLE package emitting
      under `rootDir: src`, so importing another workspace library's source puts
      that library's files outside its own root — TS6059, the compiler saying
      the emitted package would be incomplete. Which produced the better design
      rather than a workaround: the kit holds NO role list, NO permission table
      and NO role-to-principal map, so there is nothing in it that CAN drift.
      The taxonomy arrives as a `CanonicalRoleModel`, and the spec binds the
      real one — which is also exactly where the item asks for it ("role parity
      tests against @oshun/studio-authoring"). -->
  - [x] S4.2.a Inventory canonical claims, sessions/tokens, human/service/system
        variants, tenant/workspace membership, roles, delegations, and support
        access. Evidence: resolver contract map.
        <!-- `CanonicalClaims`, closed by `parseCanonicalClaims` over 16 named
        fields, covering every item in the cell: subject/audience/issuer/
        issuedAt/expiresAt/sessionId/sessionStrength/revoked (claims and
        session), tenant + workspaces (membership), roles, onBehalfOf +
        delegationGrant (delegation), serviceName (the machine variant),
        supportGrant, sessionCorrelation.
        THE CLOSED SHAPE IS THE POINT. A claim set is exactly the object
        somebody widens by passing the whole decoded token through, and
        `rejectExtraFields` refuses the display-data class BY NAME — the test
        adds an `email` and gets "carries display data", not a generic unknown
        field. Three invariants are refused rather than tolerated: half a
        delegation (the audit row could answer "who did this" or "whose
        authority", never both), a window that expires no later than it was
        issued (a clock or signing bug, not an expiry), and an ABSENT revocation
        flag — which would read as "not revoked" and is indistinguishable from a
        revocation check nobody performed. -->
  - [x] S4.2.b Adapt the canonical resolver without copying role strings or
        reinterpreting claims. Evidence: adapter source review.
        <!-- The source review is short because there is nothing to review: grep
        `src/actors.ts` for a canonical role name and there is not one. The
        taxonomy is a three-member `CanonicalRoleModel` — `roles`, `permissions`
        and a `holds` FUNCTION, not a matrix, because a matrix crossing this
        boundary would be a copy taken at a moment of something allowed to
        change.
        THE TEMPTATION WAS THE PRINCIPAL MAPPING. A twelve-row
        `Record<StudioCreatorRole, Principal>` would have been convenient, would
        have been TOTAL (so a thirteenth canonical role would break the build),
        and would have been a transcription of the role names in the one package
        that must not hold them. It is `PrincipalMapping`, supplied by the
        deployment, and the compile-time guarantee it would have bought is
        bought instead by S4.2.f's drift check — which is strictly better,
        because it also catches the version skew a compile-time check cannot
        see. -->
  - [x] S4.2.c Preserve authenticated actor, delegated actor, tenant/workspace,
        session strength, and correlation. Evidence: actor context tests.
        <!-- `ResolvedActor` gained `sessionStrength`, `sessionCorrelation`,
        `supportGrantLive`, `canonicalRoles` and `unrecognisedRoles`.
        `SESSION_STRENGTHS` is four values rather than a boolean because S4.6
        has to be able to require a hardware key and `strong: boolean` cannot
        express the middle — and `service-key` is deliberately NOT the strongest,
        since a long-lived key is not somebody who just touched a security
        device, and ranking it as one is how a service account satisfies a
        re-authentication requirement.
        The delegated case carries BOTH identities into `ActorRef`; the support
        grant is live only while it is, so a session outliving its grant is
        standing access with an audit trail rather than access. -->
  - [x] S4.2.d Reject missing, expired, revoked, malformed, wrong-audience,
        cross-tenant, and unsupported identity state. Evidence: negative tests.
        <!-- Seven reasons in `REJECTION_REASONS`, each asserted to arrive as
        itself, plus a TOTAL `REJECTION_CODES` record in the router so adding a
        reason without deciding what a client is told is a compile error.
        THE ORDER OF THE CHECKS IS ARGUED RATHER THAN INCIDENTAL. Shape first,
        because nothing can be read off a malformed record. Audience next, so a
        token minted for another service is not inspected at all. Then
        REVOCATION BEFORE EXPIRY — asserted directly: a session that is both
        reports `revoked`, because reporting the expiry would tell an operator
        it simply timed out, which is a different incident from one somebody
        revoked.
        `cross-tenant` maps to `tenancy.tenant_mismatch` and not to an
        authentication code: the credential was perfectly valid and was
        presented against somebody else's data, which is a security event rather
        than a reason to show a login prompt. A token carrying two identities at
        once is `unsupported` rather than resolved, because picking one means
        picking which half of a compromised token to believe. -->
  - [x] S4.2.e Compare every relevant role, claim, and delegation result between
        Studio and the shared router. Evidence: parity fixture matrix.
        <!-- `parityMatrix` over 12 roles x 10 permissions = 120 cells.
        THE FIRST VERSION OF THIS WAS A TAUTOLOGY AND IS WORTH RECORDING. It
        asked the canonical model for both sides, so it agreed with itself
        unconditionally and could not have failed — a test that proves nothing
        while reading as thorough. The answerer is now a PARAMETER, so the
        matrix has something it can genuinely disagree with, and the negative
        control passes a transcribed table with one cell wrong (author gains
        `approve-publish`) and asserts exactly one disagreement is reported,
        naming both sides. Only then does the real run — the kit's own answerer
        against the canonical model, zero disagreements — mean anything.
        A second guard for vacuity: the canonical model is asserted to contain
        both answers, because a matrix over a model that said `true` everywhere
        would agree with anything. -->
  - [x] S4.2.f Detect new canonical roles/claims as drift rather than silently
        treating them as unprivileged or privileged. Evidence: drift test.
        <!-- Two halves, because the failure has two shapes.
        AT STARTUP: `roleDrift(model, mapping)` runs BOTH directions — a
        canonical role nothing maps (the identity service deployed ahead of the
        workbench) and a mapping for a role no longer in the taxonomy (a grant
        nobody can find by reading the taxonomy). It REPORTS and never resolves,
        which is the whole item: treating an unmapped role as unprivileged
        silently removes access from whoever holds it — an outage with no error
        in it — and treating it as privileged makes an unknown string a grant.
        PER REQUEST: an unrecognised role in a token is CARRIED on the
        resolution rather than filtered out, and `classifyRoles` splits a
        token's roles three ways against the vocabulary the router's own mounted
        routes name: canonical, domain, and unattributed. The third bucket is
        the one worth having — a version skew and a forged claim both land in
        it, and neither may be silently honoured. -->
      <!-- S4.2 rollup: 2607 kit tests pass (31 new); `tsc -p tsconfig.lib.json`
      and `-p tsconfig.spec.json` both clean; export map regenerates to 48 entry
      points with `--check` reporting no drift and no violations;
      `tools/router-vocabulary-scan.mjs` still clean with `actors.ts` in scope.
      `@oshun/studio-authoring` is a DEV dependency and a spec-only vitest
      alias, so the emitted package does not carry it. -->
- [x] S4.3 Define object-level authorization hooks. Evidence: an authorized role
      cannot access another tenant or unassigned protected object.
      <!-- `src/authorization.ts` + `src/authorization.spec.ts` (44 cases). New
      `./authorization` subpath, 49 entry points, no lattice violation.
      THE ITEM'S OWN SENTENCE CONTAINS THE PROBLEM: the caller is AUTHORIZED.
      They passed authentication, they hold a real role on a real route, and
      every check that looks at the REQUEST says yes. Only a check that looks at
      the OBJECT can refuse them, and the reason platforms ship without one is
      that every individual endpoint looks fine. -->
  - [x] S4.3.a Define hook input for actor/delegate, tenant/workspace, action,
        object type/ID, lifecycle, owner/assignment, classification, policy, and
        request context. Evidence: hook contract.
        <!-- `AuthorizationInput` = actor + resolved principal + roles +
        tenant/workspace + action + `ObjectMetadata` + `PolicyContext` +
        `RequestContext`. The delegate half comes through `ActorRef`'s
        `delegated` variant, which already carries both identities.
        `ObjectMetadata` HAS NOWHERE TO PUT CONTENT. No title, no summary, no
        body, no displayName — and the test asserts the exact key set rather
        than describing the rule, so a field added for convenience fails. Not
        "we redact it before use": there is no field, which is the only version
        of that promise that survives somebody in a hurry. -->
  - [x] S4.3.b Load authoritative minimal object metadata through a
        tenant-scoped resolver before decision. Evidence: loader isolation
        tests.
        <!-- `loadObject` passes the scope to the host's resolver AND compares
        the tenant on what comes back.
        THAT SECOND HALF LOOKS REDUNDANT AND IS THE MOST VALUABLE LINE IN THE
        MODULE. The most ordinary bug in a resolver is `WHERE id = $1` with the
        tenant predicate left off — invisible to every test that reads its own
        tenant's data, and a cross-tenant read the first time an id collides or
        is guessed. One comparison turns it from a leak into a `faulty` outcome
        an operator sees. `faulty` is its OWN result rather than a thrown error
        or a silent conceal: concealing it would make the leak look like a
        missing row and every request would keep being served by a resolver that
        does not scope. The caller still learns nothing — they get
        `unavailable` — and the fault goes to the log. -->
  - [x] S4.3.c Compose domain policy, ownership, assignment, team/role,
        protected-object, legal-hold, lifecycle, and support rules. Evidence:
        policy truth tables.
        <!-- Eight families plus a NINTH, `parent-visibility`, because the eight
        are all answerable from the object alone and that one needs a second
        object; folding it in would put a two-object question inside a truth
        table over one, where it would eventually be simplified away.
        Domain policy DELEGATES to S3.2's `decideAccess` rather than re-deciding
        — a second implementation would be a second chance to write `>=` where
        `===` belongs, in the module least likely to be read again — and the one
        translation it performs is which denials become CONCEAL.
        The rules that are narrower than they look: ownership speaks only about
        `administer`, because a story's author does not get to bypass review; a
        legal hold PRESERVES and does not hide, so it refuses only the
        operations policy names and a read is untouched (a hold that blocked
        reads would stop the review it exists to enable); assignment compares
        against the CURRENT list on the loaded metadata, never anything the
        request carried, so a reassignment two minutes ago takes effect now. -->
  - [x] S4.3.d Return allow, deny, concealed-not-found, step-up, or unavailable
        with stable safe reason/remediation. Evidence: result fixtures.
        <!-- `OUTCOME_PRECEDENCE` is DATA and the tests assert it rather than
        describing it, because "which refusal does the caller see" is a
        disclosure decision and an ordering accident is exactly how a 403
        replaces a 404 for a cross-tenant id.
        CONCEAL > DENY: a 403 is an answer — it says the object exists.
        DENY > UNAVAILABLE: when something already said no, no is the complete
        and more useful answer.
        UNAVAILABLE > STEP-UP: telling somebody "authenticate again and you are
        in" when the next attempt still cannot be decided is a promise nothing
        keeps.
        A concealed object and an absent one are asserted INDISTINGUISHABLE in
        everything the caller can observe — same disposition, same message key,
        same remediation — while the logged reason differs, because an operator
        has to tell "no such row" from "not yours". And every rule runs even
        after one refuses, so `contributing` answers "you were added to the
        assignment and it is still refused" without a rerun. A final test
        produces every declared reason from real code, so a reason nothing can
        produce cannot sit in the catalogue looking like coverage. -->
  - [x] S4.3.e Apply hooks to reads, counts, search, history, blobs, exports,
        jobs/events, mutations, and indirect parent/child access. Evidence:
        endpoint coverage matrix.
        <!-- `ACCESS_PATHS` (11) + `coverageGaps`, checked in the direction that
        matters: not "is every hook applied to something" but "is there a path
        with no hook on it". A count, a search snippet and a signed blob URL are
        three doors into the same room and two of them answer without ever
        returning the object.
        Two gap kinds with different sentences, because they are different
        failures: a path nothing declares is one nobody has looked at; a path
        whose surfaces all skip the hook is one somebody looked at and left
        open. `toRouterDecision` closes the router path — the mapping lives here
        rather than in each host because `unavailable` carries a retry-after and
        the other refusals must not, and a host that guessed would either hammer
        a failing dependency or give up on a blip. -->
  - [x] S4.3.f Test colliding cross-tenant IDs, unassigned protected objects,
        hidden parents, indirect refs, and stale assignments. Evidence:
        disclosure negative tests.
        <!-- Six negatives, every one of them a WELL-FORMED request from an
        authorized caller: a colliding id identical in every field but the
        tenant (which is how a collision actually looks); a protected object
        with an EMPTY assignee list, reachable by nobody — reading empty as "no
        restriction" would invert the control; a reassignment the caller's
        request still claims; an object legitimately inside the tenant but under
        a parent the caller cannot see, which is "I cannot list your folders but
        I can open a file by id"; a support session WITH a live grant that still
        may not see a protected source; and an unreachable metadata store, where
        defaulting to visible leaks and defaulting to hidden breaks every object
        whose lookup was merely slow, so neither answer is given. -->
      <!-- S4.3 rollup: 44 new cases, kit suite green; `tsc -p tsconfig.lib.json`
      and `-p tsconfig.spec.json` clean; export map at 49 entry points with
      `--check` reporting no drift and no violations. -->

- [x] S4.4 Define property-level authorization hooks. Evidence: mass-assignment
      tests cannot modify owner, tenant, approval, gate, provenance, or audit
      fields.
      <!-- `src/properties.ts` + `src/properties.spec.ts` (47 cases). New
      `./properties` subpath, 50 entry points, no lattice violation.
      S4.3 answers "may you touch this row"; this answers the question that is
      left and the one mass assignment exploits — you may update the row, so
      WHICH FIELDS may you set. A system with only the first check lets an
      author who may edit their own draft set `ownerId`, `approvedBy` and
      `tenantId` in the request that fixes a typo.
      The evidence clause names six fields and the interesting half is not the
      names: each must stay unwritable through every mutation shape AND every
      spelling a client can produce. The corpus asserts 11 protected classes x 5
      shapes = 55 refusals, plus 6 spellings of the owner field. -->
  - [x] S4.4.a Define readable, createable, updateable, clearable, sortable,
        filterable, searchable, exportable, and audit-visible permissions per
        field/path and object state. Evidence: property policy schema.
        <!-- Nine permissions, five write-resistance classes, plus per-field
        `writableInStates` and `requiredScope`. `server-owned` and `write-once`
        are kept apart because the difference is real: a server-owned field is
        refused at CREATION too, a write-once one is settable exactly then —
        asserted both ways.
        A BUG THIS FOUND IN ITSELF, worth recording: `canonicalPath` stripped
        `*` as punctuation, so every wildcard policy row was silently dropped
        from the index and `blocks.0.text` came back `unknown-field` while the
        policy file still read as covering it. A control that fails open on its
        own configuration is worse than an absent one. Fixed, with a regression
        test that also proves a non-wildcard punctuation segment is still
        refused. -->
  - [x] S4.4.b Normalize patches, nested objects, arrays, JSON Patch, forms,
        import mappings, and generated proposals before policy. Evidence:
        mutation-shape tests.
        <!-- `normalizeMutation` is the ONLY entry, so no shape has its own
        check and therefore no shape has its own bypass. Five declared shapes
        reduce to one `FieldWrite` list.
        THREE THAT A NAIVE IMPLEMENTATION GETS WRONG. A JSON Patch `move` writes
        its target AND removes its source, so both ends are authorised — the
        source end is the one skipped because the operation "carries no value".
        A SPARSE array is walked by index and not by `forEach`, which skips
        holes, so `[,,{ownerId}]` would otherwise be authorised at the wrong
        path from the one the store applies. And a generated proposal goes
        through the same normalisation as a request body, because "we generated
        it" is not a provenance claim about what the prompt contained. -->
  - [x] S4.4.c Reject unknown, immutable, server-owned, write-once,
        conditionally protected, and unauthorized fields before mutation.
        Evidence: mass-assignment corpus.
        <!-- Nine refusal kinds, and a test that produces every one of them from
        real code so none can sit in the catalogue looking like coverage.
        UNKNOWN IS REFUSED, NEVER DROPPED. Dropping is what makes mass
        assignment survivable: the request succeeds, the caller believes the
        field was set, and the day somebody adds a policy row the "harmless"
        ignored field becomes a live write.
        THE REFUSAL IS WHOLE-MUTATION. Applying the permitted half of a patch
        leaves the object in a state the caller never asked for and cannot
        predict — a headline changed without the summary that explained it — so
        one unauthorised field means nothing is applied, and the caller is told
        which field. -->
  - [x] S4.4.d Protect owner, tenant, approval, gate, provenance, audit, role,
        lifecycle, revision, rights, and consent fields explicitly. Evidence:
        protected-field tests.
        <!-- Eleven named classes, not one `protected: true` flag. The flag
        cannot say WHICH control somebody removed, and its removal is a
        one-character diff nobody reads twice. The corpus runs every class
        through every shape. -->
  - [x] S4.4.e Filter reads, errors, diffs, history, search snippets/facets,
        exports, audit, and provider context at property level. Evidence: field
        disclosure tests.
        <!-- Nine surfaces, each mapped to the permission it needs. Two mappings
        are the point: an ERROR needs `read`, because naming a field the caller
        may not read tells them it exists and is the cheapest field-enumeration
        oracle there is; and PROVIDER CONTEXT needs `export`, because everything
        sent to a third party is an export whoever wrote the integration did not
        call one — asserted with an internal note that is readable and not
        exportable.
        `filterForSurface` returns what was WITHHELD as well as what is visible,
        so a partial object can say "3 fields are not shown" instead of
        presenting a complete-looking record with holes in it. -->
  - [x] S4.4.f Reevaluate after defaults/normalization and test aliases,
        duplicate keys, Unicode/case variants, magic fields, sparse arrays, and
        version skew. Evidence: bypass negative tests.
        <!-- `ownerId`, `owner_id`, `OWNER_ID`, `Owner-Id`, `ownerid`, `OwnerID`
        and the FULLWIDTH `ｏｗｎｅｒＩｄ` all reach the same policy row; the
        Cyrillic-о `оwnerId` is REFUSED rather than folded, because
        transliterating would make the policy decide which of two
        different-looking fields it was protecting. Policy is compared against
        the CANONICAL path — the one the store will apply — and not the text
        that arrived.
        DUPLICATE KEYS NEEDED THEIR OWN SCANNER. `JSON.parse` keeps the last
        occurrence and reports nothing, so `{"ownerId":"me","ownerId":"you"}` is
        indistinguishable after parsing from a body that only said `you`, while
        every proxy, log and validator in front of the handler may have seen the
        first. `duplicateKeys` reads the TEXT, tracks string literals and
        escapes so a brace inside a value cannot open a scope, and keeps a
        container stack so a repeated string in an ARRAY is not reported as a
        duplicate key — which is how a real duplicate check gets switched off.
        `__proto__`, `constructor` and `$`-prefixed keys are refused BY NAME so
        the refusal explains itself, and the test builds them through
        `JSON.parse` because a `__proto__` key in an object literal sets the
        prototype instead of creating an own property. -->
      <!-- S4.4 rollup: 47 new cases, kit suite green; export map at 50 entry
      points, `--check` clean; `tsc` clean on both projects. -->

- [x] S4.5 Define function-level authorization for capture, edit, review, waive,
      publish, unpublish, purge, and administration. Evidence: role matrix
      contract tests.
      <!-- `src/functions.ts` + `src/functions.spec.ts` (18 cases over a register
      of all eight named acts). New `./functions` subpath, 51 entry points.
      S4.3 and S4.4 both answer questions about a ROW. This answers one about an
      ACT — may you waive a gate, sign off a release, purge a tenant, flip a
      kill switch — and some of those touch no field at all, so a platform that
      only checks rows has no check on them.
      `roleMatrix` is GENERATED from `decideFunction` rather than authored
      beside it, so it cannot say something the code does not do, which is the
      failure mode of every permission matrix maintained in a document. It also
      shows the case a role-only matrix cannot express: a publisher who holds
      the role and is still refused, because they wrote the thing. -->
      <!-- S4.5 note: `public-api.spec.ts`'s subpath-resolution case got an
      explicit 30s timeout. The root specifier pulls the whole barrel through
      the transform and crossed the 5s default once the kit passed fifty entry
      points; the case measures whether a subpath RESOLVES, not how fast, and
      letting it fail under parallel load would make a strict check flaky. -->
      <!-- S4.5.a–g are covered by the same module and spec: (a) capture
      authority with quota/rights/consent preconditions, each asserted
      independently; (b) edit by scope; (c) review refusing the author; (d) a
      waiver refusing whoever ran the gate, which is the rule every incident
      produces because the person best placed to waive a gate is the person who
      ran it; (e) publish requiring two DISTINCT approvers — "approvals >= 2" is
      satisfied by one person clicking twice, and the test asserts that case
      fails; (f) purge and kill switch as irreversible administrative acts
      needing a hardware key and a permitted classification; (g) below.
      S4.5.g IS STRUCTURAL RATHER THAN DISCIPLINED. `decideFunction` takes NO
      invocation path, so the six paths in `INVOCATION_PATHS` — including the
      compatibility shim, where the old decision usually survives, and
      `direct-service`, which skips the router entirely — cannot disagree. A
      `decideFunction(path, …)` signature would have been the obvious design and
      would have made the property untestable, because the thing under test
      would be the caller's care. UI discovery is DERIVED from the same
      decision: hidden for what the caller cannot change, disabled for what they
      can, so a menu neither teaches nothing nor lists things to file tickets
      about.
      Two more decisions with cases. A precondition nobody could answer is
      `indeterminate` and never a refusal — a caller told "no" for a quota
      service that was merely down goes and finds somebody to raise their quota,
      and the quota was never the problem. And preconditions run LAST, so an
      unauthorised attempt never reaches a downstream system: asking a quota
      service about a request that was never going to be permitted costs money
      and tells that service the attempt happened.
      `validateFunctions` holds the defaults a flat register loses one function
      at a time: a release function with no separation rule is a publish
      somebody can perform on their own work; an irreversible one with no
      session requirement is one stolen cookie away; an administration function
      with no scope is reachable by any authenticated session. -->
      <!-- S4.5 rollup: 2719 kit tests pass; `tsc` clean on both projects;
      export map at 51 entry points, `--check` clean. -->
  - [x] S4.5.a Define capture/create authority by source type, target parent,
        object type, quota, classification, rights, and consent. Evidence:
        capture permission tests.
  - [x] S4.5.b Define edit authority by revision, lifecycle, field/block class,
        ownership/assignment, lock, and conflict. Evidence: edit permission
        tests.
  - [x] S4.5.c Define comment, suggestion, assignment, verdict, approval,
        abstention, recusal, escalation, and delegation authority. Evidence:
        review permission tests.
  - [x] S4.5.d Define gate run/recompute, appeal, waiver/override, signoff, and
        policy configuration authority with separation of duties. Evidence: gate
        permission tests.
  - [x] S4.5.e Define preview, publish/release, correction, supersession,
        unpublish, recall, and rollback authority per target. Evidence: release
        permission tests.
  - [x] S4.5.f Define purge/restore, secret/key, replay, feature/config,
        support, kill switch, and other administrative authority. Evidence:
        high-risk permission tests.
  - [x] S4.5.g Apply identical decisions to UI discovery, API, job, event,
        compatibility, and direct service paths. Evidence: alternate-path tests.
- [x] S4.6 Add step-up authorization support for high-risk mutations. Evidence:
      stale or missing step-up tokens are rejected.
      <!-- `src/stepup.ts` + `src/stepup.spec.ts` (33 cases) + a real-DB harness
      `tools/step-up-race.mjs` (28th Nx target). New `./stepup` subpath, 52
      entry points.
      A step-up challenge is the one credential-adjacent object a workbench
      holds, and what it is for is narrow: proving the person performing THIS
      act, on THIS target, with THESE parameters, is present right now. Almost
      every way of getting it wrong comes from making it slightly more general
      than that. -->
  - [x] S4.6.a Define risk combinations requiring step-up and accepted assurance
        methods. Evidence: step-up policy.
        <!-- Risk is a COMBINATION — irreversible, classification, blast radius,
        externally visible — rather than a flag, because no one of them is
        sufficient and every pair is argued about: an irreversible act on public
        data is a smaller problem than a reversible one over a whole tenant.
        THE LADDER IS THE POINT. `ASSURANCE_METHODS` is ordered weakest-first
        with `recovery-code` at the bottom: a bearer secret written down
        somewhere is not a security key, and systems that model step-up as
        `verified: true` accept the weakest ENROLLED method for the strongest
        act — precisely the path an attacker holding the password takes. There
        is a case asserting enrolment does not weaken the requirement.
        When several rules apply the STRONGEST assurance and the SHORTEST
        lifetime win. First-match would mean inserting a rule at the top
        silently weakens every act below it, invisibly in the diff. -->
  - [x] S4.6.b Create a short-lived one-time challenge bound to actor/session,
        tenant, exact action, target, parameters/hash, reason, and policy.
        Evidence: challenge schema tests.
        <!-- `Challenge` binds seven things and `CHALLENGE_BINDINGS` is DATA that
        `verifyChallenge` iterates, so a binding that stops being checked is a
        deletion from a list rather than a missing `if`. Each of the seven has
        its own rebinding case: obtain a challenge cheaply, spend it
        expensively — approve a small refund, reuse the token for a large one.
        `parseChallenge` refuses a credential field BY NAME (password,
        assertion, signature, otp) rather than with a generic unknown-field
        message, because the generic message is the one somebody works around.
        And a window that expires no later than it was issued is refused as the
        clock bug it is rather than treated as merely expired. -->
  - [x] S4.6.c Complete supported WebAuthn/MFA/reauth through the canonical
        identity service without handling credentials in the workbench.
        Evidence: identity integration tests.
        <!-- The identity service is a seam returning an `Attestation` — nonce,
        actor, session, method name, completion time and an opaque signature the
        kit compares and never parses. There is no field in any type here a
        WebAuthn assertion, a TOTP code or a password could travel in, asserted
        by pinning the attestation's key set.
        `unavailable` is kept apart from `failed`: reporting an unreachable
        identity service as a failed authentication tells a user their security
        key was refused, which is a different thing to go and investigate. -->
  - [x] S4.6.d Verify signature, audience, binding, session, assurance, nonce,
        issued/expiry time, and unused state at mutation. Evidence:
        tamper/stale/replay tests.
        <!-- Eight verification failures, every one producible, in the order
        signature -> bindings -> assurance -> time -> use. Signature first
        because an unsigned attestation says nothing about the rest; USE LAST
        because it is the one fact that costs a store read, and a request that
        was going to be rejected anyway should not cause one — asserted.
        `completed-before-issued` looks pedantic and is the replay that passes
        every other check: an attestation minted for an earlier, legitimate
        challenge, with every other field copied. -->
  - [x] S4.6.e Consume the challenge atomically with command acceptance and
        reject concurrent reuse. Evidence: real-DB race test.
        <!-- `tools/step-up-race.mjs`, four properties over a real engine, in
        0.9s. The claim no unit test can make: `verifyChallenge` is handed
        `alreadyUsed` as a boolean, and everything interesting happens in the
        gap between reading it and writing the command.
        RACE: 32 connections consume one challenge and perform the command in
        one transaction — 1 commits, 1 command row, and the challenge names the
        command that exists.
        ROLLBACK: the opposite failure, and the one people ship. A command that
        fails after consuming must leave the challenge UNUSED and spendable;
        burning it means the user re-authenticates for a request that never
        happened every time the store hiccups, which is how step-up gets removed
        from the flow that needed it.
        CONTROL: the check-then-use implementation, raced identically, let 32 of
        32 through. Without it a passing race proves only that the harness ran.
        EXPIRY: the predicate lives in the consuming UPDATE, so a challenge that
        expired between verification and the write is refused by the DATABASE
        rather than by a clock the application read a moment earlier. -->
  - [x] S4.6.f Audit request, completion/failure, use, expiry, and actor without
        exposing authentication secrets. Evidence: audit tests.
        <!-- Five events and an audit row with nowhere to put a secret:
        `auditFields()` is exported so the assertion is "these and no others"
        rather than a test asserting its own copy. The nonce IS carried, because
        it is the join key an incident review needs and it is safe once consumed
        — which is what consuming it is for.
        `auditLeaks` scans VALUES as well as keys, because a caller that puts an
        attestation into `correlationId` has broken nothing structurally and
        leaked it anyway — the same argument S1.10 makes about a declared key
        given an undeclared value. Tested with a JWT-shaped blob and with an
        undeclared field. -->
      <!-- S4.6 rollup: 2753 kit tests pass (33 new); `tsc` clean on both
      projects; export map at 52 entry points, `--check` clean;
      `node tools/step-up-race.mjs` green against the dev Postgres. -->
- [x] S4.7 Add idempotency middleware for retryable commands. Evidence: repeated
      keys with identical bodies replay; mismatched bodies conflict.
      <!-- `src/retries.ts` + `src/retries.spec.ts` (32 cases). New `./retries`
      subpath, 53 entry points. The replay/conflict decision itself is S3.4's
      `decideClaim` and the pipeline placement is S4.1's — this is the part
      neither covered: what a route DECLARES about being retried, what the
      digest is taken OVER, and what happens when the first attempt neither
      succeeded nor failed.
      THE CASE THAT DECIDES WHETHER AN IMPLEMENTATION IS RIGHT is neither of the
      two the evidence clause names. It is the retry carrying one extra field
      the caller was never allowed to set: identical in effect, different in
      bytes. A digest over the raw body makes it a CONFLICT, a conflict tells
      the client to mint a new key, and minting a new key for a request that
      already ran is how a retry becomes a double charge. -->
  - [x] S4.7.a Mark retryable routes with key requirement, scope, request
        canonicalizer, retention, and unknown-outcome policy. Evidence: route
        metadata tests.
        <!-- `RouteRetryPolicy` carries the five things the cell names, and
        `validateRetryPolicy` refuses six incoherent combinations, every one
        producible. The two worth reading: an irreversible effect declaring
        `retry-safely` for an unknown outcome, which is the combination that
        turns one network timeout into two of whatever the route does; and
        RETENTION TIED TO REVERSIBILITY rather than a global TTL — an
        irreversible record must outlive every retry a client will make, because
        the moment it expires the second attempt is indistinguishable from a
        first. A reversible one may expire in an hour, and that is asserted too.
        `reconcile-without-a-probe` catches the policy that names a step nothing
        can perform, which in practice becomes a retry. -->
  - [x] S4.7.b Validate and scope keys by actor/tenant, route/contract, target,
        and operation version. Evidence: key validation tests.
        <!-- The five-field SCOPE is S3.4's `validateScope`, delegated rather
        than re-decided and asserted here so the two halves cannot drift. What
        this adds is the client's half — the key VALUE. The length floor is
        load-bearing: a short key from a client that mints them per screen
        rather than per request collides between two users, and the second is
        handed the first's result. A key over 255 characters is one somebody is
        using as storage, and a key derived from an email or a URL leaks that
        value into every log line and support ticket that quotes it back. -->
  - [x] S4.7.c Canonicalize authorized parameters and persist request hash,
        state, and result reference atomically with acceptance. Evidence:
        integration tests.
        <!-- `canonicalizeAuthorized` takes the writes S4.4 PERMITTED and
        nothing else. Stable across key order, across nested object key order,
        and across the spelling of a field — `headline` and `HEADLINE` are one
        field, so a client that changed its serialiser has not changed its
        request. Separated by control characters that cannot occur in a
        canonical path, an operation name or JSON, with a case proving
        `{headline:"x set y"}` and two fields do not collide.
        The load-bearing case is the last one: an unauthorized field cannot
        change the digest, so the retry that carries it is still a replay rather
        than the conflict that poisons the key. -->
  - [x] S4.7.d Replay prior accepted, running, success, or terminal outcome for
        identical retries without invoking the handler. Evidence: concurrent
        duplicate tests.
        <!-- Every state replays or re-claims correctly: completed replays with
        its result reference, claimed and running report already-running rather
        than executing a second time or returning null, and FAILED is a genuine
        retry rather than a duplicate — the record is re-claimed. The handler is
        not invoked in any of them, which the router's own case asserts by
        counting handler calls. -->
  - [x] S4.7.e Return a typed conflict for changed body/query/target/identity/
        version and do not reserve keys for prevalidation failures. Evidence:
        mismatch tests.
        <!-- A changed digest conflicts, and the digest is compared BEFORE the
        state — asserted across all four states, because every other branch
        returns a result and returning the stored one would report success for
        something that never ran to a caller who will never look again. An
        expired record for an irreversible operation refuses rather than
        re-claiming.
        "Do not reserve keys for prevalidation failures" is STRUCTURAL: the
        router runs `validate` and `object-authorize` before `idempotency`, so a
        request failing either never reaches the claim. `router.spec.ts` asserts
        it by counting record lookups — 0 for a request authorization refuses. -->
  - [x] S4.7.f Reconcile timeout/connection-loss outcomes before repeating
        irreversible provider or publish effects. Evidence: fault tests.
        <!-- The unknown outcome is a third answer and the dangerous one. A
        connection lost after the request left is neither a success nor a
        failure, and both obvious policies are wrong for some route: retrying
        repeats an irreversible effect, refusing strands a caller who was fine.
        The table has four cells and one of them is the whole item: a
        reconciliation that comes back INDETERMINATE refuses and asks for a
        person. That is the cell people fill in with "retry" because the
        alternative is inconvenient, and it is the cell that produces the
        duplicate charge.
        The probe is keyed by the IDEMPOTENCY KEY and not by any local id,
        because after a connection loss the local id may never have been written
        — which is the state being reconciled. Asserted by capturing what the
        probe was asked. -->
      <!-- S4.7 rollup: 2786 kit tests pass (32 new); `tsc` clean on both
      projects; export map at 53 entry points, `--check` clean. -->
- [x] S4.8 Add optimistic-concurrency preconditions. Evidence: stale revisions
      return `409` or `412` with merge context.
      <!-- `src/preconditions.ts` + `src/preconditions.spec.ts` (18 cases) plus
      three interaction cases appended to `router.spec.ts`. New
      `./preconditions` subpath, 54 entry points.
      The DECISION is S2.3's `applyPrecondition` — one total function every path
      runs — and the atomic write is already proved against a real engine by
      `tools/revision-race.mjs`. This is everything between an HTTP request and
      that function, which S2.3 deliberately did not reach. -->
  - [x] S4.8.a Declare expected-revision requirement and accepted field/header
        per mutable route. Evidence: route contracts.
        <!-- `RoutePrecondition` declares the path, whether it is required, the
        CARRIER (an `if-match` header or a body field) and the field name, plus
        `perItem` for a multi-subject request. `preconditionGaps` refuses a
        multi-subject route carrying one revision for many subjects: a single
        revision can only describe one of them and the rest are unchecked. -->
  - [x] S4.8.b Validate expected revision against authorized current object and
        reject missing/malformed conditions. Evidence: input tests.
        <!-- `evaluateAgainstAuthorized` takes S4.3's `AuthorizedLoad` and
        REFUSES TO WORK FROM ANYTHING ELSE, which is the signature doing the
        arguing. There is no way to call it with a revision from a second query
        — and a second query is the one somebody writes without the tenant
        predicate, so the object whose revision decided the conflict would not
        be the object authorization approved. It also closes the narrower hole:
        between two reads the row can change, and the precondition would be
        compared against a revision nobody was authorized to see. Cases cover a
        concealed object and a load for a DIFFERENT entity.
        A missing condition is REFUSED, never defaulted: no `if-match: *`, no
        last-write-wins fallback, because a default is last-write-wins with
        extra steps and it is off for whoever forgets the header. -->
  - [x] S4.8.c Pass the precondition to an atomic conditional persistence write.
        Evidence: real-DB race test.
        <!-- `tools/revision-race.mjs` (S2.3.b), re-run green on this branch
        against PostgreSQL 16.14: 3 concurrent connections, atomic UPDATE and
        INSERT each producing exactly one winner with no lost update, and BOTH
        read-then-write controls losing one — so the harness is known to be
        capable of failing. Nothing new was needed here; what was needed was to
        confirm the claim still holds, which the execution contract requires of
        existing code used as evidence. -->
  - [x] S4.8.d Return `409`/`412`, safe current/expected revision, conflict
        reason, compare/reload action, and correlation. Evidence: response
        fixtures.
        <!-- 409 when the object is there and has moved on; 412 when the
        condition itself could not be evaluated. The distinction is not
        decoration: a 409 says "reload and compare" and a client can act on it,
        while a client treating a 412 the same way would reload forever against
        a request that will never be conditional. `subject-absent` is the one
        conflict kind that is a 412 and not retryable — telling a user to reload
        an object that has been deleted sends them somewhere that will 404.
        The envelope is S2.3's `conflictEnvelope`, so the conflict inherits the
        canary scan and the declared-key rule; the response asserts both
        revisions, the mutation path, the `reload-and-compare` remediation, the
        correlation id and the preserved intent token — which is what makes it
        different from a bare 409, because the client can offer compare, retry
        and copy instead of "your changes were lost".
        Both authorization-shaped input faults produce the SAME public answer as
        a malformed condition, so a caller cannot learn the object exists. -->
  - [x] S4.8.e Apply to edit, transition, restore, review, waiver, bulk, import,
        job, and publish paths. Evidence: coverage matrix.
        <!-- `PRECONDITION_PATHS` is the item's NINE and deliberately not
        S2.3's seven `MutationPath`s, which are shaped by how storage sees a
        write: a review, a waiver and a publish are all `single` there and are
        entirely different things to leave unprotected. `storagePathFor` maps
        between them, so the coverage question is asked in the vocabulary a
        reviewer thinks in while the decision stays in the one the engine needs.
        The gap check runs in the direction that matters — not "does every
        declared route have a precondition" but "is there a path nothing
        declares one for", which is invisible to any check iterating the routes
        that exist. -->
  - [x] S4.8.f Verify idempotent retry replays an accepted result while a new
        stale intent conflicts. Evidence: interaction tests.
        <!-- Three cases at the router, where the two steps actually meet.
        A RETRY of an accepted request replays even though the object has since
        moved to a newer revision: it already happened, and re-deciding it
        against a revision that changed after it succeeded would refuse a
        request that cannot be refused. The trail proves it never reached the
        concurrency step.
        A NEW intent carrying the same stale revision conflicts, running the
        full prefix through `concurrency`.
        And a retry whose DIGEST differs conflicts on the key BEFORE the
        precondition is consulted, so an accepted key cannot be reused to
        smuggle a different mutation past a stale revision. -->
      <!-- S4.8 rollup: 2808 kit tests pass (18 + 3 new); `tsc` clean on both
      projects; export map at 54 entry points, `--check` clean;
      `node tools/revision-race.mjs` green against PostgreSQL 16.14. -->
- [x] S4.9 Add deterministic cursor pagination. Evidence: no duplicate or
      missing records while concurrent inserts occur.
      <!-- `src/cursors.ts` + `src/cursors.spec.ts` (40 cases). New `./cursors`
      subpath, 55 entry points. `indexes.ts` (S3.x) already refuses `offset` as
      a pagination MODE and holds `sortIsTotal`; this is the cursor itself.
      THE CASE THAT ACTUALLY DECIDES IT IS NOT THE CONCURRENT INSERT. It is the
      TIE — a hundred rows sharing one `updatedAt`, which is what an import
      produces — with the page boundary falling inside it. The property runs
      over 300 rows across 3 distinct timestamps, at seven page sizes including
      1 and n+1, forward and backward. -->
  - [x] S4.9.a Define default/maximum limit and stable default/allowed sorts
        with unique tie-breakers per collection. Evidence: pagination registry.
        <!-- `validateCollection` refuses seven incoherent declarations, every
        one producible. Two are the item: a sort whose last key is not unique
        within scope, and a unique key that is NOT LAST — which looks like
        pedantry and is not, because every key after a unique one is dead (the
        rows are already totally ordered by then), so `[id, updatedAt]` is
        `[id]` with a decoration a reviewer believes is doing something.
        The two are reported as DIFFERENT faults with different fixes: telling
        somebody to add a tie-breaker that is already there sends them the wrong
        way. A requested limit is CLAMPED rather than refused. -->
  - [x] S4.9.b Encode sort, filter, direction, position, version, and safe
        consistency context in an opaque protected cursor. Evidence: cursor
        tests.
        <!-- `CursorSeal` is a seam the host implements with authenticated
        encryption. The kit will not offer base64 as a substitute: a client that
        decodes a cursor and edits the position is a client reading rows by
        editing a string, and the test asserts the seal REFUSES a token it did
        not produce rather than decoding one and finding it wrong.
        The payload carries sort, direction, position (VALUES, not an index —
        an index describes a position in a result set that no longer exists the
        moment anything is inserted before it), version, query digest and an
        opaque consistency marker. `parseCursorPosition` refuses anything but
        scalars: a row fragment travelling in a token is one the next caller may
        not be allowed to read. -->
  - [x] S4.9.c Bind cursors to actor/tenant scope, route/contract, normalized
        query, and lifetime. Evidence: misuse tests.
        <!-- Nine rejections, every one producible: tenant, actor, collection,
        route, contract version, cursor version, sort, expiry and query digest.
        THE QUERY DIGEST IS CHECKED LAST, deliberately and with its own case.
        "Your cursor is for a different query" tells a caller their cursor WAS
        valid for something, which is worth withholding from somebody holding a
        token that is not theirs — so the scope bindings answer first.
        Without the digest at all, the second page of a search for
        `status=draft` becomes the second page of a search for everything,
        starting at a position that means nothing there. An EMPTY query gets a
        digest of its own rather than a null, or a cursor for it would match
        any query. -->
  - [x] S4.9.d Query through authorization using strict before/after keyset
        conditions rather than offset. Evidence: query plan and disclosure
        tests.
        <!-- `keysetCondition` returns a described ROW-VALUE comparison — `(a,
        b) > (?, ?)` is one index range scan and the OR expansion is several,
        only one of which the planner will use — and it is STRICT always,
        because a `>=` returns the row the previous page ended on, which is the
        duplicate the whole scheme exists to avoid and the mistake made when
        somebody "fixes" a missing row that was actually a tie.
        A BACKWARD PAGE IS THE FORWARD QUERY WITH EVERY DIRECTION FLIPPED and
        the results reversed afterwards, not a separate query with `<` swapped
        in by hand — which is where the two directions drift and one starts
        skipping ties. Asserted, along with the absence of any offset in the
        condition's own shape. -->
  - [x] S4.9.e Define concurrent
        insert/update/delete/tombstone/permission-change behavior and any
        snapshot guarantee. Evidence: scenario tests.
        <!-- Seven concurrent events, each mapped to what the traversal
        promises, with and without a snapshot.
        THE HONEST ADMISSIONS ARE THE UPDATE ROWS: a row that moves ACROSS the
        cursor position mid-traversal can be missed or repeated, and no keyset
        scheme prevents that without a snapshot. Claiming otherwise would be the
        fabricated guarantee. What it DOES promise is that a row nobody touched
        is returned exactly once, which offset cannot promise even with one.
        And a snapshot fixes the data and never the permissions: a row the
        caller lost access to mid-traversal stops being returned either way,
        which is the one cell that does not move. -->
  - [x] S4.9.f Prove forward/backward traversal has no duplicates or missing
        items within the declared model across equal sort values. Evidence:
        property tests.
        <!-- The traversal runs to completion over 300 rows across 3 distinct
        sort values — a hundred-way tie at every position — at page sizes 1, 2,
        7, 25, 299, 300 and 301, forward and backward, with zero duplicates and
        zero missing. Reversing the backward PAGES (not the rows) reproduces the
        forward order exactly; the page boundaries differ between directions and
        that is correct, since the remainder is at the start one way and the end
        the other.
        Then the evidence clause literally: a row inserted after the cursor
        between every page, still zero and zero.
        AND THE CONTROL. The offset implementation, raced identically with one
        insert BEFORE the cursor per page, is asserted to duplicate or skip.
        Without it a green traversal proves only that the harness ran. -->
      <!-- S4.9 rollup: 2849 kit tests pass (40 new); `tsc` clean on both
      projects; export map at 55 entry points, `--check` clean. -->
- [x] S4.10 Add payload, upload, query-complexity, rate, concurrency, and spend
      limits. Evidence: limit tests return actionable retry metadata.
      <!-- `src/limits.ts` + `src/limits.spec.ts` (33 cases). New `./limits`
      subpath, 56 entry points. S4.1 put the two-phase `LimitSeam` in the
      pipeline; this is the policy behind it.
      EVERY LIMIT HERE HAS A BYPASS, AND THE BYPASSES ARE WHY THIS IS A MODULE
      RATHER THAN A FEW NUMBERS. A suite that sends one oversized body from one
      identity in one tenant confirms the numbers are wired up and misses all
      six of them. -->
  - [x] S4.10.a Enforce compressed/decompressed body, field, array, batch,
        response, and archive limits before expensive parsing. Evidence: payload
        boundary tests.
        <!-- The wire cap, the DECOMPRESSED cap and the RATIO are three limits,
        not one. A 4 MB body cap enforced on the wire is not a cap against
        `Content-Encoding` — a few hundred kilobytes of zeroes expands to a
        gigabyte — and a body inside BOTH byte caps that expands 3000:1 is still
        a bomb aimed at a smaller target, which is its own case.
        Every breach is reported rather than the first, because a client fixing
        one and resubmitting to discover the next is making the problem worse
        one round trip at a time. -->
  - [x] S4.10.b Enforce upload bytes, parts, files, dimensions, duration,
        frames/pages, nesting, and decompression ratio. Evidence: upload limit
        corpus.
        <!-- PIXELS is the limit people leave out and the one that decides an
        image endpoint: a 64,000 x 64,000 PNG of one colour is twelve kilobytes
        on the wire and gigabytes once decoded, so every byte limit passes and
        the decoder is the target. Asserted as its own case with a 12 KB upload.
        `declared-size-mismatch` is not a size limit at all — it is the check
        that the declared and received lengths agree, because a limit enforced
        against a DECLARED size is a limit against a number the client chose.
        Eight breach kinds, every one producible. -->
  - [x] S4.10.c Score and limit filter depth, predicates, joins, expansion,
        requested fields, sort, page size, graph traversal, and search cost.
        Evidence: query complexity tests.
        <!-- Scored from the query's own shape BEFORE it runs, because a
        complexity budget checked afterwards is a timeout with extra steps: the
        work is done, the connection was held, and the limit only threw the
        result away.
        `COMPLEXITY_WEIGHTS` is DECLARED so a refusal can be explained — "your
        query scored 248" is not an explanation — and the verdict names its
        drivers largest-first. Joins and expansions outweigh predicates because
        both multiply rows; graph traversal outweighs both because its cost is
        exponential in depth rather than linear. -->
  - [x] S4.10.d Enforce sustained/burst request rate by actor, tenant, route,
        client class, and trust tier. Evidence: rate-limit tests.
        <!-- Rules are keyed by DIMENSION and BUCKET, and `consume` charges
        every bucket a request falls into rather than the one it names. The
        tenant bucket is checked first and its breach reported in preference,
        because a tenant breach is the one another caller in the same tenant can
        do nothing about and is therefore the more useful thing to be told. -->
  - [x] S4.10.e Enforce concurrent requests, streams, uploads, jobs, provider
        calls, and expensive operations by actor/tenant/resource. Evidence:
        concurrency tests.
        <!-- Concurrency dimensions carry a window of zero — they are
        instantaneous, not windowed — and a breach still returns a retry-after
        of one second, because no reset exists to name and "try again when
        something finishes" is the honest answer. Five concurrency dimensions
        are declared separately (requests, streams, uploads, jobs, provider
        calls) so `dimension-with-no-rule` can find the ones nothing limits. -->
  - [x] S4.10.f Estimate/reserve and enforce per-operation, actor, tenant,
        period, provider, and exceptional spend budgets. Evidence: spend tests.
        <!-- RESERVE BEFORE THE WORK, SETTLE AFTER. Charging afterwards is a
        budget that can only be exceeded and the amount is unbounded — one
        runaway job spends a month's allowance and the limit reports it
        afterwards. Reservations count against the ceiling, or two concurrent
        expensive operations each see room for one.
        An operation whose cost cannot be ESTIMATED is `unestimable` and blocks;
        treating it as zero is the shape of every "we could not price it so we
        ran it". An exception carries an expiry, because an exceptional
        allowance with none is a permanent one somebody described as
        exceptional. And `settle` REPORTS the overrun rather than absorbing it:
        a reservation regularly under the actual is an estimator wrong in the
        one direction that matters. -->
  - [x] S4.10.g Return stable limit category, safe current/max where permitted,
        reset/retry time, remediation, and correlation. Evidence: response
        fixtures.
        <!-- `current` and `maximum` are disclosed for rate, payload, upload,
        complexity and concurrency, and WITHHELD for spend. The asymmetry is
        deliberate: a rate ceiling is a property of the API and telling a client
        makes them a better client, while a spend ceiling is a commercial fact
        about the tenant that the person hitting it may not be entitled to know.
        `retryAfterSeconds` is present wherever a reset exists, because a limit
        with no reset time is one every client guesses at — and the ones that
        guess badly are indistinguishable from an attack.
        `parsePositiveLimit` refuses zero and infinity, which are usually a unit
        conversion that went wrong and are both silent: one refuses everything
        and the other limits nothing. -->
  - [x] S4.10.h Prevent identity rotation, multiple clients, retries, or batch
        subdivision from bypassing tenant/actor limits. Evidence: abuse tests.
        <!-- Four bypass findings over a RULE SET rather than over traffic,
        because none of them shows up in a test that sends requests from one
        identity in one tenant.
        `no-tenant-bucket`: ten identities in one tenant get ten times the
        allowance, and minting identities is cheaper than the limit.
        `no-actor-bucket`: one caller consumes everybody else's allowance and
        the tenant experiences it as an outage.
        `dimension-with-no-rule`: the dimension nothing limits at all — the test
        finds `concurrent-uploads` and `concurrent-provider-calls` unlimited in
        a plausible-looking rule set.
        `cost-not-charged-per-item`: a request costs one whatever it carries, so
        one request split into ten batches costs the same as one and the limit
        is a limit on round trips rather than on work. `consume` charges a batch
        of fifty as fifty, asserted. -->
      <!-- S4.10 rollup: 2883 kit tests pass (33 new); `tsc` clean on both
      projects; export map at 56 entry points, `--check` clean. -->
- [x] S4.11 Add guarded remote-resource fetching. Evidence: SSRF, redirect,
      private-IP, DNS-rebinding, oversize, timeout, and decompression tests.
      <!-- `src/fetching.ts` + `src/fetching.spec.ts` (27 cases). New
      `./fetching` subpath, 57 entry points.
      THE REPOSITORY ALREADY HAS A REAL SSRF GUARD — `@oshun/http-client`'s
      `assertUrlAllowed` / `classifyIp` / `makeSsrfSafeLookup` — and it is
      `layer:infra`, which `layer:workbench-kit` may not depend on. Same wall
      S4.2 hit with `@oshun/auth`, and it points the same way: a second address
      classifier here would be the duplicate this ledger exists to avoid, and it
      would be the WORSE of the two because it is the one nobody runs against
      real traffic.
      So this module classifies NO address. It owns the policy, the seam, and a
      CORPUS — and `fetching.spec.ts` points that corpus at the real guard the
      way S4.2's parity matrix points at the real role taxonomy. The guard
      refuses all 21 targets, including every encoded bypass. -->
  - [x] S4.11.a Canonicalize scheme, credentials, IDNA/host, port, IP literal,
        path, and fragment before policy. Evidence: URL parser fixtures.
        <!-- Not a URL parser — the canonical guard has one and it is better
        placed. What is here is the POLICY the parser is run under: schemes,
        ports, redirect handling, the six timeouts, the byte and archive bounds,
        the accepted media types, and two booleans about credentials.
        `validateFetchPolicy` refuses ten incoherent combinations. -->
  - [x] S4.11.b Allowlist schemes/ports and block loopback, link-local, private,
        reserved, multicast, metadata, internal service, file, and unix targets.
        Evidence: address negative tests.
        <!-- `SSRF_CORPUS`: 21 targets, each with the reason it exists, run
        against `@oshun/http-client` in the spec. Loopback, link-local, private,
        reserved, multicast, four cloud metadata endpoints, credentials in the
        authority — and the ENCODED BYPASSES, which are the interesting half:
        `127.1`, the decimal `2130706433`, the octal `0177.0.0.1`, and
        `::ffff:169.254.169.254`, where an IPv6 classifier that does not unwrap
        the mapping applies IPv6 rules to an IPv4 address and the metadata
        service is an IPv4 address.
        Two controls make the green run mean something: a guard that refuses
        NOTHING is reported in full, and an ordinary public target is asserted
        still permitted — a guard that refuses everything passes every negative
        test and is useless.
        `internal-service` is deliberately EMPTY with a stated reason: a
        deployment's own service names are its highest-value targets and the kit
        knows none of them, so inventing plausible ones would produce a corpus
        that passes everywhere and protects nowhere. `extendCorpus` is how a
        deployment adds its own, and it refuses an entry with no reason. -->
  - [x] S4.11.c Resolve through controlled DNS, validate every address, pin the
        connection target, and revalidate on reconnect. Evidence: DNS-rebinding
        tests.
        <!-- `connectionWasPinned` compares the address the guard VALIDATED
        against the address the socket CONNECTED to. That comparison is the
        whole of DNS rebinding: the guard resolves a name and approves the
        answer, the HTTP client resolves the name again, and the record's TTL
        was one second because the attacker owns the zone. The only defence is
        to connect to the address that was checked, and the only way to know it
        happened is to compare them. -->
  - [x] S4.11.d Disable redirects or validate every hop/target/scheme/port/
        credential with a strict maximum. Evidence: redirect escape tests.
        <!-- A redirect limit that contradicts the redirect policy is refused
        in BOTH directions — redirects refused with a limit above zero, and
        validated with a limit of zero — because one of the two is what somebody
        will believe. A chain longer than ten hops is refused on the grounds
        that nobody is reading them and every real target is reachable in fewer.
        `FetchHop` carries the resolved address per hop, so a redirect chain is
        auditable after the fact rather than only bounded. -->
  - [x] S4.11.e Enforce connection/TLS/header/idle/body/total timeout,
        bandwidth, response, retry, and content limits. Evidence: slow/oversize
        tests.
        <!-- Six timeouts, and the TOTAL is required separately with its own
        case: the phase timeouts cannot substitute for it, because a server that
        sends one byte before each idle timeout holds the connection for ever
        without ever being idle. A phase timeout above the total is refused too
        — it can never fire, so the total is the only one that ever does, and
        the phase a reviewer believes is bounding something is bounding
        nothing. -->
  - [x] S4.11.f Bound compressed/decompressed bytes, archive entries/depth,
        dimensions, frames, duration, and parser resources. Evidence: bomb
        fixtures.
        <!-- Compressed bytes, decompressed bytes, expansion ratio, archive
        entries and archive depth, with `decideContent` refusing an oversize
        response and an over-expanding one separately. The ratio matters on its
        own: a response inside both byte caps that expands ninety-fold is still
        a bomb aimed at the fetcher. -->
  - [x] S4.11.g Sniff content, quarantine mismatch/malicious media, isolate
        parsing, and prevent privileged interpretation of fetched text.
        Evidence: polyglot/injection tests.
        <!-- The SNIFFED type decides, not the declared one, because the
        declared type is the far side's claim — asserted with a response
        labelled `text/markdown` whose bytes are an ELF binary.
        A MISMATCH IS QUARANTINED RATHER THAN REFUSED OR TRUSTED. It is often
        innocent (a server that labels everything `application/octet-stream`)
        and it is also exactly what a polyglot file looks like, so it is
        isolated and reported instead of either being handed to a parser or
        thrown away with the evidence. -->
  - [x] S4.11.h Strip outbound credentials and redact signed URLs/protected
        locations from logs and public errors. Evidence: leakage tests.
        <!-- `withoutUserinfo` removes the OUTER authority's credentials and
        leaves an inner URL alone — asserted with
        `https://example.com/?next=https://a:b@c`, which a naive pattern eats,
        changing the request it was meant to sanitise.
        `redactUrl` replaces the WHOLE query rather than known parameter names:
        a per-parameter redaction has to know every provider's parameter, and
        the one it does not know is the one that goes out. The path is kept,
        which is what somebody debugging actually needs. Unparseable input
        returns `(unparseable url)` rather than throwing, because a redactor
        that throws is one somebody wraps in a try/catch that logs the original. -->
      <!-- S4.11 rollup: 2911 kit tests pass (27 new); `tsc` clean on both
      projects; export map at 57 entry points, `--check` clean.
      `@oshun/http-client` is a DEV dependency and a spec-only vitest alias, so
      the emitted package does not carry it — same arrangement as S4.2's
      `@oshun/studio-authoring`. -->
- [x] S4.12 Add audit capture for every mutation outcome, including refusals and
      partial failures. Evidence: route inventory has no mutation without an
      audit assertion.
      <!-- `src/mutation-audit.ts` + `src/mutation-audit.spec.ts` (28 cases).
      New `./mutation-audit` subpath, 58 entry points.
      S2.14 built the LIFECYCLE audit — fifteen kinds from the stage engine, the
      gates and the signoffs, folded into a projection. This is the other one,
      and the evidence clause says what makes it different: "route inventory has
      no mutation without an audit assertion". The subject is COVERAGE, not
      shape. -->
  - [x] S4.12.a Derive the mutation route/command/job inventory and assign
        required audit event types. Evidence: route-to-audit matrix.
        <!-- `auditCoverage` runs from the INVENTORY toward the assertions, and
        the direction is the whole design. Asking "does every audit row have a
        route" finds nothing; asking "does every mutation have an assertion"
        finds the route somebody added last month.
        Four surfaces — route, command, job, event handler — because the last
        two are the ones that never appear in a route table and mutate anyway.
        Six gap kinds, every one producible, including the one that inflates a
        coverage number: an assertion for a mutation that no longer exists,
        which passes against nothing and is counted. -->
  - [x] S4.12.b Capture actor/delegate, tenant, target, action, authorized
        parameters/hash, prior/new revision, reason, policy, correlation, and
        time. Evidence: audit schema assertions.
        <!-- Actor with its delegate half, tenant, target, action, the
        parameter DIGEST, reason key, policy version, correlation and time.
        THE DIGEST AND NOT THE PARAMETERS. It answers "was this the same
        request" without answering "what did it say", which is the question an
        audit trail must not answer to whoever reads it — and it is the same
        digest S4.7 takes, so a retry and its original are visibly one act.
        `priorRevision` and `newRevision` are both required and non-null on a
        mutation record: a record of a change that cannot say what it changed
        FROM is one nobody can reconstruct a history out of. -->
  - [x] S4.12.c Persist accepted/completed audit atomically with authoritative
        mutation or guaranteed outbox intent. Evidence: commit/rollback tests.
        <!-- Two lawful designs and one that resembles both.
        `assessPersistence` accepts the audit row in the mutation transaction,
        and accepts an outbox INTENT in that transaction delivered afterwards —
        which is what survives an audit sink being down. It refuses the third:
        writing the audit row after the transaction commits, which is what "we
        log after we save" produces, where every crash between the two leaves a
        mutation nobody can attribute and the window is exactly as long as the
        audit write takes. An outbox intent written OUTSIDE the transaction is
        refused as the same design with an extra table in it.
        The outbox half is proved against a real engine by
        `tools/outbox-atomicity.mjs`, re-run green on this branch: one
        transaction left both tables unchanged, and the negative control — the
        same failure across two transactions — produced the orphaned
        authoritative row, which is what makes the first result mean
        anything. -->
  - [x] S4.12.d Record validation, authentication, authorization, policy,
        conflict, limit, and dependency refusals without claiming mutation.
        Evidence: refusal audit tests.
        <!-- TWO RECORD TYPES, NOT ONE WITH A NULLABLE FIELD. The refusal
        record has no `newRevision` at all — not "we leave it null", there is
        nowhere to put one — because a single type with an optional revision is
        how a refused request ends up in a report of what changed. Asserted both
        ways: the shape has no such key, and a record that acquired one through
        an untyped boundary is reported as `refusal-claiming-a-mutation`.
        Seven refusal classes covering validation, authentication,
        authorization, policy, conflict, limit and dependency, each carrying the
        stable code and never a message. -->
  - [x] S4.12.e Record started/running/partial/completed/failed/cancelled/
        compensated/unknown outcomes and per-item bulk results. Evidence:
        asynchronous/partial audit tests.
        <!-- Ten outcomes including `compensated` and `unknown`. THE `unknown`
        MEMBER IS THE POINT: a worker that died mid-flight has an outcome nobody
        knows, and leaving the member out does not remove the state — it records
        it as `failed` or `completed`, one of which is a lie in every case, and
        S4.7's reconciliation exists because it is real.
        `summarizeBulk` has NO THRESHOLD: one failure in ten thousand is
        `partial`, asserted at exactly that scale, because the person whose row
        failed is not helped by a percentage and "the import succeeded" is the
        report this prevents. -->
  - [x] S4.12.f Redact content/secrets/protected data, enforce tenant/property
        access, and preserve immutable integrity/retention. Evidence: audit
        security tests.
        <!-- `AUDIT_RECORD_FIELDS` is exported so the assertion is "these and
        no others" rather than a copy of the list beside the list, and
        `mutationAuditLeaks` scans VALUES as well as keys: a caller who puts the
        record's content into `action` has broken nothing structurally and
        leaked it anyway. Prose in an audit field is flagged because the audit
        trail is read by more people than the record is. The parameter digest is
        exempted by name — 64 hex characters would otherwise trip the credential
        pattern, and being an unreadable identifier is its whole purpose.
        Retention refuses two things: a trail that can be UPDATED, which is a
        record of what somebody last wanted it to say; and refusals kept longer
        than mutations, which leaves an investigation into a breach finding
        every failed attempt and none of the successful ones. -->
  - [x] S4.12.g Fail the inventory check when a mutation lacks an outcome audit
        assertion. Evidence: deliberate uncovered-route failure.
        <!-- The negative control, and the load-bearing case of the whole item:
        a deliberately uncovered route that the gate must fail on, asserted down
        to the single gap and its mutation id. A coverage gate that has never
        failed is a gate nobody has proved is connected to anything, and every
        other assertion in this file is conditional on this one. -->
      <!-- S4.12 rollup: 2940 kit tests pass (28 new); `tsc` clean on both
      projects; export map at 58 entry points, `--check` clean;
      `node tools/outbox-atomicity.mjs` green with its negative control. -->
- [x] S4.13 Add request/trace correlation to audit and job IDs. Evidence: one
      journey can be reconstructed across web, BFF, worker, storage, and
      publisher.
      <!-- `src/correlation.ts` + `src/correlation.spec.ts` (22 cases). New
      `./correlation` subpath, 59 entry points.
      WHAT MAKES THE EVIDENCE CLAUSE HARD IS NOT THE PLUMBING. A journey is
      reconstructable only if every hop wrote down the SAME identifier, and the
      hops that forget are always the asynchronous ones — the job attempt, the
      outbox delivery, the provider callback — because those are where the
      request that started it has already returned. -->
  - [x] S4.13.a Generate or validate request and W3C trace context at the
        browser or trusted ingress without accepting spoofed privileged
        correlation. Evidence: ingress tests.
        <!-- THE ASYMMETRY IS THE RULE: an inbound TRACE is continued and an
        inbound CORRELATION is refused. A trace id is a join key with no
        authority attached and the worst a forged one does is join two unrelated
        traces; a correlation is what support SEARCHES BY, so accepting a
        claimed one lets a caller file their request under another tenant's
        reference and the person who searches it is shown somebody else's
        request. A trusted internal ingress may supply one; a browser may not.
        The refusal is RECORDED rather than silent, because a client that keeps
        sending one is a client somebody should talk to.
        `parseTraceparent` refuses an all-zero trace id by specification and by
        argument: accepting it makes every request from every tenant appear to
        be the same trace, which is worse than no tracing because it looks like
        tracing. A malformed inbound trace does not refuse the request — it is a
        reason not to CONTINUE a trace that was never valid. -->
  - [x] S4.13.b Create command/idempotency and audit IDs and link them to
        request, trace, actor, tenant, target, and revision. Evidence: mutation
        correlation tests.
        <!-- Thirteen link kinds, each node naming its parent plus trace,
        correlation, tenant, target and revision. `linkFaults` reports eight
        ways a set of nodes fails to be one journey, every one producible,
        including a CYCLE — reported rather than walked, with a bound, so a bad
        graph is a failing check rather than a hanging one. -->
  - [x] S4.13.c Propagate correlation through database transaction/outbox,
        event, inbox, job parent/child/attempt, blob, provider, and external
        operation. Evidence: async propagation tests.
        <!-- The asynchronous kinds are named as a set, and a node of one of
        them with NO parent at all is its own fault kind: the request that
        produced it has already returned, which is exactly why this is the hop
        that forgets. A dangling parent is the hop that will simply be absent
        from the reconstruction, and nobody finds out until they try — usually
        during an incident. -->
  - [x] S4.13.d Propagate allowed trace/correlation through publisher,
        callback/webhook, desktop/bridge, and read-back verification. Evidence:
        boundary integration tests.
        <!-- Publish, callback, bridge-call and read-back are link kinds, so a
        boundary crossing is a node in the same graph rather than a separate
        story. The tenant check across a parent edge looks redundant beside the
        correlation check and is not: a node that inherited the right
        correlation and the WRONG tenant is a row returned to whoever searches
        that correlation, and they are in the other tenant. -->
  - [x] S4.13.e Index support-safe IDs for reconstruction without using raw
        high-cardinality IDs as metric labels. Evidence: observability schema
        tests.
        <!-- Checked in BOTH directions, which is the part usually missed. An
        id used as a metric LABEL is one series per request: the scrape gets
        slower every day, the dashboards stop loading, and the fix is to delete
        the metric — taking the signal it was added for with it. And an id NOT
        in the support index is the opposite failure: the journey is fully
        recorded and unfindable, which fails "reconstructable" just as
        completely as not recording it. A secret in the support index is refused
        too, since indexing it means storing it. -->
  - [x] S4.13.f Reconstruct a complete web-to-publisher journey with retries,
        partial failure, and compensation from stored evidence. Evidence:
        correlation acceptance report.
        <!-- A fourteen-node journey across the five tiers the clause names,
        with a failed first attempt, a RETRY, a provider call, a publish, a
        read-back and a compensation, rebuilt from the stored nodes alone.
        A retry appears as two ATTEMPTS OF ONE JOB rather than two jobs —
        asserted through `attemptsByJob` and the per-attempt ordinal — because
        three attempts of one job is a retry story and three jobs is a different
        and much worse one.
        Steps are ordered by TIME and carry DEPTH, because the two answer
        different questions: time is what happened next, depth is what caused
        what, and a reconstruction with only one is either a list nobody can
        read as a tree or a tree nobody can read as a sequence.
        And it REFUSES to reconstruct from nodes that do not form one journey,
        rather than returning a partial one nobody can tell is partial. -->
      <!-- S4.13 rollup: 2963 kit tests pass (22 new); `tsc` clean on both
      projects; export map at 59 entry points, `--check` clean. -->
- [ ] S4.14 Add explicit capability and dependency-health endpoints. Evidence:
      client states derive from real probes, not build-time guesses.
      <!-- `src/health.ts` + `src/health.spec.ts` (29 cases). New `./health`
      subpath, 60 entry points.
      The pieces existed — S1.6's five states, S3.14's probes, S1.11's discovery
      document. What was missing is the ENDPOINT, which is where the decisions
      none of the pieces can make alone get made.
      THE EVIDENCE CLAUSE IS MADE STRUCTURAL RATHER THAN ASPIRATIONAL.
      `deriveCapabilityState(capability, probes, missingConfiguration)` has no
      parameter a build-time guess could enter through — asserted on its arity —
      so a fast path cannot be added without editing the signature. -->
  - [x] S4.14.a Define authenticated capability discovery by workbench/domain,
        contract/version, audience, host, feature/config state, and lifecycle.
        Evidence: capability endpoint contract.
        <!-- `discoveryFaults` over rows carrying workbench, domain, contract
        version, audiences, host, lifecycle, live flag values and state.
        `unauthenticated-discovery` is the one worth arguing about: a capability
        list is a map of what this deployment can do and which tenants have
        which features enabled, so served openly it is a reconnaissance endpoint
        with an OpenAPI-shaped response. And a row reporting `configured` that
        nothing probed is reported as a build-time guess wearing a runtime
        answer's clothes. -->
  - [x] S4.14.b Implement real database/schema, blob, queue/worker,
        cache/search, provider/model, signing/trust, delivery/publisher, and
        bridge/host probes as applicable. Evidence: probe inventory.
        <!-- Fourteen dependency kinds — database, schema, blob store, queue,
        worker, cache, search, provider, model, signing, trust store, delivery,
        publisher, bridge host — so a probe inventory has a vocabulary to be
        complete against rather than a list somebody extends when they remember.
        The kit does not implement the probes: they reach real systems, which is
        the host's, and S3.14 already holds the rules a database probe has to
        obey. What is here is the shape their results arrive in. -->
  - [x] S4.14.c Return configured, not-configured, degraded, unavailable, or
        failed state with checked time, freshness, affected/retained capability,
        safe reason, and remediation. Evidence: state fixtures.
        <!-- `deriveCapabilityState` returns S1.6's five states with the
        message key, the affected dependencies and the retained behaviour.
        S1.6's `degraded` distinction is HELD rather than restated: nothing
        retained is `unavailable` and not `degraded`, so a client is never told
        part of something works when none of it does. And a capability nothing
        probed is `unavailable` rather than `configured`, which is the same
        fabricated-success refusal the rest of the kit makes. -->
  - [x] S4.14.d Separate liveness/readiness from user-facing dependency state
        and avoid probing through build-time environment assumptions. Evidence:
        runtime configuration-change tests.
        <!-- Four endpoint kinds with `endpointFaults` keeping them apart.
        `liveness-depends-on-a-dependency` is S3.14's rule restated where it is
        actually violated: a liveness probe that checks the database restarts a
        healthy process every time the database is briefly slow, and the restart
        makes the database slower. `readiness-with-no-dependencies` is the
        opposite — it answers yes while the process cannot serve a request.
        The build-time half is the derivation's signature (see the parent). -->
  - [x] S4.14.e Authorize and redact probe details so ordinary clients receive
        no secret, topology, internal host, protected tenant, or exploit detail.
        Evidence: role response tests.
        <!-- Three audiences, and the client view is CONSTRUCTED rather than
        filtered. Deleting fields from a richer object is a blocklist, and a
        blocklist is wrong the first time somebody adds a field — the leak then
        arrives with a feature that had nothing to do with health. The client
        shape has four fields and nowhere to put a hostname, a driver message, a
        query, a version or a duration; the test asserts the key set and that
        `10.0.4.19` and `SELECT 1` are absent from the serialised view.
        `healthDisclosureFaults` covers what the shape cannot see: a message key
        or capability name somebody built by interpolating a hostname, a
        credential or a driver error into it. -->
  - [x] S4.14.f Add timeout/cache/staleness/circuit behavior and distinguish
        probe failure from dependency failure. Evidence: fault tests.
        <!-- Freshness is THREE-VALUED because `stale` is the one that gets
        served. Under load a cached result is the right thing to return; what is
        not right is returning it as though it had just been taken, and a
        two-valued answer forces either that or withholding it entirely.
        An EXPIRED cache derives `unavailable` rather than its last answer,
        because serving the last known good state is how a dependency that has
        been down for an hour reads as healthy.
        And `unhealthy` (the dependency answered that it is broken) is kept
        apart from `unreachable` (the probe did not complete). The two have
        opposite retry semantics, and reporting the second as the first pages
        somebody about a healthy database. -->
  - [ ] S4.14.g Make UI capability banners and controls consume these endpoints
        exclusively. Evidence: browser network assertion.
        <!-- `controlFor(view)` takes a `ClientHealthView` and NOTHING ELSE,
        asserted on its arity. "Consume these endpoints exclusively" is
        therefore a property of the type rather than a rule somebody follows:
        there is no parameter a build-time flag could enter through.
        `BANNER_FOR_STATE` is total over the five states, so a sixth breaks
        every consumer at compile time rather than falling into a `default:` and
        rendering nothing. `degraded` is a WARNING and not blocking, which is
        the whole reason the state exists — a blocking banner over a partially
        working capability stops the part that does. `not_configured` is
        informational rather than a failure, because nothing is broken and
        telling a user their system is sends them to the wrong person. And a
        STALE answer still enables a working capability, since refusing to act
        on one is how a cache miss becomes an outage.
        THE CHECKBOX STAYS UNCHECKED. The item's named evidence is a BROWSER
        NETWORK ASSERTION, and no application mounts these endpoints yet — the
        contract they would be mounted against is what this commit delivers. The
        `controlFor` signature makes "exclusively" true by construction for any
        surface that uses it, and that is a different claim from having watched
        a browser make no other request. Under §2.1 a checkbox flips only when
        the evidence NAMED by the item exists, so this one waits for the app
        work, and S4.14 waits with it. -->
      <!-- S4.14 status: a–f are built and green (2993 kit tests, 29 new; `tsc`
      clean on both projects; export map at 60 entry points, `--check` clean).
      The parent stays unchecked because S4.14.g does, per §2.1. -->
- [x] S4.15 Add safe error mapping. Evidence: expected domain errors remain
      stable while unexpected errors produce an opaque ID and server trace.
      <!-- `src/error-mapping.ts` + `src/error-mapping.spec.ts` (23 cases). New
      `./error-mapping` subpath, 61 entry points.
      S1.10 built the substrate — registry, constructed envelope, canary scan,
      classifier-not-message rule, transport table — and its own
      `validateErrorRegistry` is re-run green here, since the execution contract
      requires existing code used as evidence to have passed on this branch.
      What this adds is the three things a register needs beyond the codes in
      it: a check for HOLES, a boundary, and a degradation. -->
  - [x] S4.15.a Inventory validation, auth, tenancy, conflict, lifecycle,
        policy/gate, dependency, provider, job, quota, and internal error types.
        Evidence: error mapping register.
        <!-- `familyGaps` asks which of the eleven families has NO code, because
        a family with none is one whose failures all arrive as
        `internal.unexpected` — a 500 for something the caller could have fixed,
        and a page for somebody who cannot. Invisible to any check that iterates
        the codes that exist. The eleven families map onto S1.10's twelve
        namespaces, and the mapping is asserted TOTAL so no namespace escapes.
        A RULE WAS TRIED HERE AND REMOVED, which is worth recording. "Every code
        in this family says the same thing about retrying" sounds like a smell
        and is not: `validation` should be entirely non-retryable, because
        waiting never fixes a bad request, and `conflict` entirely
        `after-change`, because waiting never resolves a conflict. It fired on
        both — on correct configuration — which is a check somebody switches off
        within a week, the same failure mode S4.1.d's scan was narrowed to
        avoid. It was replaced by `family-served-only-by-deprecated-codes`,
        which is sound, and by an assertion that the shipped registry produces
        NO findings at all — the thing that keeps the replacement honest. -->
  - [x] S4.15.b Map expected errors to stable code, status, message key/params,
        field/ref details, retryability, remediation, and correlation. Evidence:
        mapping tests.
        <!-- The envelope is S1.10's `publicError`, exercised here end to end:
        a stale-revision conflict carrying its key, its details, its
        `reload-and-compare` remediation and its correlation, with
        `scanEnvelope` finding no leak. And the negative: a parameter the code
        never declared is refused, because an undeclared parameter is how a
        field added to some handler's context object six months from now goes
        out with it. -->
  - [x] S4.15.c Preserve discriminated domain semantics across API, job update,
        event, and UI without relying on message parsing. Evidence: end-to-end
        error fixtures.
        <!-- All four transports branch on the CODE — that is the claim. A job
        update does not say "failed: not authorized" for a UI to parse; it
        carries the code, and the UI renders from the code and the message key.
        The four projections are built from ONE registry row, so agreement is
        derived rather than asserted twice, and three drifts are caught: a code
        that differs (one failure with two identities), a retryability that
        differs (the same failure telling a worker to retry and a client to give
        up), and a SIGNAL CONTAINING A SPACE — a sentence, which a consumer
        branching on breaks the day somebody improves the wording. That last one
        is the message-parsing this item exists to stop, arriving through the
        field nobody looked at. -->
  - [x] S4.15.d Map unexpected errors to an opaque public ID/message and retain
        access-controlled cause/stack/trace internally. Evidence: unexpected
        error tests.
        <!-- `mapSafely` gives the caller an opaque `internal.unexpected` with
        a support reference, and retains the cause internally behind S1.10's
        support scope. Three ways in, one way out: no mapping, a mapper that
        THREW, and a code the registry does not know — the last because a code
        nobody registered has no status, no retryability and no declared
        parameters, so emitting it invents all three.
        The caller cannot tell the three apart, which is correct; the log can,
        which is the point — both halves asserted. -->
  - [x] S4.15.e Redact secrets, SQL, stack, internal URLs, signed URLs, content,
        prompts, protected identities, and cross-tenant references. Evidence:
        canary leakage tests.
        <!-- S1.10's canary scan, exercised on a real constructed envelope. The
        stronger guarantee is structural and already held there: the envelope is
        CONSTRUCTED from declared scalars rather than filtered from a rich
        cause, so a secret, a SQL fragment, a stack frame or a signed URL has no
        field to travel in. The thrown mapper case additionally asserts that the
        exception's own text does not appear in the result. -->
  - [x] S4.15.f Handle mapper failure, unknown future code, nested aggregate
        error, and partial bulk result safely. Evidence: resilience tests.
        <!-- Four resilience cases, all real.
        A MAPPER THAT THROWS DOES NOT BECOME THE ERROR — when it does, the
        original failure disappears and everybody spends the incident debugging
        the error handler.
        An UNKNOWN FUTURE CODE degrades by NAMESPACE and never by a default: a
        client compiled last year meeting `policy.consent_withdrawn` should
        treat it as a policy failure, and "unknown error, retry" is how a
        permanent refusal becomes a retry loop. S1.10 closed the namespace set
        precisely so this fallback has something to fall back to.
        An AGGREGATE reports every distinct member code rather than the first,
        because "the import failed: field_invalid" over a run where half the
        rows hit a quota sends the operator to fix the wrong thing.
        And a run with any success is PARTIAL with its own code, because a
        client that reads a bulk failure as total failure retries the whole
        batch and duplicates everything that worked. -->
      <!-- S4.15 rollup: 3017 kit tests pass (23 new); `tsc` clean on both
      projects; export map at 61 entry points, `--check` clean. -->
- [x] S4.16 Generate OpenAPI or equivalent route contracts. Evidence: request,
      response, auth, error, and idempotency semantics are complete.
      <!-- `src/openapi.ts`, generated from the route descriptors rather than
      written beside them. A hand-maintained specification describes what
      somebody believed the API did on the day they last edited it, and the gap
      between that and the code is invisible from either side.
      THE DECISION THAT SHAPES EVERYTHING ELSE: completeness is measured FROM
      THE ROUTES TOWARD THE DOCUMENT. A specification is trivially made complete
      by deleting the operations that are hard to describe, and a check that
      iterated the document would find nothing wrong with having done it. -->
  - [x] S4.16.a Derive operation ID, method/path, owner/capability, summary,
        lifecycle, version, and tags from route descriptors. Evidence: operation
        inventory parity.
        <!-- The operation id IS the route id. The descriptor already carries a
        dotted, lowercase, unique, stable identifier, and inventing a second one
        creates a mapping nobody maintains and a generated client whose method
        names drift from the routes they call. Parity is asserted in both
        directions over the two archetypes' REAL routes: a mounted route with no
        operation is a client missing a call the server answers, and an
        operation with no route is a client with a method that 404s. The summary
        is a translation KEY and never a sentence — a summary written in one
        language is one a generated client shows to everybody — and
        `summary-is-a-sentence` is a gap kind for exactly that. -->
  - [x] S4.16.b Generate path/query/header/body/upload schemas with units,
        formats, limits, defaults, and examples. Evidence: request schema tests.
        <!-- Every `:param` in the declared path must appear in the contract, or
        a generated client cannot build the URL — asserted against
        `/desks/:workspaceId/stories/:storyId/quotes/:blockId`, which has three.
        `number-without-a-unit` is the fault that causes real incidents: a
        `timeout` documented as `integer` is one every client guesses the unit
        of, and half guess seconds where the server means milliseconds — a
        thousand-fold error in the direction that looks like a hang.
        `required-with-a-default` is the contradiction where the client picks
        one answer to "what happens when it is absent" and the server picks the
        other. A body with no documented limit is one every client discovers as
        a 413. And a parameter can be PRESENT in the document and still
        undocumented in the only sense that matters, so `parameterFaults` feeds
        `contractGaps` rather than sitting beside it. -->
  - [x] S4.16.c Generate success/async/partial/paginated/stream responses and
        capability/job references. Evidence: response schema tests.
        <!-- A success and an error response are both required: without the
        error, every generated client treats a failure as an unexpected
        exception and every one of them handles it differently. A 202 must name
        where the caller polls — an acceptance nobody can follow up on is worse
        than a refusal — and a paginated response must name the cursor parameter
        it accepts back, or it is a first page and nothing else. Both are gap
        kinds, both produced by a test, and the archetype's 201-returning import
        is carried through the real handler in the gate. -->
  - [x] S4.16.d Document authentication, tenant/workspace, object/property/
        function authorization, step-up, and data classification. Evidence:
        security completeness check.
        <!-- Security is PER OPERATION and never only at the document root.
        "This API uses bearer tokens" is true and useless; which roles, which
        scope, whether the object is authorized separately, whether a step-up is
        required and what classification the data carries are the things that
        DIFFER between operations, and the ones that differ are the ones worth
        documenting. The console's intake names `system` among its audiences and
        therefore authenticates as a service rather than a session — derived,
        not asserted twice. `security-not-documented` fires on a scoped
        operation that lists no roles. -->
  - [x] S4.16.e Document validation/auth/conflict/limit/dependency/provider/
        internal error envelopes and statuses. Evidence: error coverage check.
        <!-- `errorCoverageGaps` checks S1.10's register in BOTH directions
        against the document. Forward: each of the seven families the item names
        must have a documented envelope, spelled in the register's own
        vocabulary — "limit" is `quota` there, and a second word for it would be
        two codes for one failure with different retry advice. Backward, and
        this is the direction that matters more: a document that names a code
        the register does not have, or gives it a status the server never sends,
        is worse than one that says nothing — a client branching on it takes the
        default branch nobody tested. Both directions are exercised, and the
        shipped register produces zero findings. -->
  - [x] S4.16.f Document idempotency key scope/retention/replay/conflict and
        expected-revision preconditions. Evidence: mutation semantics check.
        <!-- The mutation section is derived from S2.3's `CommandSchema`, so the
        document cannot claim semantics the command does not have: the key's
        scope, its retention, the `if-match` precondition and the 409 it can
        return. A mutation with neither is two gap kinds, not one, because they
        fail differently — a client with no idempotency key discovers retry
        semantics by causing a duplicate, and a client with no precondition
        writes last-write-wins by default. A READ carries no mutation section at
        all rather than one full of nulls. -->
  - [x] S4.16.g Validate spec, examples, generated clients, server conformance,
        deterministic output, and drift. Evidence: contract generation gate.
        <!-- `tools/route-contract-gate.mjs`, an Nx target
        (`nx route-contract-gate workbench-kit`), running the WHOLE chain:
        route descriptors → contract → generated client → mounted router →
        archetype handlers. A specification validates; that is the problem. A
        document can be syntactically perfect, internally consistent and
        complete against its own schema while describing an API that does not
        exist, because every check that reads only the document agrees with it.
        So the gate never reads the document alone. The routes are the
        archetypes' real ones, the router is `createWorkbenchRouter` with all
        ten seams, the handlers are the archetypes' own bindings over in-memory
        ports, and the client is GENERATED from the contract and IMPORTED as a
        module rather than compared as text. Six checks: spec (from the routes),
        examples (parsed and checked against their own schemas and limits — an
        example is the part of a document that gets copied), errors, clients,
        conformance (every generated call must reach the route the contract
        names, with a status the contract documents), and drift against a
        committed `tools/route-contract.json`, byte-compared after regenerating
        twice and in reverse route order.
        FIVE NEGATIVE CONTROLS, every one verified to fire by inverting it: a
        document with a route deleted, a body example that is not JSON, a status
        the server never sends, a client built from a contract with the wrong
        path (which must NOT reach the route, or the conformance check proves
        nothing), and a changed status code that must show up as drift.
        Two real defects were found by running it rather than reading it: the
        pull-quote body the document described was not the block the desk's own
        parser accepts, and `properties.js` initialised a `Set` at import time
        under a `"sideEffects": false` promise — caught by the packed-consumer
        smoke once the new entry point widened its scan. -->
      <!-- S4.16 rollup: 3060 kit tests pass (43 new); `tsc` clean on lib and
      spec projects; export map at 62 entry points, `--check` clean; the
      route-contract, vocabulary-scan and packed-consumer gates all pass. -->
- [x] S4.17 Add consumer-driven contract tests for every host application.
      Evidence: incompatible BFF changes fail before merge.
      <!-- `src/consumers.ts` plus `tools/consumer-contract-gate.mjs`
      (`nx consumer-contract-gate workbench-kit`). A route contract says what
      the provider OFFERS and cannot say what anybody NEEDS, which is the whole
      difference between a change that is free and one that takes a host down:
      removing a response field is safe unless somebody reads it, and no amount
      of reading the provider's own document says which.
      THE TWO SIDES ARE PRODUCED DIFFERENTLY ON PURPOSE. The provider surface
      (`tools/provider-surface.json`) is OBSERVED — every guaranteed field is
      one a real handler returned through the mounted router, and every error
      code is one a real deliberate mistake provoked. The consumer manifests
      (`tools/consumer-contracts.json`) are DECLARED by the hosts, because a
      manifest derived from the provider would agree by construction and prove
      nothing; the entire value is in the disagreement. -->
  - [x] S4.17.a Inventory each host application's consumed routes, operations,
        versions, fields, enum/error/state variants, pagination, and streaming
        assumptions. Evidence: host consumer manifests.
        <!-- Three manifests over the archetypes' real routes: studio-web
        (studio-guild), operator-console (reliability-guild) and cli
        (platform-tools). `consumes-nothing` is the fault that catches the real
        omission — a host with an empty manifest is not one with no
        dependencies, it is one nobody has inventoried, and it passes every
        check downstream precisely because it asked for nothing. The five
        non-success STATES are enumerated and all five must be answered for: a
        host that handles `error` and nothing else renders `not-configured` as
        something to retry, `degraded` as fresh and `version-unsupported` as an
        outage, and all three look like the product working. Also refused: a
        window containing no version (the skew matrix comes out empty rather
        than red), an operation that both streams and paginates (two answers to
        "where does the next item come from"), and variants declared for a field
        the host does not read. A FALSE POSITIVE WAS FOUND AND FIXED here by
        running it: duplicate detection folded the request and response field
        lists together, so `feedId` — sent in the intake and returned in the
        plan — was reported as listed twice. -->
  - [x] S4.17.b Capture provider-independent expectations as source-controlled
        contracts with valid/invalid request and response examples. Evidence:
        per-host contract suites.
        <!-- Sixteen committed examples, two per consumed operation, and the
        checks are on the LABELS rather than on the shapes. A "valid" example
        missing a field the manifest says the host reads means one of the two is
        a lie and the suite will pass against a provider that omits it. An
        "invalid" example that satisfies every declared requirement means the
        suite passes whatever the provider does — worse than having no suite,
        because it is counted as coverage. Both are checked by evaluating the
        example against the manifest, so the label has to earn itself. An
        operation with only happy paths is also a finding: a suite with no
        refusals passes against a provider that has stopped validating
        anything. -->
  - [x] S4.17.c Verify current BFF/provider satisfies each consumer contract
        without permissive unknown fallbacks hiding required behavior. Evidence:
        provider verification reports.
        <!-- `verifyProvider` reports per host with its owner, and
        `code-handled-only-by-an-unknown-fallback` is the fault the clause is
        about: a consumer whose default branch shows a generic error technically
        handles every code that will ever exist, and counting that as coverage
        would make every consumer compatible with every provider — a suite that
        cannot fail. The code is reported whatever the policy says; the policy
        only decides how bad it is. `providerSurfaceGaps` ties the surface to
        S4.16's GENERATED contract in both directions, so "the provider offers
        this" is a claim about the mounted routes rather than about a second
        hand-maintained document, and a surface listing variants for a field it
        does not guarantee is refused — otherwise every "the provider cannot
        emit that" finding is decided by a list nothing keeps true. -->
  - [x] S4.17.d Verify consumers tolerate declared additive fields/variants and
        handle not-configured/degraded/unavailable/error/version states.
        Evidence: consumer compatibility tests.
        <!-- `surfaceChanges` classifies and `impactedHosts` decides, and the
        split is the point: whether a change breaks anybody is NOT a property of
        the change. An added enum variant is additive for a host with a default
        branch and a crash for one that switches exhaustively; an added error
        code is additive for a host that degrades on unknown codes and a refusal
        for one that does not. Both pairs are asserted on the SAME change with
        opposite verdicts. A rule that guessed would be wrong for half the
        fleet, and wrong in the reassuring direction. The five states are
        required by the manifest check in (a). -->
  - [x] S4.17.e Test old/new host and BFF versions across the supported
        compatibility window. Evidence: version-skew matrix.
        <!-- A cell per host per release, each unsupported one carrying the
        window that excludes it. The RELEASE window is kept apart from the
        per-operation contract versions on purpose: an operation's version says
        what shape one call has, a release version says which deployment is in
        front of it, and a host can be current on every operation and still
        unable to run against next week's release. `commonVersions` answers the
        question anybody actually asks — "can we deploy this?" — and an empty
        answer is a state the fleet can be in with no single host at fault. The
        gate asserts the release actually being served is among them. -->
  - [x] S4.17.f Add CI ownership and affected execution so a breaking BFF change
        fails before merge with the exact impacted host. Evidence: deliberate
        breaking-change test.
        <!-- Every manifest must name an owner (`no-owner` is a fault: a report
        that cannot say who to tell is one somebody routes by asking around),
        and `breakingChangeReport` emits one build-failing line per impacted
        host naming the host, the owner, the operation and the exact field —
        the difference between a gate people fix and a gate people disable.
        `affectedHosts` is deliberately WIDER than `impactedHosts`: selection
        picks whose tests could possibly notice, not who a static rule already
        calls broken, because selecting the latter would make the suites unable
        to find anything the rule missed. THE DELIBERATE BREAKING CHANGE RUNS IN
        THE GATE: a field a host reads is removed from the observed surface and
        the gate fails unless the report is breaking, names that host's owner,
        and quotes the field.
        EIGHT NEGATIVE CONTROLS, every one verified to fire by inverting it.
        A REAL DEFECT WAS FOUND BY RUNNING THIS RATHER THAN READING IT: every
        archetype refusal came back as a bare status with NO envelope — no code,
        no message key, no correlation — because their message keys
        (`storyDesk.error.…`) have an uppercase letter in the first segment,
        which the kit's own translation-key rule rejects, so `publicError`
        refused and the router fell back to a status alone. Every existing test
        asserted the status and passed straight through it. The keys are now
        domain-prefixed (`veritas.storyDesk.error.…`) and `router.spec.ts`
        asserts a domain refusal carries a real envelope. -->
      <!-- S4.17 rollup: 3096 kit tests pass (36 new); `tsc` clean on lib and
      spec; export map at 63 entry points, `--check` clean; route-contract,
      consumer-contract, vocabulary-scan and packed-consumer gates all pass. -->
- [x] S4.18 Add an API inventory drift gate. Evidence: undocumented and unowned
      routes fail CI.
      <!-- `src/inventory.ts` plus `tools/api-inventory-gate.mjs`
      (`nx api-inventory-gate workbench-kit`). S1.8's `routeInventory` projects
      the routes somebody DECLARED; this inventories the routes that EXIST, and
      the difference between the two sets is the whole item. The ones nobody
      declared are exactly the ones nobody reviews.
      EVERYTHING IS OBSERVED. The inventory is built by mounting the real router
      and reading what it did — which routes it mounted, and, by driving each
      one, the actual pipeline steps each request ran. -->
  - [x] S4.18.a Enumerate framework, router factory, domain plugin, proxy,
        redirect, webhook/callback, streaming, health, and administrative
        routes. Evidence: deterministic API inventory.
        <!-- Nine origins, because nine different things mount HTTP surface and
        only one of them goes through the descriptor table: a gate that
        enumerated the router factory alone would report a clean inventory for a
        deployment whose entire admin surface is unowned. `originCoverage`
        reports every origin INCLUDING the zeroes — "we found no webhooks" and
        "we never looked for webhooks" are the same row in a report that drops
        empties, and only one of them is good news. Deterministic by
        construction: the same routes walked in any order produce one digest,
        asserted by reversing the list. Every entry names its file and symbol,
        because a finding that cannot say where sends somebody grepping. -->
  - [x] S4.18.b Reconcile mounted operations with route descriptors, generated
        contracts, capability registry, owner, audience/host, and lifecycle.
        Evidence: bidirectional mapping report.
        <!-- Both directions, and they are different failures: an entry with no
        descriptor is a route nobody declared — no owner, no audience, no
        lifecycle, nothing that would put it in front of a reviewer — and a
        descriptor with no entry is a declaration nothing serves, which is a 404
        for whoever followed the documentation. Ten faults, every one producible,
        including method/path/host/audiences/lifecycle/version disagreeing with
        the descriptor. `descriptorlessOrigins` is SUPPLIED rather than assumed:
        whether a health endpoint is declared is a deployment's decision, and a
        kit that decided would produce either a permanent false finding or a
        permanent blind spot. A LOGIC BUG WAS FOUND BY THE PRODUCIBILITY TEST
        here — the capability check only ran when the entry and the descriptor
        DISAGREED, so a route and a descriptor both pointing at a capability the
        registry never declared passed clean. -->
  - [x] S4.18.c Detect new, removed, moved, renamed,
        method/path/version-changed, undocumented, unowned, duplicate, and
        unreachable operations. Evidence: drift classifications.
        <!-- Eleven classes. MOVED and RENAMED are kept apart because the fix is
        opposite: a moved operation kept its identity and changed its address, so
        every client needs a new URL and the calling code is fine; a renamed one
        kept its address and changed its identity, so every client's URL still
        works and every generated method name is wrong. Collapsing them into
        "changed" produces a report whose remediation is a guess. The static
        classes hold whether or not anything changed — undocumented, unowned,
        duplicate (two registrations on one address, of which one is unreachable
        and which one depends on registration order), and unreachable (the
        inventory has it, the router did not mount it, so it is documented,
        owned, and answers nothing). -->
  - [x] S4.18.d Ignore comments/formatting/generation noise while retaining
        semantic contract and middleware changes. Evidence: noise-control tests.
        <!-- The digest covers the semantics and excludes the source location and
        the walk order. A gate that fired on a handler moving file is one
        somebody adds `--no-verify` for, and after that it never fires again —
        so the gate proves it by relocating EVERY handler to a generated path
        and requiring zero findings. Middleware stays in, and that is the half
        that matters: a route that quietly stopped running its authorization step
        is byte-identical from the path, and the negative control removes
        `object-authorize` from one route and fails the gate if it is not
        reported. Audiences are compared as a SET, so reordering is noise and
        adding one is not. -->
  - [x] S4.18.e Fail CI with exact route source, missing metadata, affected
        consumers, and remediation command. Evidence: deliberate route addition
        test.
        <!-- `REMEDIATION_FOR_CLASS` is a total map held next to the classes, so
        a new class cannot be added without a command to fix it: a failure that
        says what is wrong and not what to do is one people route to whoever last
        worked it out. Affected consumers come from S4.17's manifests — the kit
        already knows who reads what, so leaving it to the reader would be
        withholding it. THE DELIBERATE ADDITION RUNS IN THE GATE: an undeclared
        administrative route is added to the observed inventory and the gate
        fails unless it is reported as new, undocumented AND unowned, with
        `apps/oshun/bff/src/admin.ts:reindexRoute` on every line. -->
  - [x] S4.18.f Require explicit disposition/deprecation/migration for removals
        and compatibility routes. Evidence: removal mutation tests.
        <!-- Four dispositions — deprecated, migrated, compatibility-route,
        never-shipped — and a removal with none is refused, because "it is gone"
        is the absence of a decision and is indistinguishable from a route
        deleted by a bad merge. Each is checked against something real: a
        deprecation must cite a notice the register actually holds, a migration
        must point at an operation something still serves, a compatibility route
        must carry an end date (one without is a permanent route nobody decided
        to keep), and `never-shipped-claim-for-a-shipped-operation` catches the
        convenient answer — it is the cheapest disposition to write and the
        inventory knows the lifecycle, so the claim is checkable. A disposition
        recorded for an operation still being served is also a fault: a stale
        record silently excuses the next removal. THE REMOVAL MUTATION RUNS IN
        THE GATE, including the false "never shipped" claim.
        SIX NEGATIVE CONTROLS, every one verified to fire by inverting it. -->
      <!-- S4.18 rollup: 3125 kit tests pass (29 new); `tsc` clean on lib and
      spec; export map at 64 entry points, `--check` clean; route-contract,
      consumer-contract, api-inventory, vocabulary-scan and packed-consumer
      gates all pass. §S4 is complete except S4.14/S4.14.g, which stay open:
      their evidence is a browser network assertion and no app mounts the
      capability-health endpoints yet. -->

### S5 — Provider, grounding, trust, and policy seams

- [x] S5.1 Extract `WorkbenchModelProvider` with explicit operations rather than
      a generic text-completion escape hatch. Evidence: unregistered operations
      fail typecheck.
      <!-- `src/model-provider.ts`. The failure this exists to prevent is ONE
      GENERIC METHOD. A port with `complete(prompt: string): Promise<string>`
      has no safety policy, no grounding requirement, no acceptance criterion,
      no cost model and no version — because none of those can be stated about
      whatever the caller put in the prompt. Every one of them then moves into
      the callers, where it is implemented differently in each and reviewed in
      none. So the port takes REGISTERED OPERATIONS, and the kit holds the
      eight domain-neutral CLASSES while the domain holds the operations. -->
  - [x] S5.1.a Inventory domain model operations with distinct inputs, outputs,
        safety, grounding, latency, streaming, tool, cost, and acceptance
        semantics. Evidence: operation register.
        <!-- Eleven register faults, every one producible. The load-bearing one
        is `generation-with-neither-grounding-nor-human-acceptance`: an
        operation that generates free output, grounds it in nothing and is
        accepted by nobody IS a text completion, and giving it a name does not
        change what it is — which is how the escape hatch comes back after being
        removed. The classes are named by what the OUTPUT is rather than by the
        task, because the output decides everything downstream: an extraction
        can be checked against its source and a generation against none of
        those, which is exactly why a generation must state its acceptance
        criterion. Also refused: a tool WILDCARD (blast radius is the whole tool
        registry and it grows every time somebody adds one), a moderation whose
        failure only annotates (no effect unless every caller remembers to read
        it), a zero latency budget (nothing ever fires and it reads as green),
        and personal data admitted with nothing screened for. -->
  - [x] S5.1.b Define stable operation IDs and typed versioned request/result/
        error contracts for each operation class. Evidence: provider contract
        types.
        <!-- `OperationRegister<TId>` is keyed by the operation id, and
        `operationRef` is generic over that key union — which is what makes
        S5.1.e's compile-time refusal a type error rather than a lookup that
        returns undefined. The version is read from the DECLARATION and never
        passed in: a caller that could name the version could name one the
        provider never served. `resolveOperation` keeps the two runtime failures
        apart — an unregistered id is a caller using something that does not
        exist, and a version mismatch is a caller that is simply older than this
        deployment. The result is a three-way `ModelOutcome`, and the third arm
        is the point: an UNKNOWN outcome is a result, not an error, because the
        work may have run and the money may have been spent. -->
  - [x] S5.1.c Define provider capability discovery and operation-specific
        model, region, modality, tool, context, output, and policy support.
        Evidence: capability fixtures.
        <!-- Eight capability faults checked against the register, all
        producible. `policy-the-operation-requires-is-unsupported` is the one
        worth having: a provider serving a screened operation without being able
        to screen for the category returns output that looks exactly like
        screened output, and nothing downstream can tell. `configured: false` is
        a STATE rather than a failure, and a provider that reports itself
        configured while offering nothing is a routing destination that refuses
        everything sent to it. -->
  - [x] S5.1.d Define invoke/stream/cancel/reconcile interfaces that preserve
        idempotency, correlation, usage/cost, provenance, and unknown outcome.
        Evidence: lifecycle interface tests.
        <!-- `reconcile` exists because of the unknown outcome: without it every
        caller resolves "did that run?" by retrying, which is the one thing an
        unknown outcome makes unsafe. Six request faults and six outcome faults.
        The correlation is echoed in the PROVENANCE and not only carried on the
        request — a provider log and an application log that cannot be joined
        are two records of one event. The quiet outcome fault is
        `completed-with-unmeasured-usage-reported-as-zero`: a provider that
        cannot report usage and returns zeroes produces a cost dashboard that is
        confidently wrong, and the most expensive operation is the one that looks
        free. `refused-not-configured-naming-a-credential` refuses a refusal
        that carries a credential VALUE where a setting NAME belongs. -->
  - [x] S5.1.e Register domain adapters explicitly and prevent generic prompt/
        text completion or arbitrary tool invocation through the shared API.
        Evidence: compile-time negative tests.
        <!-- Two `@ts-expect-error` calls inside a function the spec never
        invokes: the assertion is made by `tsc`, and an UNUSED
        `@ts-expect-error` is itself a compile error — so if either call ever
        started type-checking, the spec project would fail to build. Alongside
        it, `modelAdapterFaults` refuses an adapter registered for an operation
        nothing declares (reachable surface with no safety policy), two adapters
        for one operation (which one serves a call depends on registration
        order), and an operation whose last id segment is `complete`, `prompt`,
        `raw`, `chat` or `passthrough` — names for "run whatever I send you",
        because the escape hatch always returns as a legitimately registered
        operation whose input is a prompt. -->
  - [x] S5.1.f Implement two providers with different supported operation sets
        and honest unsupported/not-configured results. Evidence: adapter
        contract suite.
        <!-- `src/testing/model-provider-conformance.ts`, SHIPPED as a
        test-contract subpath rather than kept in the kit's own spec, because the
        adapters worth checking are the ones domains write next year against
        providers this repository has never heard of. Two adapters exercise it
        with disjoint operation sets, and both really compute: the extraction
        scans sentences for claims, the classification counts terms against a
        closed severity set, and the embedding is a real hashing projection whose
        unit norm is asserted to five places. The two probes that carry the suite
        have no convenient correct answer — an operation the adapter does not
        list must come back `unsupported-operation` and NOT retryable (answering
        it is output invented for work it cannot do; marking it retryable turns a
        permanent gap into a retry loop), and an unconfigured provider must
        refuse everything while naming what is missing. The suite is shown to
        CATCH all three: an adapter that answers unlisted work, an unconfigured
        one that answers anyway, and a cancel that always succeeds. -->
      <!-- S5.1 rollup: 3156 kit tests pass (31 new); `tsc` clean on lib and
      spec; export map at 66 entry points, `--check` clean; the packed-consumer
      and vocabulary-scan gates pass. -->
- [x] S5.2 Implement `not_configured` as the production default. Evidence: no
      provider binding results in an exact 503 and zero fabricated output.
      <!-- `src/provider-registry.ts`. The registry starts EMPTY — not "empty in
      production", not "empty unless a fallback is registered", empty in every
      composition until a binding has passed three probes. Every other
      arrangement has the same failure: a deployment ships with no credentials
      and answers anyway, because something plausible was registered as a
      default and nothing in the request path can tell a real answer from a
      convincing one.
      The 503 is `dependency.not_configured`, added to S1.10's register in the
      same change: `after-remediation` and `park-for-operator`, because waiting
      never fixes a missing binding and neither does changing the request. -->
  - [x] S5.2.a Initialize provider registries empty unless an approved runtime
        binding passes configuration/credential/capability probes. Evidence:
        default registry test.
        <!-- `emptyRegistry` binds nothing and `resolveCapability` answers
        not-configured, which is the only state a deployment can be in that
        cannot answer with something it made up. `admitAll` is a FOLD rather
        than a mutable registry, and it returns the refusals alongside the
        result: a deployment that silently admitted two of three providers looks
        exactly like one that was only given two. Eight refusals, every one
        producible, and the one that matters is `probe-never-ran` — an absent
        probe result is refused rather than read as a pass, because the opposite
        default is how a credential check skipped for a release becomes a
        credential check nobody runs. `capability-already-bound` is refused for
        the ordering reason the router uses: which provider serves a call must
        not depend on registration order. -->
  - [x] S5.2.b Remove or isolate demo, fake, deterministic, fallback, and
        heuristic providers behind explicit test/dev-only composition. Evidence:
        production dependency scan.
        <!-- Two layers. At admission, a `fixture`-class binding is refused
        outright in a production composition, and any binding is refused for a
        composition it was not approved for. At the artifact level, S1.13.f's
        `production-scan` target already walks the BUILT package — the emitted
        `.js` and `.d.ts` graphs separately, the source maps, and the exports
        manifest — for test utilities and reserved fixture identifiers, with a
        negative control that re-runs the whole scan over the emitted test files
        and fails if it finds nothing there. It is green at 59 production entry
        points against 85 derived test-utility tokens, with 299 control hits. -->
  - [x] S5.2.c Return typed `not_configured` with capability, safe missing
        category, owner/remediation, and correlation as HTTP 503. Evidence:
        contract fixture.
        <!-- `notConfiguredResponse` projects S1.6.c's `NotConfiguredState` onto
        the wire, and the projection is where the decision is: it carries the
        missing CATEGORIES and never the setting names. `CMS_API_KEY` is exactly
        what an operator needs to hear and exactly what a public error must not
        say — a setting name is a map of the deployment, and the caller who
        triggered this is not the person who can act on it. The owner, the
        resolution authority and the correlation go instead, so the answer is
        actionable by whoever reads it. The status and the code are asserted
        against the shipped register rather than restated. -->
  - [x] S5.2.d Prevent UI/BFF adapters from substituting sample output, empty
        success, cached unrelated output, or inferred scores. Evidence:
        end-to-end negative tests.
        <!-- Four named substitutions, all producible, and all four turn "we
        cannot do this" into something that renders as the product working.
        `empty-success` is the one that gets written by accident: a 200 with an
        empty list reads as "there is nothing here", which is a true statement
        about a different situation. `cached-unrelated-output` compares the
        correlation the body was produced for against the one it is being served
        for — a cache keyed loosely enough to do that is keyed loosely enough to
        cross a tenant. And the checker says NOTHING about a capability that is
        actually bound, so it cannot be satisfied by refusing everything. -->
  - [x] S5.2.e Keep fixture providers visibly labeled and impossible to enable
        in production build/runtime configuration. Evidence: production guard
        tests.
        <!-- `fixture-approved-for-production` checks the DECLARATION and not
        only the registry, because `approvedFor: ['production']` on a fixture is
        one line in a file nobody re-reads and it is the line that makes the
        admission check pass. The labelling half runs both ways over S1.13.d's
        synthetic mark: fixture output without it is indistinguishable from a
        real answer once it has been passed on twice, and RUNTIME output
        carrying it is a fixture wired in behind a real provider's name — which
        is the direction a one-way check would miss. -->
  - [x] S5.2.f Exercise configured, missing credentials, unsupported operation,
        disabled provider, unhealthy provider, and probe failure separately.
        Evidence: capability state matrix.
        <!-- Six situations, asserted DISTINCT in one matrix built from real
        admission refusals rather than from labels. The remedies differ and only
        one of them is the caller's: a missing credential is an operator's
        afternoon, a disabled provider is a decision somebody made, an unhealthy
        one may recover on its own, and an unsupported operation never will — so
        `retryHelps` is false for the disabled provider and true for the
        unhealthy one, and a single "unavailable" would give one retry policy
        that is wrong for one of them. The ORDER is the order of certainty: an
        unsupported operation is refused before health is consulted, because a
        provider that is up cannot start supporting an operation it does not
        implement. And `healthy: null` is `probe-failed` rather than configured,
        since an unprobed provider is not a healthy one. -->
      <!-- S5.2 rollup: 3184 kit tests pass (27 new); `tsc` clean on lib and
      spec; export map at 67 entry points, `--check` clean; the production-scan,
      packed-consumer, vocabulary-scan, route-contract, consumer-contract and
      api-inventory gates all pass. -->
- [x] S5.3 Fence instructions, user content, retrieved sources, tool output, and
      attachments into distinct trust zones. Evidence: indirect-injection eval.
      <!-- `src/trust-zones.ts` and `src/injection-eval.ts`. The failure this
      prevents is a prompt that is ONE STRING: once the policy, the template,
      the question, a retrieved page and a tool's JSON have been concatenated,
      nothing downstream can say which of them said what, and the model infers
      authority from phrasing — the one signal an attacker controls completely.
      THE CANONICAL RENDERER ALREADY EXISTS and the kit may not import it:
      `@oshun/prompt-trust` is `layer:shared` and the kit is buildable (TS6059),
      the same shape as S4.2 and S4.11. So the kit owns the CONTRACT, the
      CHECKS and the CORPUS; the conformance spec imports the real renderer as
      a devDependency and runs the real corpus through it. Writing a second
      sanitiser here would have produced two that drift, and the one that
      matters is the one in the request path. -->
  - [x] S5.3.a Define immutable trust-zone types and precedence for
        system/policy, developer template, user request/content, retrieved
        source, tool result, attachment, and prior generated output. Evidence:
        trust contract.
        <!-- Precedence is a TOTAL order rather than a trusted/untrusted flag,
        because the question asked at runtime is always between two named zones
        — "the retrieved page contradicts the template, which wins?" — and a
        partial order has pairs with no answer, which then get settled by
        whoever writes that branch and settled differently in the next file.
        Rank comes from the KIND and a zone never carries its own: a zone that
        could state its rank would be ranked by the assembler, and the assembler
        stands closest to the attacker's bytes. The AUTHOR is what an attacker
        controls rather than the kind, so a "developer template" that is really
        a database row an operator edits is refused, and a system policy may
        come from the repository and nowhere else — deployment policy is
        configuration SELECTING among repository-authored policies, or an
        operator zone one rank lower that can narrow and never widen.
        `user-request` outranks `user-content` because the human authored one
        and merely POSSESSES the other, which is the whole of indirect
        injection. `prior-generated-output` ranks last, below the attachment:
        it is the only zone with no external author, and one instruction that
        survives one turn comes back as something already decided. Eight
        precedence faults, all producible, and the shipped tables are asserted
        to produce none. The first version of the unaccountable-author check
        fired on the CORRECT table — `user-request` is authored by the end user,
        which is what it is — the same failure S4.15 had; it was narrowed to
        `binding` and `directive`, with a test naming the mistake. -->
  - [x] S5.3.b Serialize each zone with unambiguous boundaries, source identity,
        authority label, data classification, and truncation status. Evidence:
        request assembly tests.
        <!-- Thirteen assembly faults, checked on the rendered STRING rather
        than on the object that produced it, because every failure here is a
        property of the string: a fence the content can forge, a label a
        template change dropped, a note somebody added between two blocks. A
        check that reads the object confirms the object.
        `unfenced-text-between-zones` is the one that catches the real
        regression — a fencing scheme is never removed, it is bypassed by one
        helpful line ("Note: the requesting user is an administrator") appended
        by somebody fixing a ticket, and that line has no author, no rank and no
        classification while sitting at the top of the model's attention. Its
        threshold is 16 characters and not zero, because a finding that fires on
        "\n\n" is a finding somebody switches off.
        `truncation-recorded-but-not-visible-to-the-reader` is the quiet one:
        the only reader who could compensate for a document being cut in half is
        the model, and it reads the fence rather than the audit record. Zones
        must also be emitted in precedence order, since the first
        instruction-shaped text in a prompt sets the frame the rest is read
        against. -->
  - [x] S5.3.c Prevent untrusted zones from altering system policy, tool
        availability, authorization, output schema, or evidence standards.
        Evidence: precedence tests.
        <!-- Each of the five controls carries a FLOOR: the lowest-ranked zone
        permitted to influence it. `authorization`'s floor is `null` — not "only
        the system policy may decide it" but NO zone at all, because the answer
        is read from the actor and the grant store before assembly begins, and a
        control whose floor is any zone is a control somebody eventually decides
        from a prompt. The load-bearing fault is
        `control-decided-after-the-model-ran`, and it is the one that looks fine
        in review: a tool allow-list computed from the model's chosen call is
        not an allow-list, it is a record of what happened; filtering output
        against a schema the output selected is not validation. In each case the
        control is still there, still logged, and no longer a control. Also
        checked: a control nothing states took whatever default the assembly
        code happened to have, and an unstated default is the one nobody
        reviews. -->
  - [x] S5.3.d Sanitize/normalize title, metadata, hidden text, markup, URLs,
        filenames, archive members, media metadata, and multimodal text.
        Evidence: indirect-injection corpus.
        <!-- Ten channels with a disposition each, because `body-text` is the
        channel everybody sanitises and the one attacks stopped using: the other
        nine reach the model through code that FORMATS rather than fences —
        `Document: ${doc.title}` — and a title is attacker-controlled in exactly
        the way the body is, with none of the handling. `hidden-text` is
        `removed` rather than sanitised, since there is no legitimate reason for
        text a human reader cannot see to reach a model that acts on it, and the
        removal must be DISCLOSED or the model summarises a document that says
        something different from the one the user is looking at. The kit does
        not normalise — it VERIFIES, given what arrived and what is about to be
        emitted, which composes with any sanitiser including one written next
        year. The real algorithm it does own is the invisible-character scan:
        Unicode tag characters (U+E0000–U+E007F, a complete ASCII alphabet that
        renders as nothing and is the case an eyeball review cannot perform),
        interlinear markers, and a bidi BALANCE check rather than a presence
        check — the 2021 trojan-source result turns on a run that is never
        popped, and balance is evaluated per LINE because the bidi algorithm
        resets at a paragraph separator, so a whole-string counter would report
        every multi-paragraph RTL document. Zero-width joiners are allowed in
        prose (load-bearing in Devanagari and in emoji sequences) and refused in
        identifier channels, where their only effect is to make two different
        names render identically. -->
  - [x] S5.3.e Minimize tools and bind typed arguments to actor/tenant/resource/
        side-effect policy outside the model. Evidence: tool authorization
        tests.
        <!-- The model chooses WHICH tool and never WHOSE data. A
        `context-bound` argument's value is taken from the request and the
        model's value for it is DISCARDED rather than validated — because
        validating a model-supplied tenant id against the actor's tenants is a
        correct check that still leaves the model picking which of the actor's
        three tenants gets written to, and the test asserts exactly that
        difference. `identity-argument-left-model-supplied` is the confused
        deputy in one line: the model holds the deployment's credentials while
        reading the attacker's document.
        `external-effect-without-confirmation-beside-inert-zones` is compound on
        purpose — the tool alone is fine, the untrusted zone alone is fine, and
        the two in one call with no human between them is the arrangement where
        a page decides what gets sent and to whom. `minimizeTools` is an
        intersection computed BEFORE assembly, since minimisation applied
        afterwards is minimisation of a list the model has already been
        shown. -->
  - [x] S5.3.f Evaluate direct, encoded, multilingual, split, multi-turn,
        retrieved, tool-result, attachment, URL, and multimodal attacks.
        Evidence: versioned adversarial report.
        <!-- 23 cases across all ten vectors and all five controls, graded by
        the kit's own production checks and run in `injection-eval.spec.ts`
        against the repository's REAL renderer. THE CORPUS IS NOT A FILTER:
        nothing in the request path consults it, and it would be worthless
        there, because a defence that works by recognising the attack string
        fails on the string it has not seen. Four gradings carry the report. A
        case that did not RUN is graded as harshly as one that escaped, or a
        suite improves its score by erroring. A payload that never reached the
        prompt is `inconclusive` rather than a pass — a pipeline that drops the
        attachment on a parse error would otherwise sweep the attachment vector,
        and that vector is about the attachment being READ. Coverage is gated on
        BOTH margins, so forty direct cases and no attachment cases is not 97%
        covered; the vector×control matrix is published rather than gated, since
        fifty pairs is a gate somebody deletes. And the corpus pins a LITERAL
        contract fingerprint: the first version evaluated
        `trustContractFingerprint()` here, which equals the live value by
        construction and made the staleness check unable to fire in the one
        situation it exists for. Two tests are the evidence the composition is
        load-bearing rather than decorative: with the invisible-strip step
        removed and nothing else changed, exactly `encoded-003`, `encoded-004`
        and `multilingual-002` escape — `@oshun/prompt-trust` normalises,
        neutralises encoded delimiters and bounds length, and does not strip tag
        characters or balance bidi runs; and with a pipeline that lets the
        payload's zone influence its target control, all 23 escape. -->
      <!-- S5.3 rollup: 3266 kit tests pass (81 new); `tsc` clean on lib and
      spec; export map at 69 entry points, `--check` clean; the production-scan,
      packed-consumer, vocabulary-scan, route-contract, consumer-contract and
      api-inventory gates all pass. The packed-consumer gate caught real work at
      import time in both new modules (`Object.freeze`, a `Symbol()` brand, two
      `new Set`s and a `tagEncode` call) — `sideEffects: false` tells a bundler
      it may drop exactly that, so the tables are plain literals, the brand is
      minted on first use behind a `declare const`, and the tag-block payload is
      an escaped literal whose plaintext a test asserts. -->
- [x] S5.4 Require structured-output schema validation. Evidence: malformed,
      truncated, extra-field, and wrong-version outputs are rejected.
      <!-- `src/structured-output.ts`. The failure this prevents is
      `JSON.parse(response)` — not because parsing is wrong, but because of
      everything that sentence skips: no declared schema, no bound checked, a
      parser that accepts a duplicate key and silently keeps the last one, bytes
      that may be the first half of an answer, and the original discarded the
      moment it succeeds. Each is a separate failure and each presents as the
      same thing, an object that looks right. Six fault families, 45 faults, all
      producible. -->
  - [x] S5.4.a Select exact operation/result schema and version before provider
        invocation and include provider-supported structured mode where
        available. Evidence: request contract tests.
        <!-- The schema is pinned BEFORE the call, because choosing the
        validator once the output is in hand means output that happens to match
        a laxer schema is accepted — S5.3.c's
        `control-decided-after-the-model-ran` with a JSON parser attached. A
        provider's structured mode is a guarantee about SYNTAX and nothing else:
        a grammar can force `{"approved": <boolean>}` and cannot make the
        boolean right. So the strongest available mode is requested (declining
        it means paying for a retry to learn something the provider would have
        refused to emit) AND
        `structured-mode-used-instead-of-validation` is a fault — it is the only
        one in this section that makes something PASS rather than fail, and the
        team that turns on JSON-schema mode and deletes their validator has not
        moved validation to the provider; the provider is checking a shape and
        nobody is checking what is in it. -->
  - [x] S5.4.b Bound raw bytes/tokens/depth/arrays/strings/numbers and detect
        incomplete/stream-truncated output before parsing. Evidence: limit
        tests.
        <!-- All of it runs BEFORE the parse, because a truncated response can
        still parse: `{"total": 4}` is valid JSON and valid against any schema
        whose remaining fields are optional, so the object is complete, the
        validation passes and nine tenths of the answer is missing with nothing
        anywhere saying so — the test asserts exactly that pair. The finish
        reason is the only place that fact exists, and
        `unknown-finish-reason-treated-as-complete` is the quiet version: the
        default reading of an absent measurement must never be the good one.
        `content-filter` is kept separate from `length`, because partial text
        after a refusal is not a partial answer. The default depth budget is 32:
        a document nested 200 000 deep is ~400 kB of brackets, comfortably
        inside any byte budget. -->
  - [x] S5.4.c Parse strictly and reject malformed syntax, duplicate keys,
        invalid Unicode/numbers, schema mismatch, unknown forbidden fields, and
        wrong discriminator/version. Evidence: adversarial fixtures.
        <!-- `scanJsonSource` is a real strict scanner over the SOURCE, and it
        has to be, because three of its findings do not survive parsing.
        `JSON.parse('{"approved":false,"approved":true}')` returns `true` — the
        last one wins, the first leaves no trace, and anything able to append to
        a response has a rewrite primitive; the spec asserts that V8 behaviour
        rather than describing it. `9007199254740993` parses to
        `...992` with no error at all, so an identifier arriving as a large
        integer is silently a different identifier. And a lone surrogate becomes
        U+FFFD in whichever component re-encodes first. The scanner keeps an
        EXPLICIT STACK rather than recursing, which is not style: a
        recursive-descent parser meets a depth bomb by exhausting the call
        stack, so the check written to report the attack is what the attack
        breaks. The test shows the hazard is real — V8's own `JSON.parse` is
        iterative and survives 200 000 levels, and the first ordinary recursive
        walk over the object it returns throws `RangeError`. Duplicate detection
        is scoped per OBJECT, since the same key in two sibling objects is
        ordinary and a check that fired on it would be switched off in a week.
        The envelope is deliberately not a schema language — the kit shipping
        one would be a second validator beside the domain's, and two validators
        disagreeing is what this module is about; what the kit owns is the
        discriminator, the version and the field allowlist, because those decide
        WHICH validator runs. An unknown field is refused rather than ignored:
        a field nobody declared is a field nobody reviewed, and in a model's
        output it is additionally a place to put things. `Object.hasOwn` rather
        than truthiness, so `"verdict": null` is an answer that was given rather
        than one that was missing. -->
  - [x] S5.4.d Validate cross-field/domain invariants, stable references, units,
        authorization scope, and prohibited instructions/URLs after schema
        validation. Evidence: semantic validation tests.
        <!-- `citation-to-a-source-that-was-never-supplied` catches a
        fabrication rather than a bug: an id resolving to nothing is the shape a
        made-up reference takes once the format is strict enough to stop made-up
        prose. `reference-to-an-entity-outside-the-actor-scope` is the
        cross-tenant leak, checked here rather than at render time because the
        id is in the result the moment it validates and everything downstream
        treats a validated result as a checked one. An invariant that THROWS is
        reported as violated rather than swallowed — a check that errors is not
        a check that passed. And the ledger's "prohibited instructions" is
        answered WITHOUT scanning the output for instructions, which would have
        been the S4.15 mistake again: whether the output contains an instruction
        does not matter if it returns in an inert zone, and no amount of
        scanning saves it if it returns above one. So the check is
        `output-readmitted-above-a-generated-zone`, read off S5.3's rank. -->
  - [x] S5.4.e Preserve raw response hash/ref and validation errors; never
        repair silently into a passing result. Evidence: invalid-output
        provenance tests.
        <!-- The line is drawn on what a repair CHANGES, not on its size:
        trimming a trailing newline and supplying a missing required field are
        both one line of code. Whitespace, encoding normalisation and stripping
        a ```json fence remove ambiguity about what the model said; adding a
        field, removing one or coercing a value WRITE what it said and file the
        result under its name. `accepted-with-faults-recorded` is the silent one
        and the reason the record exists: somewhere between "the validator found
        three problems" and "the result was used" there is a branch that decided
        they did not matter, and nothing in the accepted object shows it — a
        validator whose findings can be recorded and ignored is a logging call.
        A rejection must keep the raw bytes, since the one case where the exact
        bytes are certainly needed is the one where validation failed. -->
  - [x] S5.4.f Define bounded retry-with-correction only for approved error
        classes and retain every attempt/cost/provenance. Evidence: retry tests.
        <!-- Only FORM is correctable: `lexical` and `envelope` are statements
        about the shape of the answer and a model can be told which shape to
        use. `semantic` is deliberately excluded — a model told "that citation
        did not resolve" returns one that does, and the second answer is worse
        than the first because it now looks correct. `selection` and `pre-parse`
        are excluded because neither is the model's doing: retrying a truncation
        at the same token budget truncates again. A MIXED set is refused
        outright, since fixing the syntax of an output that also cites a
        non-existent source leaves the content problem behind a response that
        now parses — and `correctionAllowed` returns the blocking families,
        because "no" without a reason is a decision somebody works around.
        `only-the-final-attempt-retained` is checked by requiring the attempt
        numbers to be exactly 1..n: a pipeline that keeps the attempt that
        worked reports a cost short by the retry factor, and the operations that
        retry most are the ones being under-reported.
        `schema-loosened-between-attempts` is the escape hatch reopening — the
        bar moved after somebody saw the output — and
        `prior-output-fed-back-above-a-generated-zone` is the S5.3 tie: a
        correction prompt is the shortest path from "the model said something
        wrong once" to "the model was instructed to say it". -->
      <!-- S5.4 rollup: 3323 kit tests pass (57 new); `tsc` clean on lib and
      spec; export map at 70 entry points, `--check` clean; all seven gates
      pass. Two barrel collisions caught by `tsc` only after the index edit
      (`semanticFaults` with `error-mapping.ts`, `provenanceFaults` with
      `blobs.ts`, plus `Quantity` with `diff.ts`) — renamed to the
      `result*`/`validation*`/`Output*` families. The packed-consumer gate again
      caught import-time work (`new Set` for the whitespace table). -->
- [x] S5.5 Preserve provider response provenance before normalization. Evidence:
      raw response hash and normalized projection are linked.
      <!-- `src/response-provenance.ts`. The failure this prevents is a
      normalized record that is the ONLY record. Normalization is lossy and not
      reversible: once a response has been mapped into the shape the rest of the
      system wants, "what did the provider return?" can only be answered from
      that shape — a projection made by an adapter whose version nobody wrote
      down. The day the adapter turns out to have been wrong, every artifact
      downstream is wrong and there is nothing left to re-derive them from. Six
      fault families, 44 faults, all producible. -->
  - [x] S5.5.a Capture provider operation/request IDs,
        provider/model/deployment/ version/region, parameters,
        tool/template/policy versions, actor, and timestamps before
        normalization. Evidence: run metadata tests.
        <!-- `captured-after-normalization` is load-bearing and invisible in a
        working system: everything reconciles until the first response that
        makes the adapter throw, which produces no record at all — so the
        population with no provenance is exactly the population somebody would
        want to look at. `model-without-a-deployment` is the quiet one: on every
        hosted platform the model NAME is not the thing serving the request, two
        deployments of one name differ in version, region, quota and content
        filter, and a record naming only the model cannot tell a regression in
        one from a regression in the other. The tool-version check is scoped to
        the tools the run could REACH rather than the ones it called, since a
        tool that changed behaviour makes two runs incomparable whether or not
        it fired. Parameters are recorded as a DIGEST so two runs can be
        compared without copying temperature and seed into every row. -->
  - [x] S5.5.b Store or securely reference exact raw response bytes/event stream
        and compute hash/length/media type under retention policy. Evidence: raw
        response integrity tests.
        <!-- A hash without the bytes is an assertion, not evidence: a digest
        says whether bytes you already hold are the recorded ones and cannot say
        what they were. So an absent artifact must carry a REASON, and the four
        are kept apart because "deleted on schedule" and "the write silently
        failed" look identical in every listing and the difference is discovered
        during an incident. A reference AND an absence reason together is
        refused, since one of the two is stale and nothing says which. Zero
        length is checked against the SHA-256 of zero bytes rather than treated
        as empty, because a length taken from one place and a hash from another
        have already disagreed. Stream events and single bodies are checked in
        both directions. Digest algorithms come from `blobs.ts`'s approved set
        rather than a second list. -->
  - [x] S5.5.c Record transport/status/headers allowlist, finish reason, usage,
        safety, provider errors, cancellation, and partial/truncation state.
        Evidence: provider outcome fixtures.
        <!-- Headers are an ALLOWLIST and never a capture: storing them
        wholesale stores the authorization echo, the `set-cookie`, the
        rate-limit token and whatever the provider starts sending next quarter,
        into the record most likely to be exported, replicated and kept longest.
        A separate FORBIDDEN set is checked as well as the allowlist, because
        the allowlist is a list somebody edits and the forbidden set is what
        must survive the edit. `blocked-safety-verdict-on-a-complete-outcome` is
        the contradiction worth catching: the artifact will be used and the
        safety record will say it never should have been, and both are true in
        the store. A 200 carrying a provider error code is kept apart from a 500
        with none, since a reader that switches on the status has already
        treated the first as a result. -->
  - [x] S5.5.d Normalize through a versioned adapter and store adapter version,
        schema, validation result, warnings, and normalized hash. Evidence:
        normalization provenance tests.
        <!-- The adapter version is part of the OUTPUT, because two deployments
        running different mappings over identical bytes produce different
        records and nothing can otherwise tell which produced which.
        `normalized-digest-equal-to-the-raw-digest` catches an adapter that did
        nothing, which is not hypothetical: a passthrough left from a migration,
        a branch that returns its input on an unrecognised shape, and a digest
        computed over the wrong variable all produce it and all three look like
        a normalization that ran. A failed validation whose projection was KEPT
        is a fault and one whose projection was discarded is not — the failure
        is a bad projection sitting in the store beside the good ones, where the
        next query does not join on validation status. -->
  - [x] S5.5.e Link every normalized field/output/score to originating response
        or mark derived/defaulted/rejected explicitly. Evidence: lineage tests.
        <!-- Four lineages because they are four different things.
        `defaulted` must not fold into `supplied`: a confidence of 0.5 the
        adapter filled in because the provider sent none reads, in every chart
        and every threshold, exactly like one the model computed — so a default
        may name no source and must give a reason.
        `supplied-field-with-more-than-one-source` makes the supplied/derived
        line enforceable: a value assembled from two places is a computation
        however simple, and an audit tracing it lands on one input and concludes
        the provider said something it did not. A `rejected` field must be
        ABSENT from the projection, since every reader takes the projection. And
        coverage runs the other way too — a projection field nothing traces may
        have come from the response, a computation or a constant, and all three
        read the same. -->
  - [x] S5.5.f Enforce authorization, encryption, redaction, retention, legal
        hold, and deletion for raw provider content. Evidence: provenance data
        lifecycle tests.
        <!-- Retention, holds, encryption and purge are NOT reimplemented here:
        `deletion.ts`, `protection.ts` and `blobs.ts` already hold every other
        artifact in the workbench to them, and a second copy for provider
        content would be the one that misses the next hold. What this owns is
        the composition and the two rules specific to this data. Raw provider
        content takes the STRICTEST classification of everything on both sides
        of normalization, because it holds what the redactor removed on the way
        to the projection — classifying it from the projection classifies it
        from the copy with the sensitive parts already taken out.
        `raw-readable-by-more-roles-than-the-projection` is the first specific
        rule: access is granted on the projection and then extended to "the
        underlying record" by whoever is debugging, and the raw response is the
        unredacted original. `projection-outliving-the-raw-it-came-from` is the
        second and is the inversion people expect — keeping the derived record
        after the original is gone leaves an artifact nothing can substantiate.
        A purge under an open hold is refused, since a hold a document purge
        respects and a transcript purge does not is a hold with a gap in it. -->
      <!-- S5.5 rollup: 3366 kit tests pass (43 new); `tsc` clean on lib and
      spec; export map at 71 entry points, `--check` clean; all seven gates
      pass. Four more barrel collisions caught only after the index edit
      (`ProviderOutcome` with `router.ts`, `NormalizationFault` with
      `properties.ts`, `LINEAGE_KINDS`/`LineageKind` with `revisions.ts`) —
      renamed to `*Record`/`FIELD_LINEAGE_*`. -->
- [x] S5.6 Implement cancellation, timeout, retry classification, and
      non-retryable safety refusals. Evidence: deterministic fault-injection
      tests.
      <!-- `src/provider-lifecycle.ts`. The failure this prevents is a system
      where every one of these is PRESENT and none of them reaches the provider:
      cancellation stops the spinner while the tokens keep being generated, a
      client timeout fires while the call runs to completion on the far side,
      and a retry loop treats a safety refusal as transient and asks eleven more
      times. All three look correct in the code that contains them; what is
      wrong is what they do not reach. Seven fault families, 45 faults and
      refusal reasons, all producible; the deterministic part of
      "deterministic fault-injection" is that the jitter is a PARAMETER. -->
  - [x] S5.6.a Propagate user/job cancellation through queue, worker, provider
        request/stream, tools, and downstream side effects. Evidence:
        cancellation boundary tests.
        <!-- Cancellation propagates SOURCE-FIRST, and the order is read from
        `CANCELLATION_STAGES` rather than restated in the check. The other order
        has a race with a name: stopping the worker while the queue is still
        live hands the same job to the next worker, and the cancellation is now
        racing the redelivery it caused. A stage that cannot be cancelled must
        NAME ITS COMPENSATION — an email has been sent, a webhook delivered —
        because "not reachable" with nothing beside it is a cancellation that
        reported success and changed nothing, which is worse than one that
        failed since nobody looks again.
        `reported-cancelled-while-a-stage-is-still-running` is the one users
        meet: the request leaves the interface, the provider call runs to
        completion and is billed, and the only visible consequence of cancelling
        was that nobody is watching. Timestamps are checked as well as ranks,
        because a stage can be listed in order and reached out of it. -->
  - [x] S5.6.b Define connect, first-byte, idle, total, tool, stage, and job
        deadlines and preserve partial/unknown outcome. Evidence: clock tests.
        <!-- All seven, and DEADLINES NEST — the nesting is the check. A connect
        timeout of 60s under a total of 30s means the total can never fire,
        because the thing it bounds is still inside a connect that has not given
        up; every layer here has a version of that bug. An expiry is an UNKNOWN
        OUTCOME and never a failure (S5.1's third decision reached from the
        other side): a deadline says how long WE waited and nothing whatever
        about what the far side did, so recording it as failed is how a charged
        request is retried. `succeeded-with-partial` is refused for the opposite
        reason — the part that arrived before the timeout is being handed on as
        the answer, and every reader downstream takes it for a complete one. And
        the partial must be RETAINED, since it is the only evidence of how far
        the call got and the retry that follows starts from nothing. -->
  - [x] S5.6.c Classify throttle/capacity, transient network, timeout,
        unavailable, malformed output, policy refusal, safety refusal,
        authorization, invalid input, quota, and unknown errors. Evidence:
        classification table tests.
        <!-- All eleven as ONE TABLE with a retryability (from `errors.ts`), a
        family (from `error-mapping.ts`), whether the outcome is unknown and
        whether it may already have been charged. A classification spread across
        eleven `if` statements in six adapters is eleven decisions nobody can
        read together, and they will disagree. `malformed-output` is retryable
        and is not a refusal, because the model produced something and it was
        the wrong shape — the one failure a second attempt genuinely fixes. The
        test names the three cells where a retry can duplicate a charge
        (`timeout`, `transient-network`, `unknown`), which is the whole reason
        S5.6.f exists. AN EARLIER CHECK WAS REMOVED for the S4.15 reason: "a
        family shared by more than one class" fires on the CORRECT table, since
        transient-network, timeout and unavailable are all rightly `dependency`
        — the remedy for all three is to wait. -->
  - [x] S5.6.d Retry only approved transient classes with idempotency, bounded
        attempts/age/backoff/jitter, budget, and cancellation. Evidence: retry
        schedule tests.
        <!-- Full-jitter backoff — a uniform draw across the whole window rather
        than the window plus a wobble, because equal jitter leaves every caller
        retrying in the same half-second and the thundering herd survives it —
        with the jitter supplied as a PARAMETER so the schedule is testable and
        the function stays deterministic. The budget's fourth bound is the one
        that matters: a per-WINDOW retry share ceiling, because per-request
        bounds are what turn a partial outage into a full one (every caller
        retries three times, the provider sees four times the load at exactly
        the moment it is failing, and what was degraded is now down). The
        refusal ORDER is the order of certainty: cancellation first, since a
        retry after the user walked away is work nobody will read; then the
        refusal classes, which no budget makes retryable; then the
        unknown-outcome cells last, because those need the reconciliation to
        have run. -->
  - [x] S5.6.e Never automatically retry or prompt-around safety/policy refusal,
        unauthorized action, invalid consent/rights, or nonretryable validation.
        Evidence: refusal negative tests.
        <!-- `prompt-changed-after-a-refusal` is the whole point, and it is why
        an attempt record carries a PROMPT DIGEST: an automatic re-ask with
        different wording after a safety refusal is indistinguishable from a
        legitimate retry unless the two prompts are compared. It is not error
        handling — it is the system looking for a phrasing that gets the answer
        that was declined, run at machine speed with nobody in the sequence, and
        it is reached by ordinary-looking code that regenerates on failure. The
        `humanInitiated` exception is narrow and deliberate: a person who reads
        a refusal and edits their request is doing something the system should
        support, and the fault is the loop that does it without them.
        `refusal-reclassified-as-transient-on-a-later-attempt` catches the other
        route — whichever classification is wrong, a retry loop takes the
        permissive one. -->
  - [x] S5.6.f Reconcile external request status after local timeout/worker loss
        before repeating costly or irreversible operations. Evidence:
        unknown-outcome tests.
        <!-- `retries.ts` already decides this for an effect, on REVERSIBILITY.
        What is added here is COST as a separate axis: a model call is usually
        reversible in the sense that nothing durable changed, and never
        reversible in the sense that matters to a budget, so a pipeline
        reasoning only about reversibility repeats an expensive generation
        freely and discovers it at the end of the month. A repeat with no
        provider request id is refused outright, since there is no handle to ask
        with and the only available answer is a guess that costs money. And the
        indeterminate cell refuses rather than retrying — it is the cell people
        fill in with "retry" because the alternative needs a person, and it is
        the one that produces the duplicate charge. -->
  - [x] S5.6.g Persist attempt history, partial outputs, costs, reason, and
        final state without false success. Evidence: fault-injection report.
        <!-- `succeeded-with-no-successful-attempt` is the false success in its
        plainest form and it is reached honestly: a `catch` falling through to a
        default result, a state machine whose terminal transition is
        unconditional, a reconciliation that filled in a value — all three
        produce a run that reads as having worked.
        `total-cost-below-the-sum-of-attempts` is the same shape applied to
        money, since the attempts that failed cost exactly what the one that
        worked cost. `attempt-numbers-not-contiguous` is how a dropped attempt
        is detected at all: its cost and its partial output went with it. And a
        run may not be closed while it still contains an attempt whose outcome
        nobody knows. -->
      <!-- S5.6 rollup: 3414 kit tests pass (48 new); `tsc` clean on lib and
      spec; export map at 72 entry points, `--check` clean; all seven gates
      pass. Four more barrel collisions (`REFUSAL_CLASSES`, `retryDecision`,
      `RetryDecision`, `StageEffect`) renamed to the `provider*`/`Cancellation*`
      families. The `public-api` spec catches a stale generated map before the
      `--check` does, so regenerate before running the suite. -->
- [x] S5.7 Implement per-run cost estimate, reservation, actual cost, and budget
      variance. Evidence: over-budget dispatch is blocked before provider
      invocation.
      <!-- `src/cost-accounting.ts`, the MONEY layer above `limits.ts` — which
      already owns `SpendBudget`, `available`, `reserve` and `settle` and is
      scope-agnostic on purpose. The failure this prevents is a budget that can
      only be EXCEEDED: cost measured after the work arrives too late to change
      anything, and the amount by which it is wrong is unbounded, because one
      runaway job spends a month's allowance and the limit reports it the
      following morning. Seven fault families, 47 faults and refusal reasons,
      all producible. -->
  - [x] S5.7.a Maintain versioned provider/model/operation/tool/input/output/
        region unit prices, currency, effective period, and source. Evidence:
        rate-card fixtures.
        <!-- Money is INTEGER MINOR UNITS at a stated scale, because a price of
        $0.000003 per token is not representable in cents and is exact in
        millionths of one, and floating-point money accumulates a discrepancy
        across a million calls that nobody can reconcile afterwards. Currency
        travels with every amount rather than being a deployment constant, since
        two providers billing in two currencies summed into one "cost" produces
        a number in no currency that will be compared against a budget in a
        third. `gap-between-periods-for-one-key` is the one that costs money
        quietly: an overlap is noticed because two prices disagree, and a gap is
        not, because a lookup that finds nothing returns nothing and the caller
        adds nothing to the total — so the run in the gap is free, on the day
        the price changed, which is the day the totals are being watched. An
        abutting pair (one period ending exactly where the next begins) is
        correct and is not reported. And a price must name a SOURCE, because one
        nobody can trace to a page, a contract or an invoice is a number
        somebody typed and it is what every total is built on. -->
  - [x] S5.7.b Estimate cost from validated request, context, output limits,
        retries/tools/derivatives, and uncertainty before acceptance. Evidence:
        known-price tests.
        <!-- An estimate is an INTERVAL and its upper bound is RE-DERIVED by the
        checker rather than trusted, which is the only way this is worth having:
        an estimator that quietly uses the expected output length produces an
        interval that looks exactly like a correct one and is wrong by the ratio
        between typical and maximum — for a generation, often a factor of ten. A
        budget sized on the typical case is exceeded by a run that behaved
        perfectly legally, and that run is not a bug anybody can fix. The
        derivation must cover the retry bound and the tool bound as separate
        terms, both checked by re-deriving with that bound removed and asserting
        the stated ceiling is larger. Derivative operations (an embedding of the
        output, a moderation pass) must appear in a term — matched on a WHOLE
        TOKEN after a bare `includes` was found to let an id of `x` match
        `maxToolCalls`, which is a check reporting coverage it does not have.
        And an operation with no declared output ceiling is refused rather than
        estimated: the upper bound of an unbounded output is not a number. -->
  - [x] S5.7.c Reserve actor/project/tenant/provider budget atomically with run
        acceptance and reject concurrent oversubscription. Evidence: budget race
        tests.
        <!-- `limits.ts` decides whether an estimate FITS, and that decision is
        taken against a snapshot — so between reading availability and writing
        the reservation, any number of concurrent runs each individually fit,
        and the budget is oversubscribed by exactly the number of workers.
        `read-then-write-without-a-version` is that race named, and the shipped
        contract is a conditional write carrying the version each scope was read
        at. `partial-reservation-across-scopes` is the other half: a run draws
        on four budgets, and if two are written and two fail, budget is held
        against a run that was not accepted and nothing will release it because
        nothing recorded it. A reservation must also EXPIRE, or a worker that
        crashes between reserving and settling shrinks the budget by one run
        permanently, with nothing in the system spending it. -->
  - [x] S5.7.d Recheck reservation immediately before each provider/tool
        dispatch and prevent invocation when insufficient or expired. Evidence:
        predispatch failure tests.
        <!-- The gap between reserving and dispatching is where the queue is,
        and it can be hours; a reservation taken at acceptance and never
        rechecked is a reservation against a budget since spent by everything
        that queued behind it, and the first anybody hears of it is the invoice.
        An expired reservation is REFUSED rather than renewed — renewing it
        silently grants budget that was not available at acceptance, on the
        strength of the run having waited. The comparison is against the
        dispatch's UPPER bound rather than its expected cost, because
        dispatching on the expected cost permits a legal run to overrun the
        check that just passed. -->
  - [x] S5.7.e Capture provider-reported and independently derived actual usage/
        cost per attempt/tool and handle missing/disputed billing. Evidence:
        metering tests.
        <!-- TWO measurements, because the provider's number is simultaneously
        the one you are billed on and the one you cannot check; the independent
        derivation from the recorded request and response is not a better number
        — it is the only way to notice that the billed one moved. Missing
        provider usage recorded as zero is a fault rather than a value.
        `tolerance-wide-enough-to-hide-a-doubling` exists because a tolerance is
        where a disagreement goes to be forgiven, and it is set by whoever is
        tired of the alert: at 100% it silences a bill that doubled, which is
        the one event the comparison exists for. -->
  - [x] S5.7.f Release/refund unused reservation and reconcile estimate,
        reserved, actual, cancelled, failed, retried, and unallocated values.
        Evidence: reconciliation report.
        <!-- Reconciliation is an EQUATION: reserved = settled + released +
        still-held. A residual nobody can name is budget that has leaked, and it
        leaks in the direction of LESS apparent spend, because it is money that
        was held and is now attributed to nothing.
        `budget-still-held-on-a-finished-run` is how the leak usually happens —
        the happy path settles and releases and the failure path returns early —
        and held budget on a run that is still RUNNING is correct and is not
        reported. A cancelled run that released nothing shrinks the budget every
        time somebody presses cancel. -->
  - [x] S5.7.g Record absolute/percentage variance and stable reason and alert
        on threshold anomaly. Evidence: variance scenario tests.
        <!-- BOTH thresholds must be crossed. A one-penny estimate that came in
        at nine pence is 900% over and worth nobody's morning; a five-percent
        overrun on a four-thousand-pound batch is worth somebody's — and a
        channel that fires on the first is muted before the second happens, so
        an alert on the relative threshold alone is a fault in its own right.
        The reasons are a stable closed set so variance can be grouped, and
        `unexplained` on a variance past both thresholds is reported, because
        that is the one worth investigating and it is the one the record gives
        an investigator nothing to group by. A zero estimate yields 0 or
        Infinity rather than a NaN somebody charts. -->
      <!-- S5.7 rollup: 3471 kit tests pass (57 new); `tsc` clean on lib and
      spec; export map at 73 entry points, `--check` clean; all seven gates
      pass. Only one barrel collision this time
      (`ReconciliationFinding` with `staleness.ts`), caught by grepping the
      planned export names BEFORE writing the index block. -->
- [x] S5.8 Extract the generic dependency/claim graph without hard-coding Tara's
      claim kinds. Evidence: Metis evidence, Veritas sources, Yemaya canon, Isis
      provenance, and Euterpe rights fixtures compile.
      <!-- `src/dependency-graph.ts`, and the module is not called
      `claim-graph.ts` for the reason the item exists. The failure this prevents
      is a shared library extracted from one domain that still speaks its
      language: every other domain then either translates itself into the first
      domain's words or forks, and both are worse than not sharing. The tell is
      always in the NAMES. The kit owns the SHAPE and the plugins own the TYPES
      — a node kind is a string a plugin registers, and what is fixed here is
      structural: which endpoint depends on which, whether a kind is versioned,
      whether an edge may leave its scope, whether cycles over it are a fault,
      and what a bounded traversal means. Five fault families, 33 faults, all
      producible. -->
  - [x] S5.8.a Define plugin-owned node/edge type registry atop shared stable
        identity, version, direction, source, classification, and lifecycle.
        Evidence: graph extension contract.
        <!-- DIRECTION is a property of the edge KIND rather than of the
        traversal: if each caller decides which way is "upstream", the word has
        as many meanings as there are call sites, and an impact query in one
        screen answers a different question from the identically named one in
        the next. `edge-into-a-versioned-kind-that-does-not-pin` is the fault
        that produces wrong ANSWERS rather than errors — an edge to "the current
        version" is still an edge after the target changes, nothing fails,
        nothing is emitted, and every conclusion drawn through it now rests on
        something the graph never said. `neither` is a real value for a
        symmetric relation, and `symmetric-edge-declared-acyclic` exists because
        such an edge is a cycle by construction, so the acyclicity check would
        fire on correct data — the S4.15 failure, refused in advance. Every kind
        must name an OWNER, since an unowned kind is one the kit is assumed to
        define, which is exactly how a domain's vocabulary reaches a shared
        surface. -->
  - [x] S5.8.b Define generic add/remove/version/supersede/resolve and bounded
        upstream/downstream/path/impact query interfaces. Evidence: operation
        tests.
        <!-- The traversal is BREADTH-first, and that is not a preference: a
        depth-limited depth-first walk returns one deep path and none of the
        near neighbours, which is the opposite of what anybody asking "what does
        this affect" wants back. Truncation is part of the ANSWER and is
        reported by which bound was hit, because a bounded query that does not
        say it stopped early returns a small result that reads as a complete one
        and the reader has no way to tell. A `neither` edge is traversed in
        neither direction, so an impact query cannot report an impact through a
        relation that asserts no dependency. Paths are bounded on count as well
        as depth, since the number of paths between two nodes in a dense graph
        is exponential and the query that discovers this is the one running in a
        request. -->
  - [x] S5.8.c Enforce node/edge schema, same-scope rules, duplicate/cycle/
        dangling policy, and immutable historical version references. Evidence:
        graph integrity tests.
        <!-- Cycles are checked PER EDGE KIND, and that distinction is the
        difference between a useful finding and a permanent one: real graphs
        contain cycles across kinds — A annotates B while B supersedes A — and
        reporting those would report every real graph. What is a fault is a
        cycle within a kind that declared itself acyclic, because that kind's
        own traversals will not terminate and the BOUND is what will stop them
        rather than the data. The detector is iterative with an explicit stack,
        since a graph deep enough to matter is deep enough to exhaust a
        recursive walk. `pinned-version-that-is-not-present` is the case pinning
        exists for — a reference to something that has since been rewritten. And
        a duplicate edge is a fault because every count through it doubles,
        which puts every de-duplicating reader in disagreement with every
        counting one. -->
  - [x] S5.8.d Apply tenant/object/property/protected-identity authorization to
        nodes, edges, counts, paths, and graph errors. Evidence: disclosure
        negative tests.
        <!-- An edge survives the projection only when BOTH endpoints do: an
        edge with one visible end is not a partially visible relation, it is the
        statement "this thing you can see is connected to something you cannot",
        which is precisely the fact being withheld.
        `count-computed-before-projection` is the leak nobody notices, because
        the node LIST is correct — fourteen reported, three returned, and the
        difference is eleven facts about a graph this reader has no access to.
        `path-returned-with-its-middle-elided` is the same idea one level up:
        the elision asserts the connection. And S5.8.d is the one place in the
        kit where NOT-FOUND and FORBIDDEN are deliberately the SAME answer —
        everywhere else they are kept apart, and here they cannot be, because
        the only thing separating them is the existence of the node and
        existence is what is being denied. -->
  - [x] S5.8.e Build Metis evidence, Veritas source/claim, Yemaya canon, Isis
        artifact/provenance, and Euterpe rights fixtures using only plugin
        types. Evidence: five compiling fixtures.
        <!-- Five registries in `dependency-graph.spec.ts`, each written as its
        own domain would write it: Metis exhibits and findings, Veritas
        assertions and attributions, Yemaya works and editions, Isis renders and
        input assets, Euterpe recordings and licences. No two share a node kind
        or an edge kind, and the test proves it pairwise. The claim is scoped to
        KINDS and deliberately NOT to lifecycle states: the first version of
        that test failed for the right reason, because Metis withdraws an
        exhibit and Euterpe withdraws a recording — English having a word for it
        is not two domains sharing a model, the kit never reads a state name,
        and a test demanding five disjoint English vocabularies would be
        enforcing a thesaurus. Yemaya's `sits-beside` is carried through as a
        symmetric edge without a direction being invented for it. -->
  - [x] S5.8.f Verify shared source/export names contain no Tara-specific claim,
        meditation, stage, authority, or policy semantics. Evidence: API review.
        <!-- A CHECK rather than a reading. `domainVocabularyFindings` splits an
        identifier the way a reviewer reads it (`camelCase`, `PascalCase`,
        `SCREAMING_SNAKE`) and matches WHOLE WORDS, so `reclaimed` is not a
        claim and `stagedFile` is a stage — the first is the false positive that
        gets a check deleted and the second is the real one. The vocabulary is a
        PARAMETER and not shipped, because a kit holding a list of one domain's
        words is a kit that knows them; the spec derives the corpus from the
        five fixtures' own registries plus the words this item names by hand.
        And the check runs against `Object.keys(import * as dependencyGraph)` —
        the module's real runtime exports rather than a list somebody
        maintains. -->
      <!-- S5.8 rollup: 3509 kit tests pass (38 new); `tsc` clean on lib and
      spec; export map at 74 entry points, `--check` clean; all seven gates
      pass. Pre-checking the planned export names caught nine collisions before
      they were written; `DependencyNode` slipped through against `diff.ts` and
      became `GraphEntity`. -->
- [x] S5.9 Implement authority/severity policies as domain plugins. Evidence:
      one domain cannot silently use another domain's floors.
      <!-- `src/policy-plugins.ts`. The failure never arrives as a decision; it
      arrives as a FALLBACK. Somebody's lookup returns undefined, the crash is
      fixed with `?? defaultPolicy`, and from then on a domain with a
      two-independent-source rule is judged by whichever policy happened to be
      registered first — producing verdicts that are the right shape, pass every
      schema, and answer a question nobody asked. Five fault families, 36 faults
      and refusal reasons, all producible. -->
  - [x] S5.9.a Define plugin identity/version/domain, applicable node/edge/gate/
        action types, inputs, outputs, and ownership. Evidence: policy plugin
        contract.
        <!-- `applies-to-nothing` is the one that matters: a policy declaring no
        scope can be selected for anything, which defeats the applicability rule
        by OMISSION rather than by a mistake anybody would review. The version
        must be pinned because an evaluation records which policy judged it, and
        an unpinned record names a moving target that no past verdict can be
        reproduced against. `domain-prefixed-capability` is small and saves a
        class of confusion — `veritas.sourcing` inside the `veritas` domain
        produces the key `veritas/veritas.sourcing`, and the day somebody
        normalises those keys two domains collide. -->
  - [x] S5.9.b Define authority ranking/independence, source/evidence
        sufficiency, conflict, protected/anonymous source, and unavailable
        semantics per domain. Evidence: authority policy fixtures.
        <!-- Independence is an AFFILIATION PARTITION rather than a pairwise
        relation: counting a maximum independent set over an arbitrary relation
        is intractable, and the domain question is not arbitrary — two sources
        from one press office, one lab or one rights holder are one source, and
        a rule that counts documents is satisfied by one office sending two
        letters. UNAVAILABLE is not the lowest rank: ranking it bottom turns "we
        could not check" into "we checked and it was weak" and lets the
        computation proceed. It produces UNDETERMINED — but only when it could
        have changed the answer, because sufficient without it stays sufficient
        and insufficient counting every unreachable source optimistically stays
        insufficient, which is what makes a third value survivable in a system
        that has outages. A protected source counts and is never named, and
        neither is the COUNT of them, since "met, on three sources, one
        withheld" has disclosed that a protected source exists.
        `conflict-rule-with-no-tie-rule` is the branch nobody writes:
        `higher-rank-wins` cannot resolve two sources at the SAME rank saying
        opposite things, which is the situation a conflict rule exists for. -->
  - [x] S5.9.c Define severity classes, thresholds, escalation, blocker/warning,
        target/audience, and remediation semantics per domain. Evidence:
        severity policy fixtures.
        <!-- `blocking-class-below-a-non-blocking-one` is the shape that reads
        as fine and is not: if a middling finding blocks and a worse one only
        warns, then the way to ship is to make the problem worse, so blocking
        has to be monotone in severity or the policy rewards the wrong move. A
        blocking class must name what CLEARS it, or the only way forward
        anybody finds is the override. Escalation is walked rather than assumed,
        so a downward escalation (an unresolved finding getting quieter over
        time) and a cycle are both reported. And `classifyMeasurement` returns
        the HIGHEST class cleared rather than the first matched — a scan that
        stops at the first match reports a critical finding as a warning
        whenever the classes are read in the order they are declared. -->
  - [x] S5.9.d Register exact policy by domain/capability/version and reject
        missing, duplicate, wrong-domain, or unsupported policy. Evidence:
        registry negative tests.
        <!-- No default and no nearest match. Every refusal here is a case where
        some implementation somewhere would have returned SOMETHING — the policy
        from the neighbouring domain, the previous version, the first of two
        registrations — and every one of those produces a verdict of the right
        shape about a question that was not asked. The wrong-domain refusal is
        detected rather than merely missed: when the capability exists but under
        another domain, the refusal SAYS so, because "not found" would send the
        next person to register a duplicate. -->
  - [x] S5.9.e Persist selected policy/version with each evaluation and prevent
        caller-supplied weakening below code/domain floors. Evidence: downgrade
        tests.
        <!-- A caller may NARROW and may never widen. The direction of
        strictness is read from the setting rather than assumed, because a
        maximum-staleness threshold widens by going UP while a required-source
        count widens by going down — a checker that assumed one direction would
        wave the other through. Both floors are checked because they fail
        differently: the code floor is what this system will not do at all, and
        the domain floor is what this domain decided for itself. A domain floor
        found BELOW the code floor is a configuration that was accepted once and
        has been quietly ineffective since, while whoever set it believes it is
        in force. Refused rather than logged, because a logged weakening is a
        weakening. -->
  - [x] S5.9.f Run cross-domain fixtures that deliberately attempt policy
        substitution and verify refusal. Evidence: isolation tests.
        <!-- Two registered domains that genuinely DISAGREE about the same
        evidence — Veritas needs two independent affiliations before an
        assertion is published, Metis needs one because an exhibit is examined
        rather than corroborated — and the spec asserts the disagreement first,
        because without it "substitution is refused" is a rule with nothing at
        stake. Four routes are then attempted and all four refused: asking as
        one domain for a subject only the other declares, asking as one domain
        at the other's version, asking under a domain nobody registered, and
        asking for the right domain with the wrong policy kind. A fifth case
        asserts Metis can still reach its OWN policy, so the refusals are a
        boundary rather than a wall around everything. -->
      <!-- S5.9 rollup: 3547 kit tests pass (38 new); `tsc` clean on lib and
      spec; export map at 75 entry points, `--check` clean; all seven gates
      pass. Pre-checking the export names against `src/*.ts` before writing the
      barrel found the one collision (`classifySeverity`) in advance; it became
      `classifyMeasurement`. -->
- [x] S5.10 Implement recomputation and blast-radius planning. Evidence: preview
      is read-only; apply creates jobs and audit.
      <!-- `src/recomputation.ts`, composing S5.8's bounded traversal and S5.7's
      money. The failure this prevents is a preview that is not one: a
      blast-radius view is the thing a user opens most often — on every edit, on
      every hover, from a table of two hundred rows — and every write it
      performs happens at that rate. Six fault families, 34 faults and refusal
      reasons, all producible. -->
  - [x] S5.10.a Accept exact changed node/version/event and policy context and
        resolve affected direct/transitive results, gates, artifacts, reviews,
        releases, and projections. Evidence: impact query tests.
        <!-- The query names a VERSION of the node and a VERSION of the policy,
        because a radius computed from "the node" describes whatever the node is
        when somebody reads the answer, and an unpinned policy lets the plan and
        the run be judged differently. Six affected KINDS, structural rather
        than any domain's nouns. `direct-impact-at-distance-zero` catches the
        subject listed as its own consequence — or, more often, a distance
        nobody computed. -->
  - [x] S5.10.b Bound traversal by tenant, authorization, edge type, depth,
        fan-out, cycles, and pagination while representing inaccessible/unknown
        impact. Evidence: traversal safety tests.
        <!-- The bounds and the cycle handling come from S5.8's
        `traverseDependencies` rather than a second traversal. What is added
        here is REPRESENTATION: inaccessible impact is one disclosed bit rather
        than an omission, and the argument for disclosing it is the alternative
        — a user applying a change believing it touches three things when it
        touches thirty has been misled by a system that was being careful. A
        truncated radius presented as the radius is the same failure as S5.8's,
        one level up, and worse here because the number is about to decide
        whether to proceed. -->
  - [x] S5.10.c Compute deduplicated dependency order, required versus optional
        work, stale-only versus recompute, provider/resource needs, cost/time,
        blockers, and releases at risk. Evidence: plan fixtures.
        <!-- FULL RECOMPUTE IS NOT THE SAFE DEFAULT. It costs more and it
        discards human work: recomputing a gate somebody signed off throws away
        the signature, and re-obtaining it is asking a person to agree again to
        something they have already agreed to, which is how a sign-off becomes a
        formality. So stale-only is the default and a full recompute that
        discards a decision has to be ASKED FOR.
        `required-step-depending-on-an-optional-one` is the ordering mistake
        that survives review: an optional step may fail without failing the run,
        so the required step behind it either waits for something that never
        arrives or runs on an input that was never produced. A step listed twice
        for one item is two jobs racing each other for one output row, and a
        total below the sum of the steps matters because the headline is what
        gets approved. -->
  - [x] S5.10.d Return an immutable versioned preview with no persistence,
        provider invocation, queue write, notification, or audit-as-mutation.
        Evidence: read-only side-effect tests.
        <!-- Eight forbidden effects enumerated, including the two the ledger
        does not name — a cache entry and a lock — because those are the writes
        that get called not-writes. The AUDIT write is reported as its own fault
        kind rather than folded in with the rest, and that separation is the
        point: the others reach production as bugs, and the audit row reaches it
        as a decision somebody defended in review. A preview that records that
        it happened has happened, and it happens at the rate a user hovers over
        a row. The returned plan must also be immutable, or the caller can edit
        it between preview and apply and the digest apply revalidates describes
        a plan nobody produced. -->
  - [x] S5.10.e Revalidate source/dependency/policy versions, authorization,
        budget/capacity, and plan hash when applying. Evidence: stale-plan
        tests.
        <!-- The ORDER of the refusals is the order of certainty and it is
        checked: the digest first, because every later check would be answered
        about the wrong plan; authorization next, so a caller who may not do
        this does not learn the plan's contents from the refusal; then
        freshness, then budget and capacity, then the radius approval — last,
        because it is the only one a person can resolve in the moment. A plan
        older than its freshness bound is refused EVEN WHEN THE DIGEST MATCHES,
        since a matching digest only says nothing changed that the digest
        covers. And refusing on budget is refusing to leave a graph half
        recomputed, which is the state with no correct reading at all. -->
  - [x] S5.10.f Create idempotent parent/child durable jobs in dependency order
        and a correlated audit/notification record. Evidence: apply integration
        tests.
        <!-- The idempotency key is derived from the plan digest and the step,
        so a re-apply of one plan is one job. `job-order-not-matching-the-plan`
        compares the job's wait-list against the plan's dependency edges rather
        than trusting that they agree. And `apply-with-no-audit-record` is the
        deliberate MIRROR of S5.10.d: the preview must not audit and the apply
        must, and a system with those two backwards has a trail of everything
        nobody did and none of what they did. -->
  - [x] S5.10.g Expose partial, blocked, failed, cancelled, superseded, and
        completed recomputation and updated release eligibility. Evidence:
        lifecycle tests.
        <!-- The two central faults are one distinction from opposite ends:
        required work outstanding must not read as COMPLETED, and optional work
        failing must not read as FAILED. A system that collapses them has one of
        two behaviours — it blocks releases on work nobody needed, or it
        releases on work that did not happen — and which one it has is an
        accident of which branch was written first. A superseded run must name
        its successor or the work cannot be followed, and a release whose
        eligibility moved must say why, because whoever is waiting on it has
        nothing else to act on. -->
      <!-- S5.10 rollup: 3585 kit tests pass (38 new); `tsc` clean on lib and
      spec; export map at 76 entry points, `--check` clean; all seven gates
      pass. Four barrel collisions (`OUTCOME_FAULTS`, `outcomeFaults`,
      `OutcomeFinding`, `PlanFault`) renamed into the `recomputation*` family. -->
- [x] S5.11 Add policy version and evaluation timestamp to every trust result.
      Evidence: policy changes invalidate dependent results.
      <!-- `src/trust-results.ts`. The failure this prevents is a system that
      gets MORE WRONG as its policy gets better: an unstamped result is read a
      year later as though it had been evaluated under today's rules, so every
      improvement silently reinterprets a body of old results as agreeing with
      it — and the better the new policy is, the more confidently wrong that
      reinterpretation becomes. Nothing fails; the record simply means something
      it never meant. Five fault families, 31 faults, all producible. -->
  - [x] S5.11.a Inventory all gate, safety, authority, rights, consent, canon,
        provenance, quality, fairness, and release trust result types. Evidence:
        result coverage register.
        <!-- All ten families, and the register is checked in BOTH directions: a
        family nothing produces a result for answers the coverage question with
        "none" rather than "checked", and a declared type that carries no stamp
        is named. `persisted-type-whose-row-omits-the-stamp` is the one that
        produces the problem years later — the in-memory result carries its
        policy version, the write drops it because the column was added
        afterwards, and every reader of the table sees results that appear to
        have been evaluated under the current policy. The schema is right and
        the data is not. A type that is never persisted is deliberately not
        reported for it. -->
  - [x] S5.11.b Require stable policy ID and immutable semantic version/hash in
        each result schema and persistence row. Evidence: schema tests.
        <!-- The HASH is required beside the version and is not redundant: a
        version is what somebody remembered to bump, and two deployments can
        hold different bytes under one number for as long as it takes anybody to
        notice — the version is for a human deciding whether the rule changed
        and the hash is for a machine deciding whether the bytes did. The
        version must be SEMANTIC, or a reader cannot tell a clarification from a
        change of rule and every policy edit invalidates everything or nothing.
        The EVALUATOR version is stamped too, because a bug fixed in the
        evaluator changes results the policy did not, and without it those
        results are indistinguishable from the corrected ones. -->
  - [x] S5.11.c Record evaluation start/completion/reference clock, expiry, and
        source/evidence observation time with explicit semantics. Evidence:
        temporal tests.
        <!-- FOUR times, not one. A result computed at 10:00, about evidence
        seen at 09:00, evaluated as of 08:00 has three different answers to
        "when", and a system with one field has picked one of them for
        everybody. The REFERENCE clock is load-bearing: a rule like "within
        thirty days" needs an anchor, and an anchor of "now" makes the same
        evidence pass today and fail on Thursday — so
        `reference-clock-equal-to-completion` is reported, and reported ONLY
        when the result declares a freshness window, since a result with no
        window is not made wrong by anchoring on itself. EXPIRED IS NOT FAILED:
        an expired result says the answer is too old to use and nothing about
        the subject, and collapsing the two turns a stale freshness window into
        an accusation. -->
  - [x] S5.11.d Include evaluator/corpus/model/threshold/input/dependency
        versions needed to reproduce or explain the result. Evidence: provenance
        completeness tests.
        <!-- The check takes what was USED as a separate argument rather than
        reading it back out of the record, and that is the whole design: a
        completeness check that reads only the record can report internal
        consistency and nothing else, so a corpus that was consulted and never
        recorded is invisible to it. The reverse is checked too — a threshold in
        the provenance that no code applies is not pedantry, it is a number
        somebody will tune expecting an effect. And a dependency read without a
        version is what breaks S5.11.e: a change to it cannot mark this result
        stale. -->
  - [x] S5.11.e Emit policy-change events and mark all affected current results
        stale before downstream authorization. Evidence: invalidation tests.
        <!-- The ORDER is the guarantee. Marking results stale is easy; marking
        them before anything reads them is the problem — a gate that read a
        result in the window between the policy changing and the sweep finishing
        passed on an answer already known to be wrong, and it will not be asked
        again. `rule-change-recorded-as-a-clarification` is how the sweep gets
        skipped legitimately: a clarification invalidates nothing, so labelling
        a rule change as one is the cheapest possible way to avoid re-evaluating
        a large population, and the label is chosen by whoever is shipping the
        change. The over-wide sweep is a fault too, since re-evaluating work
        that was correct costs money and re-opens decisions somebody had
        closed. -->
  - [x] S5.11.f Reject or migrate legacy results missing policy/time rather than
        assuming current policy. Evidence: legacy compatibility tests.
        <!-- Three answers and never a fourth. `migrate` requires an EXTERNAL
        source that establishes which policy version was live during the
        evaluation window — a deployment log, a release record — because a
        migration that infers the version from the current one is exactly the
        assumption this section exists to refuse, performed once and then
        invisible. Everything else is `reject`, and rejecting is not the
        destructive option: a rejected legacy result is re-evaluated and becomes
        correct, while an accepted one is wrong and looks identical to a right
        one for ever. A stamp that is PRESENT and incomplete is rejected rather
        than accepted. -->
      <!-- S5.11 rollup: 3620 kit tests pass (35 new); `tsc` clean on lib and
      spec; export map at 77 entry points, `--check` clean; all seven gates
      pass. No barrel collisions — the pre-write name grep is now part of the
      recipe. -->
- [x] S5.12 Add safe human override hooks. Evidence: authorization, rationale,
      scope, expiry, notification, and audit are enforced.
      <!-- `src/overrides.ts`. The failure this prevents is a control plane that
      is never removed and stops applying anyway: overridden often enough, the
      override IS the process and the check is a formality somebody clicks past.
      The load-bearing decision is that an override DOES NOT CHANGE THE ANSWER —
      there is no requested outcome meaning "make it pass", because a system that
      rewrites the result destroys the only record the check ever fired, and six
      months later the release reads as one where everything passed. Eight fault
      families, 71 faults, all producible; 96 tests. -->
  - [x] S5.12.a Register overrideable and nonoverrideable result/gate/policy
        types and maximum scope. Evidence: override registry.
        <!-- Two decisions the registry exists for. Overrideability is NOT
        decided by the team that owns the check: `governedBy` is separate from
        `producedBy`, or the check is advisory to its own owner and mandatory to
        everybody else, and nobody outside that team can tell which of the two
        they are looking at. And the lookup DEFAULT-DENIES — an unregistered
        subject is refused, since the other default ships every result type ever
        added as overrideable with nobody ever deciding. A non-overrideable entry
        must name an obligation OUTSIDE this system (a statute, a licence, a
        contract) so reversing it is an argument rather than a one-line diff, and
        one that still carries an approver count and an assurance level is
        reported: the machinery is built and a single boolean in the same file
        stands between it and being used. `FAMILY_MAX_BREADTH` is a ceiling
        rather than a default, and safety/rights/consent/authority/provenance sit
        at `single-object` because breadth changes the KIND of those decisions —
        a platform-wide safety override is not an exception, it is the check
        switched off by somebody who did not have to say so. -->
  - [x] S5.12.b Require exact result/input/policy versions, requested outcome,
        structured rationale, evidence, residual risk, and intended consequence.
        Evidence: request validation tests.
        <!-- The rationale is STRUCTURED because a free-text reason box collects
        "approved, see thread" and makes three different decisions
        indistinguishable. `the-check-is-wrong` is a bug report and must carry a
        defect, or the same override is filed again every week for ever and the
        check stays wrong because nothing ever recorded that it was;
        `the-risk-is-real-and-accepted` is the only claim where a residual-risk
        statement means anything; `the-check-could-not-decide` is an absence of
        information somebody could go and get, so it has the shortest window in
        the system (72h). The binding is handed to S5.11's own `stampFaults`
        rather than restated — an override is an exception to one evaluation and
        the definition of "which evaluation" belongs to the module that stamps
        them. `argument-that-cites-authority-instead-of-a-reason` is deliberately
        narrow and has a counterexample test: a rationale that names the VP AND
        makes the argument passes. -->
  - [x] S5.12.c Enforce actor/role/tenant/object/target/stage scope, step-up,
        separation of duties, quorum, and delegated authority. Evidence:
        authorization matrix.
        <!-- A REFUSAL IS NOT A VOTE. Systems model approvals as a count, so a
        refusal is a missing approval, so the remedy for a refusal is to ask
        somebody else — and a threshold of two is met by the third and fourth
        people asked while the first one's objection sits unanswered.
        `overrideAuthorization` refuses whatever the count says. Separation of
        duties is about the BENEFICIARY and not only the requester, who is very
        often a release engineer filing on another team's behalf; the reporting
        line is WALKED, with a cycle guard and a test that proves termination.
        `one-actor-counted-twice` is how a quorum of three is met by two people,
        one of whom holds two roles — ordinary in a small team and never
        re-counted. Delegated authority is bounded three ways: it may not compound
        (`onward-delegation`), may not hand over more than the delegator held, and
        past 720h is a role change that never got a role change's review. -->
  - [x] S5.12.d Define start, expiry/one-use, review, renewal, revocation, and
        invalidation on dependency/version change. Evidence: clock and binding
        tests.
        <!-- The override TOLERATES NOTHING, and that is the deliberate contrast
        with S2.11's waiver: a waiver may declare which changes it survives
        because it waives a gate's verdict on a class of failure, while an
        override disputes ONE computed answer — if the policy, its bytes, the
        evaluator, the input or a dependency moved, there is a different answer
        nobody has read, and an exception that survives into it approves a
        conclusion no human has seen. `amended` is a STANDING rather than a
        validation violation and is ordered ahead of the clock: a grant whose
        request has been edited since approval is not a live exception with a
        paperwork problem. `renewal-on-the-same-evidence` and
        `renewed-before-the-review-it-was-granted-with` are the two ways a
        seventy-two-hour exception becomes a standing arrangement no single
        approval ever authorised. `overrideGrantFaults` closes the other
        direction from the digest: a grant that was never an answer to its
        request, including one that quietly lowered the severity — which
        understates what was accepted AND, because S5.12.e routes on that exact
        field, decides that fewer people hear about it. -->
  - [x] S5.12.e Notify affected owners/reviewers/publishers/security/safety/
        rights roles according to severity without leaking protected detail.
        Evidence: routing tests.
        <!-- ONE severity scale doing double duty as the residual risk and the
        routing key, because two scales means somebody writes a mapping and the
        mapping is where a critical override becomes a routine notification. The
        notice is part of the CONTROL: at `serious` and above it must land before
        the override takes effect, or the people who would have objected learned
        about it from the thing that had already happened and the decision was
        unilateral. The two leak faults point in OPPOSITE directions on purpose —
        sending the finding to the publisher broadcasts the thing the result was
        protecting, and withholding it from the safety desk asks them to review a
        decision they may not see, which is a review in name only. `protectedTerms`
        are supplied by the caller because a redaction rule that guesses redacts
        the wrong things and then gets switched off. The routing table is checked
        for MONOTONICITY, since a table that drops a recipient as things get worse
        makes escalating an override the way to stop that role hearing about it;
        the shipped table produces zero findings, and over-notification has a
        legitimate case (`additionalRecipients`) with its own test. -->
  - [x] S5.12.f Record request, approvals/refusals, activation/use, effect,
        expiry/revocation, and downstream actions immutably. Evidence: audit
        tests.
        <!-- `refusal-absent-from-the-trail` is the fault worth the function: a
        trail that records the approvals and drops the refusals turns a contested
        decision into a unanimous one, and the reader most likely to be misled is
        the incident review asking whether anybody saw this coming — the answer
        was yes and the record says no. `effect-applied-with-no-approval-before-it`
        is the ordering that makes the rest true, because an effect applied first
        and approved afterwards produces a record in which every field lines up
        perfectly. Immutability is a digest CHAIN plus sequence continuity, so an
        entry edited or inserted afterwards cannot leave the trail reading as it
        did. And `downstream-action-that-names-no-override` is the question an
        audit is actually asked — not "was this approved" but "what shipped under
        it", which an action that recorded no override id makes unanswerable. -->
  - [x] S5.12.g Surface active override and residual risk wherever the result
        authorizes review, publish, release, export, or operation. Evidence:
        end-to-end visibility tests.
        <!-- `result-rendered-as-passing-under-an-override` is decision 1 arriving
        at the screen: the record can be immaculate — result unchanged, override
        adjacent, trail intact — and a surface that draws the pair as a green tick
        has undone all of it for every person who will ever look, because nobody
        reads the record and everybody reads the screen.
        `artifact-that-left-without-the-override` is the one that outlives the
        system: a risk accepted inside the workbench travels with the decision,
        while an export or a release hands somebody a FILE, and a manifest that
        does not carry the exception gives the recipient a work product asserting
        more than anybody agreed to with no way to find out. The four disclosure
        fields are four different objections — no scope and you cannot tell if it
        covers you, no approver and there is nobody to ask, no expiry and you
        cannot tell a decision from a habit, no residual risk and it reads as
        fine — and a rendered expiry that is not the real one is worse than none,
        because it is believed. -->
      <!-- S5.12 rollup: 3717 kit tests pass (96 new); `tsc` clean on lib and
      spec; export map at 78 entry points, `--check` clean; all six kit gates
      pass. No barrel collisions — the pre-write name grep caught none of the 93
      new exports. -->


### S6 — Bundle assembly, provenance, publish, and verification

- [x] S6.1 Define a schema-parameterized bundle builder with no arbitrary file
      writes. Evidence: output layout is constrained by a domain profile.
      <!-- `src/bundles.ts` plus `tools/bundle-confinement.mjs` (new Nx target
      `bundle-confinement`). The failure this prevents is a builder that takes an
      output directory and a callback — a general-purpose file writer with a
      manifest attached. Every domain that needs one more file adds one more
      callback, and within a year the "profile" describes none of what actually
      ships; the part that makes it expensive is that it STILL VALIDATES, because
      validation was written against the profile too. Six fault families, 61
      faults, all producible; 43 tests. -->
  - [x] S6.1.a Define domain profile ID/version, bundle manifest schema, allowed
        entry kinds, required/optional paths, multiplicity, media/schema
        constraints, and target compatibility. Evidence: profile contract.
        <!-- The fault worth the function is
        `limits-that-cannot-hold-the-required-slots`: a profile whose entry cap is
        below the sum of its own required minimums (plus the manifest, which is an
        entry too) describes a bundle that can NEVER validate, and nobody finds out
        until a real assembly fails at the end, having done all the work, with an
        error about a limit rather than about the profile. The quiet one is
        `multi-entry-slot-whose-template-has-no-ordinal` — every entry after the
        first resolves to the path the last one took, the assembly succeeds, the
        manifest lists one file, and the bundle is short the rest with nothing
        saying so. No slot may reach the manifest, by path or by kind, because a
        slot that produces the manifest lets the caller write the description of
        what the caller produced. Version ranges are compared NUMERICALLY, or
        1.10.0 sorts inside a range ending at 1.9.9. -->
  - [x] S6.1.b Accept immutable artifact/reference inputs and logical profile
        slots rather than caller-selected filesystem destinations. Evidence:
        builder input schema.
        <!-- `ArtifactInput` has no destination field and that absence IS the
        contract — the only strings a caller controls are identifiers, so
        `slot-id-that-is-really-a-path` defends the one place the decision can be
        attacked: a caller who wants a destination puts a path into the identifier
        the builder carries through, it works, and nothing else looks. An external
        REFERENCE contributes a manifest row and no bytes, so
        `external-reference-with-a-byte-length` matters in both directions: it
        makes a bundle fail a limit over data it does not contain, and it makes a
        bundle that points at a hundred gigabytes look like one that holds them.
        A reference in a byte-bearing slot is a bundle claiming to contain what it
        points at, and there is a counterexample test where a reference in an
        index slot is accepted. -->
  - [x] S6.1.c Resolve every logical slot to a normalized profile-owned relative
        path and reject unmapped or duplicate slots. Evidence: layout tests.
        <!-- `resolveLayout` returns entries OR faults and never both, because a
        partially resolved layout is the thing a caller writes anyway; a test
        asserts the refusal carries no `entries` key at all. The manifest is not
        among the entries — it is written from them, so a layout containing it
        would be describing its own description. `input-with-no-slot` is reported
        here rather than left to assembly: by the time validation runs, the
        difference between "not written" and "written and not listed" has stopped
        being visible, and an artifact that resolves to no path is a file somebody
        believes is in the bundle. -->
  - [x] S6.1.d Write only through an isolated temporary assembly root using
        create-new semantics and bounded resources. Evidence: filesystem
        confinement tests.
        <!-- NOT PROVABLE IN MEMORY, so `tools/bundle-confinement.mjs` builds a
        real 0700 root, performs real `open(2)` writes, resolves every destination
        with `realpathSync`, and runs the SHIPPED checker over what the kernel
        actually did — with four controls that must each be caught. CONTROL 1 is a
        sibling directory sharing the root's prefix (`<root>-evil`), which
        `startsWith` accepts and the separator-aware `isInsideRoot` refuses; the
        tool ASSERTS the naive check accepts it, so the control cannot go vacuous.
        CONTROL 2 is a symlink out of the root: the bytes land outside and a
        checker comparing the path it was ASKED to write sees a perfectly confined
        relative path. CONTROL 3 is a symlink WITHIN the root — containment still
        passes and the bytes are not where the manifest says, which is why
        `write-that-followed-a-symlink` is a separate fault from being outside.
        CONTROL 4 truncates over an existing file, and the run asserts the same
        write with `wx` raises EEXIST from the kernel. Verified adversarially: with
        `isInsideRoot` replaced by `startsWith` in the built output, the tool
        fails on CONTROL 1. -->
  - [x] S6.1.e Validate complete layout, manifest, refs, hashes, profile
        invariants, and unused/missing inputs before archive creation. Evidence:
        assembly validation tests.
        <!-- Every digest goes through S3.11's own `verifyDigest` with the source
        the assembly REPORTS, and the outcome is carried on the finding rather than
        flattened, because "the file is corrupt", "only a prefix was hashed" and
        "the digest came from the metadata" are three different conversations with
        three different people. `entry-on-disk-that-the-layout-does-not-name` is
        the fault that catches everything this module cannot see — a symlink, a
        leftover root, a temporary file the writer forgot, a traversal that landed
        inside the tree — and once the archive is made it is indistinguishable from
        content. And validation gates the ARCHIVE rather than the publish, because
        a half-assembled archive that failed validation is still a file, and a file
        that exists is a file somebody picks up. -->
  - [x] S6.1.f Prove domain profiles can differ without arbitrary callback/file
        write escape hatches. Evidence: two-profile compile/runtime fixtures.
        <!-- Two shipped profiles as DATA: `STORY_PACKAGE_PROFILE` (an optional
        multi-entry media slot, a signature, two targets) and
        `DATASET_RELEASE_PROFILE` (a required multi-entry dataset slot, a deeper
        tree, no media, two-of-eight attestations, one target whose version range
        excludes the other profile entirely). Both produce zero profile findings
        and one `resolveLayout` produces both layouts, asserted path by path. The
        escape-hatch claim is CHECKED rather than asserted: `profileEscapeHatches`
        walks a profile at runtime and reports any function, absolute path or
        traversal in it, takes `unknown` because the value that arrives from JSON,
        a plugin or a database has not been through the type system, and
        terminates on a cycle. Both shipped profiles return nothing. -->

- [x] S6.2 Reject absolute paths, traversal, ambiguous Unicode paths, symlinks,
      case collisions, duplicate refs, and reserved names. Evidence: adversarial
      path suite.
      <!-- `src/bundle-paths.ts` plus `tools/bundle-path-adversarial.mjs` (new Nx
      target). Every classic archive vulnerability is ONE shape: a check ran on a
      string and then something transformed the string. Percent-decoding, Unicode
      normalization, separator translation, extraction and the target's own
      folding are five transformations, so the pipeline is DATA, the same path is
      checked at each stage, and every finding carries the EARLIEST stage that saw
      it — a vector visible only after decoding is a demonstration that the check
      on the supplied string agrees with an attacker. Eight fault families, 46
      faults, all producible; a 27-vector shipped corpus; 44 tests. -->
  - [x] S6.2.a Reject POSIX, Windows drive/UNC/device, URL/file-scheme, rooted,
        and platform-dependent absolute paths. Evidence: absolute path corpus.
        <!-- `windows-drive-relative` (`C:entry.json`) is the one that gets
        through review: no separator after the colon, so it reads as a filename
        with an odd prefix and every "starts with a separator" check passes it —
        and on Windows it resolves against the current directory OF DRIVE C,
        which the process never chose. `windows-device-namespace` (`\\?\`,
        `\\.\`) is separate from UNC because the prefix exists to hand the rest
        of the string to the object manager UNPROCESSED: a path that would have
        been rejected after normalization is never normalized. And a file URL is
        separated from any other scheme, because the remedies differ — one is an
        absolute path wearing a URL and the other is content fetched at
        extraction time, pinned to nothing. -->
  - [x] S6.2.b Reject dot/dot-dot traversal before and after percent/Unicode/
        separator normalization and archive extraction. Evidence: traversal
        corpus.
        <!-- `traversal-created-by-a-single-pass-filter` is the fault worth the
        function: `a/....//x` contains no `../` and ONE pass of removing `../`
        produces one, so a filter written as a single `replace` emits the exact
        string it was written to remove. `escapes-its-own-root` is kept separate
        from `dot-dot-segment` because `a/../b` resolves inside and `../b` does
        not, and telling somebody their path is an attack when it is merely
        untidy is how a warning gets switched off — `dot-segment` is reported in
        those words. The corpus carries a vector (`a/%2e%2e/%2e%2e/x`) whose
        traversal is INVISIBLE on the raw string and visible one stage later, and
        a test asserts it is invisible before decoding: that is the proof that a
        check on the supplied string agrees with an attacker. -->
  - [x] S6.2.c Normalize Unicode by one policy and reject confusable, invisible,
        bidi-control, invalid, or normalization-colliding paths. Evidence:
        Unicode path corpus.
        <!-- ONE normal form (NFC), and a path not already in it is REPORTED
        rather than silently converted, because silent conversion is how the
        manifest and the filesystem come to disagree about which file this is.
        `separator-introduced-by-a-wider-normalization` is the deep one and is
        not theoretical: U+FF0F is one component under NFC and two under NFKC, so
        the check compares the separator COUNT under the two forms rather than
        keeping a list of characters current. Bidi marks are held OUT of the
        invisible set on purpose — a directional override is not a character
        somebody cannot see, it is one that reorders the others, and reporting it
        as merely invisible puts the weaker sentence in front of the reader. The
        confusable check is per-COMPONENT, since Latin filenames under Cyrillic
        directories are ordinary and one name holding both an `a` and an `а` is
        not; the counterexample is a test. -->
  - [x] S6.2.d Reject symlink, hardlink, junction, reparse, device, FIFO/socket,
        and other nonregular file entries. Evidence: special-file corpus.
        <!-- `file-with-more-than-one-link` is the fault worth having, and it is
        PROVEN ON A REAL FILESYSTEM by `tools/bundle-path-adversarial.mjs`: a
        hardlink reports as `regular` from `lstat`, reads back byte-identical to
        a file outside the root, and would hash correctly. The link count is the
        only tell, and nothing looks at the link count unless it was written to.
        A hardlink means another name outside the bundle reaches the same inode,
        so writing through it writes somewhere the assembly never chose and
        deleting the assembly root does not remove the data. -->
  - [x] S6.2.e Detect case-fold, Unicode-fold, trailing dot/space, separator,
        and target-filesystem collisions. Evidence: cross-platform collision
        tests.
        <!-- A collision is a property of the TARGET, not of the path, so it is
        computed against a declared filesystem and three real ones ship
        (`POSIX_EXT4`, `WINDOWS_NTFS`, `APPLE_APFS`) that disagree about case,
        normal form, trailing dots and path length. The real-filesystem tool
        states the claim the only way it can be stated: `README.md`/`readme.md`
        and an NFC/NFD pair are FOUR REAL DISTINCT FILES on this box, the local
        target reports zero findings, and NTFS and APFS report a case fold and a
        Unicode fold — the bundle is fine on the machine that built it and loses
        a file on the machine that opens it, which is never the same machine. The
        fault kind names WHICH fold collapsed the pair, asked one transform at a
        time, because the remedies differ. -->
  - [x] S6.2.f Reject duplicate logical/artifact/manifest references and
        conflicting byte identities. Evidence: duplicate ref tests.
        <!-- Three faults, not one. `one-digest-at-two-paths` is deliberately NOT
        a fault — it is deduplication, and a rule refusing it is switched off by
        the first bundle that legitimately ships one image twice (there is a test
        asserting it produces nothing). `one-path-with-two-digests` is never
        legitimate: the archive holds one file and the manifest describes two, so
        a verifier passes on whichever row it reaches first. And
        `one-artifact-id-with-two-digests` is the conflicting byte identity that
        survives review, because each half is correct on its own and only the pair
        is wrong. A reference that points at the manifest is refused separately:
        the document describing the bundle would be an item inside its own
        description, and a verifier walking it does not terminate. -->
  - [x] S6.2.g Reject reserved device names, manifest/control paths, forbidden
        extensions, and overlong path/component/depth. Evidence: reserved/limit
        tests.
        <!-- The device-name check is on the STEM and case-insensitive, because
        `CON.txt` opens the console; a test walks CON/con.txt/NUL.json/lpt9.bin
        and asserts `console.json` is untouched. Control paths are an ARGUMENT
        rather than a constant, since what counts as one is a property of the
        profile — the manifest, a signature directory, whatever the importer
        reads as metadata — and an entry landing there is content that arrives as
        instructions. Forbidden extensions are the formats that execute or launch
        when opened, and a bundle is a thing people unpack without reading. -->
  - [x] S6.2.h Revalidate normalized paths at final open/write/extract time to
        prevent race or adapter reinterpretation. Evidence: TOCTOU tests.
        <!-- Everything before the open describes a STRING; only the open
        describes a FILE. The real-filesystem tool performs the swap: between the
        check and the open, `entries/` stops being a directory and becomes a
        symlink to an attacker-controlled directory, the string never changes, and
        the bytes land outside. It also recorded something worth keeping —
        `O_NOFOLLOW` guards the FINAL component only, so the O_NOFOLLOW open
        SUCCEEDED here; `usedNoFollow` in the contract therefore means "no link
        followed on ANY component", which needs per-component opens.
        `open-without-nofollow` is reported even when nothing changed, because a
        run in which the race did not fire is not a run in which it cannot, and
        a check that only reports observed divergence reports nothing on the first
        day somebody wins. The tool carries the counter-control too: an unraced
        open must report NOTHING. -->
      <!-- S6.2 rollup: 3806 kit tests pass (44 new); `tsc` clean on lib and
      spec; export map at 80 entry points, `--check` clean; all six kit gates plus
      both new real-filesystem targets pass. The corpus is 27 shipped vectors,
      each declaring the fault it must produce AND the stage at which it becomes
      visible, and both halves are asserted. -->

- [x] S6.3 Validate manifest schemas from their authoritative files. Evidence:
      tests fail when schema and builder drift.
      <!-- `schemas/bundle-manifest.schema.json` (a real JSON Schema 2020-12 file)
      + `src/manifest-schema.ts` + `tools/manifest-schema-drift.mjs` (new Nx
      target). The failure this prevents is the one every schema-shaped system
      arrives at: the document and the code that produces the document drift, and
      nothing notices because the tests were written against whichever of the two
      the author had open. The bundle STILL VALIDATES — the validator was compiled
      from the schema, the builder emits what the builder emits — so the only
      party who finds out is an importer on the other side of an organisation,
      months later, holding a file that says it conforms to something it does
      not. Five fault families, 38 faults, all producible; 29 tests. -->
  - [x] S6.3.a Register authoritative manifest schema path, ID/version, owner,
        code-generation command, and supported bundle profiles. Evidence: schema
        registry.
        <!-- `validator-version-that-is-a-range` is the fault worth the function:
        `^8.20.0` reads as a pin to everybody who skims it and means two CI runs,
        or CI and a laptop, compile the SAME schema with different code — and the
        day they disagree is the day one of them starts accepting something, and
        the run that accepted it is the one that shipped. `generationCommand` is
        not decoration either: a derived artifact whose regeneration command lives
        only in somebody's shell history is regenerated differently by the next
        person, and the difference is invisible in the diff because both outputs
        are generated. `profile-whose-manifest-no-schema-governs` is checked from
        the OTHER end, with the known profile ids as an argument, because a
        profile nothing validates is invisible from inside a register that
        contains only the schemas somebody remembered to add. -->
  - [x] S6.3.b Compile validators directly from authoritative schema with pinned
        tool/version and strict unknown/format/reference behavior. Evidence:
        validator generation.
        <!-- The spec COMPILES THE REAL FILE with the real pinned ajv and asserts
        the installed version IS the pinned one — not that the register says so.
        `schema-changed-since-the-validator-was-compiled` compares a recorded
        digest against a caller-supplied RECOMPUTATION, since comparing a stored
        digest with itself passes by construction. Three leniencies are named
        because each turns a rule into a comment: a non-strict validator IGNORES
        an unrecognised keyword, so a misspelled `requred` reads as present and
        constrains nothing; an unknown FORMAT treated as satisfied leaves a field
        unconstrained while looking constrained; and a remote `$ref` makes
        validation a network call, so the rules are whatever that host serves
        today and the document deciding whether a bundle is well-formed is not in
        the repository. A test greps the shipped file for a remote ref. -->
  - [x] S6.3.c Validate builder input, assembled manifest, serialized bytes,
        archived manifest, importer parse, and migrated output. Evidence:
        boundary coverage tests.
        <!-- VALIDATING THE OBJECT IS NOT VALIDATING THE BYTES, and
        `byte-boundary-validated-as-an-object` is the fault that says so.
        Serialization is where a Date becomes a string or an `[object Object]`, an
        `undefined` property disappears, a large integer loses precision and a
        `NaN` becomes `null`; a boundary that validated the in-memory value has
        checked the one representation that was never in doubt, and the remedy is
        one line nobody writes because the check they already have passes. The
        coverage row therefore records `parsedFromBytes`, and the spec actually
        crosses the boundary — stringify, parse, validate THAT — rather than
        asserting a flag. A boundary with no row at all is reported as
        unvalidated, because silence is not "no". -->
  - [x] S6.3.d Maintain canonical minimal/full valid and malformed/boundary/
        version-skew invalid fixtures. Evidence: schema fixture corpus.
        <!-- 16 fixtures, and 13 of them MUST FAIL: a set of valid documents
        proves a validator accepts and only a rejected one proves it still
        refuses, so a test asserts the failing half outnumbers the passing half.
        Every fixture records WHY, so one that starts passing names the rule that
        stopped applying instead of producing a diff that says a test changed.
        `version-skew` is its own class because it is the failure nobody writes a
        test for — the document is well-formed in every respect except that it
        says it conforms to a version this is not, and a validator ignoring that
        applies the wrong rules to a document that told it not to. Every verdict
        is asserted twice: once on the object and once after a real round trip
        through bytes. -->
  - [x] S6.3.e Compare generated types, builder fields/defaults, docs/examples,
        and validator output with schema. Evidence: cross-artifact drift check.
        <!-- `schemaFacts` DERIVES what it needs from the parsed document rather
        than restating it, because a TypeScript constant listing the required
        fields would be a third artifact and the third artifact is always the one
        that drifts — nothing validates against it. The sample is a manifest the
        BUILDER actually produced (S6.1's `resolveLayout` over the shipped
        profile), not a declaration of what it produces. Both directions are
        walked, since a field the schema requires that nothing emits and a field
        the builder emits that the schema never declared are different bugs with
        different owners. `schema-enum-value-nothing-can-produce` is the direction
        nobody writes: a kind left in the enum after the code emitting it was
        deleted is a value an importer is entitled to handle, so somebody writes a
        branch that is never taken and never removed. -->
  - [x] S6.3.f Deliberately mutate a required field/version/enum in schema and
        builder to prove CI catches both directions of drift. Evidence: mutation
        tests.
        <!-- `tools/manifest-schema-drift.mjs`. A drift check that has never seen
        drift is a function returning an empty array, so this runs SEVEN
        deliberate mutations — three on the schema, four on the builder — and each
        one names the mechanism that must catch it, because "something failed" is
        also what a broken suite says. Before any of them it runs the control that
        makes the rest meaningful: the unmutated pair must produce zero drift
        findings and the real manifest must validate. The interesting result is
        M3: opening `additionalProperties` makes the compiled validator ACCEPT
        what it used to refuse, so the cross-artifact check is the only thing left
        that can see it — which is the argument for having both. M6 and M7 are the
        mirror, caught by the validator and not by the drift check. Run:
        `pnpm nx manifest-schema-drift @oshun/workbench-kit`. -->
- [x] S6.4 Rehash every referenced artifact from bytes during assembly.
      Evidence: metadata-only checksum spoof is rejected.
      <!-- `src/artifact-rehash.ts` + `tools/artifact-rehash-race.mjs` (new Nx
      target). A manifest is a set of assertions about bytes, and the only
      question that matters about it is where each assertion came from. Copy a
      digest out of the store metadata and the manifest certifies the metadata;
      take it from the caller and it certifies the caller. Both produce a document
      indistinguishable from one that was earned — same fields, same shape, same
      green tick — believed by everything downstream for longer than anybody
      involved will be around to correct it. Six fault families, 43 faults, all
      producible; 38 tests. -->
  - [x] S6.4.a Resolve artifact refs through authorized immutable storage and
        reject mutable/unknown/nonfinal references. Evidence: resolver tests.
        <!-- A REFERENCE THAT CAN CHANGE IS NOT A REFERENCE: assembled twice, a
        tag or a "latest" produces two different bundles under one id, and the
        second is the one nobody diffed. `version-pinned-reference-with-no-version`
        is a mutable reference with a reassuring name.
        `reference-to-something-not-yet-final` looks like a corner case and makes
        the strangest bugs — an object whose upload is still open reads, returns
        fewer bytes than it will have, and hashes to something that will never be
        true again, so the bundle fails verification for reasons nobody can
        reproduce because by the time anybody looks the upload finished. And
        reading across a tenant boundary is checked here because an assembly is a
        very quiet way to copy bytes across one: the output is a file rather than
        a response. -->
  - [x] S6.4.b Stream exact stored bytes under size/time/resource limits and
        compute approved digest and length. Evidence: known-byte tests.
        <!-- A LIMIT ENFORCED AGAINST THE METADATA IS NOT A LIMIT. Reading
        `contentLength` and deciding the object is small enough bounds the number
        the object itself supplied — which is exactly the number under dispute
        whenever a size limit matters. The enforcement costs one counter in the
        read loop and nobody adds it, because the version they have never fails.
        `digest-over-fewer-bytes-than-were-read` is S3.11's partial read caught at
        the source: a hash finalised early gives a digest for a prefix and a
        length for the whole, and the pair reads as verified. An abort WITH a
        stated reason is deliberately not a fault — that is a bounded read doing
        its job, and reporting it would be reporting the control working. -->
  - [x] S6.4.c Compare computed digest/length/media type to artifact metadata,
        manifest expectation, and provenance binding. Evidence: mismatch tests.
        <!-- Three expectations, checked SEPARATELY because they fail for
        different reasons and are owned by different people: the store metadata
        disagreeing means the object changed or the metadata was written wrong;
        the manifest disagreeing means the assembly is building something other
        than what was planned; the provenance disagreeing means these are not the
        bytes a chain of custody was established for, and that is the only one of
        the three not fixed by rebuilding. `nothing-to-compare-against` is
        reported, because a rehash with no expectation is an expensive way to
        record a number while the log says "verified". Media type comes from
        S3.11's sniffer, which returns a LIST — a GIF header with a zip's central
        directory at the end is a valid image to a decoder and a valid archive to
        an unzipper, and a bundle carrying one hands each consumer a different
        file while the manifest describes one. -->
  - [x] S6.4.d Copy/link into assembly only after verification and bind manifest
        entry to computed values rather than caller metadata. Evidence:
        spoof-prevention tests.
        <!-- The headline. `BINDING_SOURCES` has four values and a test asserts
        exactly ONE of them produces no findings. `manifest-bound-to-store-metadata`
        IS the metadata-only checksum spoof this cell exists to reject, and it
        passes every check that reads the manifest.
        `manifest-bound-to-the-source-bytes-rather-than-the-written-ones` is the
        near-miss that survives review, because it IS a computed digest — of the
        wrong bytes; the archive contains the copy. The order is data
        (`BINDING_ORDER`), and `rehash-written` sits between the copy and the
        binding because after a successful verify and a successful copy there is
        nothing left that FEELS unchecked, which is why it is the step every
        implementation omits. -->
  - [x] S6.4.e Detect storage changes between read/hash/copy through immutable
        version/fencing or hash the exact written bytes. Evidence: race tests.
        <!-- Exactly TWO ways to close the window, and one must hold: either the
        store gives a handle that changes whenever the object does and both ends
        are compared, or the written bytes are hashed. A system with neither is
        not slightly weaker — it has no means by which it could ever notice, which
        is what `no-fencing-and-no-rehash-of-the-written-bytes` says. A race has
        no in-memory representation, so `tools/artifact-rehash-race.mjs` performs
        it on real files: the source is replaced after being verified and before
        being copied, and the run shows the written-bytes rehash is what catches
        it while a checker that hashed the source sees a perfectly consistent
        story. It carries the counter-control too — an unraced copy must report
        nothing. -->
  - [x] S6.4.f Quarantine/report corruption and affected derivatives/releases
        without publishing partial assembly. Evidence: corruption workflow test.
        <!-- CORRUPTION IS A FACT ABOUT A POPULATION. The artifact was corrupt
        BEFORE the rehash noticed, so failing this assembly answers the smallest
        version of the question and nothing else will ask the larger one.
        `derivative-left-out-of-the-enumeration` is checked against a list the
        caller supplies from the dependency graph, because an enumeration that
        walks the same index the corruption came through finds whatever that index
        says — which is the thing in doubt. `partial-assembly-published` is
        reported first and alone: every other failure here can be repaired late
        and that one cannot be undone at all. A counterexample test asserts no
        hold is demanded when nothing depends on the artifact. -->
- [x] S6.5 Produce deterministic archives. Evidence: identical inputs yield
      byte-identical output and hash.
      <!-- `src/determinism.ts` + `tools/archive-determinism.mjs` (new Nx target).
      A non-reproducible archive breaks exactly one thing, and it is the thing
      every later cell depends on: you can no longer tell "the inputs changed"
      from "the build ran again". A rebuild whose hash differs is then
      indistinguishable from a tampered artifact — and because the first
      explanation is almost always the right one, the response to both becomes
      re-run it and move on, which is correct ninety-nine times and catastrophic
      once, with nothing about the ninety-nine to say which run was the
      hundredth. Four fault families, 39 faults, all producible; 24 tests. -->
  - [x] S6.5.a Define normalized entry ordering, path encoding, timestamps,
        owner/group, permissions, extended attributes, line endings, and
        manifest serialization. Evidence: reproducibility profile.
        <!-- `CANONICAL_REPRODUCIBILITY_PROFILE` ships and is held to zero
        findings. SORT ORDER IS BYTE ORDER: `localeCompare` puts "a.txt" before
        "B.txt" and byte order puts "B.txt" first — asserted in the spec rather
        than described — so a collated archive's entry order is a function of
        LANG, which is the sort every reviewer reads as correct and the one input
        nobody records. The only admissible timestamp came from an INPUT: an mtime
        is a property of a CHECKOUT, so the same commit cloned twice makes two
        archives and neither is wrong. And `line-endings: preserved` is
        deliberately NOT a fault — normalizing line endings rewrites CONTENT and
        moves every digest the manifest records, so the dimension must be DECIDED
        and `preserved` is a decision while `platform` is not. -->
  - [x] S6.5.b Pin archive format/version, compression algorithm/level/library,
        dictionary, and platform-independent flags. Evidence: toolchain lock.
        <!-- THE CONTAINER IS PART OF THE OUTPUT, and the three gzip header
        fields are named separately: FNAME carries the path of the temporary file
        the compressor was handed, MTIME is four bytes of build clock in a header
        nobody reads, and byte 9 is the OPERATING SYSTEM the compressor ran on —
        it encodes nothing about the content, decompresses to the same bytes
        whatever it says, and makes the CI archive differ from the laptop one in a
        way no diff of the contents will ever show. The tool demonstrates exactly
        that by flipping the byte and asserting the decompressed output is
        unchanged while the hash is not. The format/compression pair is also
        checked for internal agreement in both directions. -->
  - [x] S6.5.c Exclude temporary paths, host metadata, absolute source paths,
        random IDs, current clock, process order, and nondeterministic maps.
        Evidence: nondeterminism scan.
        <!-- The scan runs over what the archive ACTUALLY contains — paths and
        text samples — rather than over a declaration, and the entry ORDER is
        rechecked here because a profile that declares byte order and a builder
        that sorts by collation is precisely the case a declaration cannot detect;
        this is the only place the real order is visible. Host facts are supplied
        by the caller for the same reason S6.2's protected terms are: a contract
        entry point has no environment, and a scan that guessed at hostnames would
        report the wrong ones and be switched off.
        `timestamp-that-is-not-the-declared-epoch` takes the declared instant as an
        argument, because the field is almost always called something innocuous
        like "generatedAt" and moves the hash every second. -->
  - [x] S6.5.d Build from the same inputs on supported operating
        systems/runtimes and compare bytes/hash/entry metadata. Evidence:
        cross-platform result.
        <!-- `buildComparisonFaults` compares archive digest, manifest digest,
        entry ORDER and per-entry mode/uid/gid/mtime, because "the bytes differ"
        is a useless message and "the mtime of one entry differs" is a fix.
        `every-build-on-one-platform` is reported honestly: path encoding, line
        endings and the container OS byte are exactly the differences a single
        platform cannot show, and this box is one platform — so the tool asserts
        the checker STILL reports `platform` as an untested axis while agreeing on
        the bytes. A green single-platform run that claimed cross-platform
        reproducibility would be the failure this item exists to prevent. -->
  - [x] S6.5.e Build under shuffled input order, clocks, locale, time zone, and
        concurrency and compare byte identity. Evidence: perturbation tests.
        <!-- `tools/archive-determinism.mjs` contains a real ~60-line ustar writer
        whose every non-content field comes from the profile, so a claim in the
        profile and a byte in the output are the same statement. It builds one
        input four times — shuffled order, every source file touched an hour
        forward, a Turkish locale, a Kiritimati time zone and a 0077 umask, all
        set for real in the process — and the four archives are byte-identical.
        Beside them four CONTROLS that must each produce a difference, because a
        determinism suite where nothing can ever differ would pass over a builder
        returning a constant: collation ordering, a real mtime, the build user's
        uid, and gzip at two levels plus the OS byte. `only-one-build` and
        `axis-never-perturbed` are the two ways this claim gets made without being
        tested, and both report green today. -->
- [x] S6.6 Generate SBOM-like component and dependency inventory for each
      release bundle. Evidence: every file and external ref has provenance.
      <!-- `src/bill-of-materials.ts` + `tools/bom-reconciliation.mjs` (new Nx
      target). A bill of materials generated from the same declaration the bundle
      was built from is a photocopy: it agrees with the manifest for the reason a
      photocopy agrees with the original, it can never disagree, and the
      disagreement is the only thing an inventory was ever for. So the failure
      this item guards against is not a missing inventory — it is one that exists,
      lists every file, reads as evidence, and is a second copy of the claim it
      was supposed to check. Six fault families, 60 faults, all producible; 59
      tests. -->
  - [x] S6.6.a Inventory every archive entry with logical role, path, media
        type, bytes/hash, source artifact/revision, generated/third-party
        status, and provenance ref. Evidence: file component rows.
        <!-- `FileComponent` + `fileComponentFaults`. `role` is S6.1's
        `BundleEntryKind` rather than a second vocabulary for the same thing, and
        `mediaType` goes through S3.11's `sniffMediaType` where bytes are
        supplied — the module decides neither question twice. `digestHex` is a
        plain string and NOT the branded `Sha256Hex` deliberately: a row is an
        OBSERVATION, so it has to be able to hold what was observed, and a branded
        field would make `row-with-a-digest-that-is-not-a-digest` unrepresentable,
        which improves the type and not the archive. "Generated" is split three
        ways because a build tool, a model and a person are three answers to who
        is answerable, and `generated-row-with-no-source-revision` is the row that
        reads as complete and is not — regenerating the file is the only way to
        check the digest, and that needs the revision. `sampledBytes` is optional
        because an inventory is usually assembled from an index, and a check that
        demanded the bytes would be run without them. -->
  - [x] S6.6.b Inventory external URLs/resolvers/providers/models/tools/plugins/
        packages/licenses that the bundle references or requires. Evidence:
        external component rows.
        <!-- `ExternalComponent` + `externalComponentFaults`, ten kinds because
        ten different things get referenced and exactly one of them arrives
        through a package manager — a bill generated from a lockfile lists that
        one and none of the other nine. The most common way a release stops
        working is a URL in a configuration file nothing counted as a dependency.
        `locator-that-carries-a-credential` is the fault that turns a compliance
        document into an incident: a bill of materials is written to be PUBLISHED,
        and a signed URL pasted into a config travels into it without anybody
        deciding to publish a token. `license-document-referenced-by-url-alone`
        applies the same idea to the one external everybody assumes is safe to
        leave remote — a license reachable only by URL can change or stop
        resolving, and the release then cannot show what it agreed to when it
        shipped. A locator with no scheme stops the checks for that row rather
        than reporting five consequences of one cause. -->
  - [x] S6.6.c Record dependency edges, version/range/digest, required/optional,
        runtime/build-only, target/environment, and resolution state. Evidence:
        dependency graph.
        <!-- `BomDependency` + `dependencyEdgeFaults`. A RANGE IS NOT A DEPENDENCY
        OF THIS BUNDLE: the bundle contains one resolved thing, and a range
        describes the family of bundles the build was permitted to produce,
        exactly one of which was produced. The mirror-image fault is
        `dynamic-edge-carrying-a-digest` — an answer that arrives at runtime by
        design has no digest by nature, so recording the one observed during the
        build states a fact about every future run from one moment. Both read as
        thorough. `vendored` is kept apart from `resolved` because the bytes are
        in the bundle and the range no longer applies to anything, and
        `dynamic-at-runtime` apart from `unresolved` because nothing is missing.
        Cycles are NOT re-decided here: S5.8 already owns what a cycle means per
        edge kind, and a second answer would be a second answer rather than a
        check. `edge-for-an-environment-the-bundle-does-not-target` reports
        nothing when the caller passes no targets, so a caller that does not know
        them is not told every edge is wrong. -->
  - [x] S6.6.d Record license/rights/consent/export/security metadata and
        unresolved/unknown status without fabricating values. Evidence:
        compliance fields.
        <!-- `ComplianceRecord` + `complianceFaults`. UNKNOWN IS A VALUE AND IT IS
        THREE VALUES: "nobody looked", "somebody looked and there is none" and
        "the question does not apply" have three different consequences, and the
        field that collapses them is the field that makes an unread license look
        permissive. `dimension-not-recorded-at-all` is the difference between a
        shorter document and an honest one — an absent dimension is not `unknown`,
        it is a document that never asked, and it reads as one with nothing to
        report. `license-inherited-from-the-bundle` is the sharpest instance of
        fabrication and the reason the worked example gives its third-party row
        `OFL-1.1` against a bundle at `CC-BY-4.0`: a default that fills empty
        license fields with the publisher's own expression produces an inventory
        with no blanks and every third-party row wrong in the same direction, and
        an example where every expression agreed could not tell that from a
        correct inventory. `not-applicable` is required to carry a basis because
        it is a conclusion somebody reached and otherwise the state that costs
        nothing to write and closes the question. The consent fault does not
        object to `unknown` being RECORDED — that is the decision working; it
        objects to shipping a release whose model-generated component is the one
        nobody assessed. -->
  - [x] S6.6.e Link components to generation/transformation actions, actors,
        tools/models/policies, validation, and release candidate. Evidence:
        provenance linkage.
        <!-- `GenerationAction` + `bomLinkageFaults`.
        `action-for-another-release-candidate` is the fault worth building this
        for: a component carried over from a previous build arrives with a
        complete, internally consistent chain — actor, tool, policies, a passing
        validation run — and every check that reads the chain on its own terms
        approves it. The only thing wrong with it is which release it belongs to,
        and that is visible from nowhere else; the spec asserts it is the ONLY
        finding on such an action. `action-consuming-something-the-bill-does-not-
        list` is the input side of the same idea and is where a training corpus, a
        reference dataset or a licensed font goes missing — the output is
        inventoried and what it was made from is not. `action-with-an-unattributed
        -actor` treats a build service as an actor with an identity and only
        `unattributed` as an absence, with a counterexample test, because a rule
        that fired on a service-run build would be switched off in a week.
        `kind` is a plugin-owned string for S5.8's reason. -->
  - [x] S6.6.f Reconcile manifest/archive/provenance/importer refs so no file or
        external ref is absent, duplicated, or orphaned. Evidence: completeness
        check.
        <!-- `bomReconciliationFaults` over four witnesses, compared pairwise in
        BOTH directions for S4.18's reason: an entry with no declaration and a
        declaration with no entry are wrong in opposite ways. ABSENT, DUPLICATED
        AND ORPHANED ARE THREE FAULTS AND A SET SEES TWO — so `archivePaths` is a
        LIST that is COUNTED, never a set that is tested for membership.
        `tools/bom-reconciliation.mjs` settles that with real GNU tar: it builds
        the archive, `tar --append`s a second member at a path the archive already
        carries, extracts it, and shows the recipient holds bytes whose digest is
        not the one the bill recorded — while asserting BOTH set-membership
        sweeps report it clean FIRST, so the control cannot go vacuous. Verified
        by patching `tally(witnesses.archivePaths)` in the BUILT dist to dedupe
        and confirming the tool fails. Three more controls: a member appended
        after the manifest was written (bill and manifest agree perfectly and only
        the archive witness sees it — the argument for the archive being a witness
        at all), the PHOTOCOPY (`every-row-from-one-observer` fires on an
        inventory that reconciles against all four witnesses and has nothing else
        wrong with it), and an ELF binary of exactly the same length described as
        an image with a digest that verifies, so the size check and the digest
        check both pass. The importer comparison covers only externals a RECIPIENT
        reaches, since a build-time model endpoint is legitimately absent from an
        importer's list. -->
      <!-- S6.6 rollup: 3960 kit tests pass (59 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 84 entry points with no drift and no violations. -->

- [x] S6.7 Add C2PA signing/validation adapters where the asset format supports
      them. Evidence: valid, tampered, unknown-trust, expired, and stripped
      credentials are distinguished.
      <!-- `src/content-credentials.ts` + `tools/c2pa-signing.mjs` (new Nx
      target). The evidence line names five situations together because a
      validator returning `{ valid: boolean }` gives the SAME answer for four of
      them: ask only whether the signature verifies and a credential signed by an
      expired certificate says yes, one whose chain reaches no anchor says yes,
      and an asset with NO credential says yes because nothing failed. One of
      those four is fine, one is a policy question, one is a key-lifecycle
      problem, and one is an asset with no provenance being displayed as though
      it had some. Five fault families, 47 faults, all producible; ten
      independent checks; nine states; 60 tests. The kit owns the CONTRACT and
      the state mapping — `@oshun/content-signing` is the repository's one real
      Ed25519 signer and is resolved from the SPEC (devDependency +
      `ALIASED_PACKAGES`), so nothing here is a second implementation of
      something that already exists. -->
  - [x] S6.7.a Register supported asset formats, C2PA spec/profile, embedding/
        sidecar mechanism, hard/soft bindings, assertions/actions, and
        unsupported behavior. Evidence: adapter profile registry.
        <!-- `AdapterProfile` + `adapterProfileFaults`, and the shipped
        `WORKED_ADAPTER_PROFILE` is held to zero findings.
        `embedded-manifest-with-no-hard-binding` is the fault that produces a
        LABEL rather than a credential: a manifest carried inside the file,
        signed, listing every action and naming no digest of the content asserts
        that somebody said these things and not that they are about THIS asset —
        and it copies into another file unchanged.
        `fallback-that-ships-the-asset-silently` is decision-by-default: refusing,
        writing a sidecar and shipping it labelled unsigned are three decisions
        somebody made, and shipping it silently is the absence of one.
        `ship-silently` is IN the vocabulary for that reason — a registry that
        could not express it would report every deployment as compliant. The
        soft-binding rule is scoped to formats where a hard binding is available,
        with a counterexample test for a live stream, because a rule that fires
        on the only configuration a format allows is one somebody switches
        off. -->
  - [x] S6.7.b Assemble ingredient/action/tool/actor/policy assertions from
        authoritative bundle/provenance records. Evidence: manifest construction
        fixtures.
        <!-- `CredentialAssertion` + `assertionAssemblyFaults`.
        `assertion-the-caller-supplied` is the whole item in one fault: a
        credential is a set of statements somebody signed, and the only question
        worth asking about each is where it came from. Read the actions out of
        the provenance ledger and the credential certifies what happened; take
        them from the caller and it certifies what the caller typed, in a
        document that looks exactly the same and is believed by everything
        downstream. `caller-supplied` is a value in `ASSERTION_SOURCES` for the
        same reason `ship-silently` is one. The reconciliation with the bundle
        runs in BOTH directions, and
        `bundle-ingredient-with-no-assertion` is the direction people forget: an
        ingredient list that omits an input is not a smaller truth, because a
        consumer reads it as the complete set of things the asset was made from
        and the omitted one is usually the one somebody would have asked
        about. -->
  - [x] S6.7.c Sign with governed key/cert chain/timestamp and bind to exact
        bytes without exposing key material. Evidence: signing integration
        tests.
        <!-- `SigningRequest` + `signingFaults` + `signerPortEscapeHatches`.
        `signing-key-that-leaves-its-boundary` and `key-material-in-the-request`
        are the same failure at two distances — a key that CAN be copied and a key
        that already has been — and the second is found by scanning every string
        field for a PEM private-key header, because key material never arrives in
        a request deliberately, it arrives in a field somebody added for
        debugging. The finding NEVER echoes what it found, asserted by a test, or
        the conformance check would put the key in the log of every deployment
        that ran it. `signerPortEscapeHatches` is shaped after S6.1's
        `profileEscapeHatches` and takes `unknown`; the spec runs it against the
        REAL `Ed25519ClaimSigner`, which holds its PEM in a `#private` field and
        therefore reports clean, and against a port with the key on a public
        field, which reports twice — once for the field NAME and once for the PEM
        in its value. `digest-that-came-from-the-caller` is S6.4 arriving here
        unchanged, and `no-timestamp-authority` is decision 5 as a requirement:
        without one the credential's lifetime becomes the certificate's, and an
        archive signed today stops validating on a date chosen by a certificate
        authority's renewal policy. -->
  - [x] S6.7.d Validate manifest structure, binding, signature, chain, trust
        list, revocation, timestamp, assertion support, and ingredient
        availability separately. Evidence: validation matrix.
        <!-- `VALIDATION_CHECKS` (TEN — `certificate-validity` is split out of
        `chain`, because "no path reaches any anchor" and "the path exists and a
        certificate on it was outside its window" are different failures with
        different responses and only the second is answerable by a timestamp) +
        `validationReportFaults` + `validityAtSigning`. The item says
        "separately" and that is decision 3: short-circuiting returns "invalid"
        when the useful sentence was "the signature is perfect and the
        certificate lapsed last Tuesday", so a report that stopped early is
        itself a finding. NOT-PERFORMED IS NOT PASS — a revocation responder
        nobody could reach has established nothing, and
        `unavailable-revocation-treated-as-a-result` fires on the DETAIL reading
        like an answer rather than on the outage, with a counterexample test for
        an honestly-described outage. `credential-absent-and-checks-reported-as-
        passing` is the shape of the boolean this module replaces: with nothing
        to check, every question passes by having nothing to fail.
        `validityAtSigning` takes all three instants as arguments (a function that
        read the clock would answer differently every run and could not be tested
        at a boundary) and refuses to be rescued by an UNVERIFIED timestamp,
        because that is a string the signer chose and honouring it lets an
        expired key sign anything by backdating. -->
  - [x] S6.7.e Represent valid/trusted, cryptographically valid-untrusted,
        tampered, revoked/expired, unknown/unsupported, missing, stripped, and
        partially recoverable states. Evidence: result union tests.
        <!-- `CREDENTIAL_STATES` + `credentialState`. NINE states, not eight:
        revoked is separated from expired because a lapsed certificate is a
        calendar problem whose content is probably fine and a revoked one puts
        everything that key ever signed in doubt — different people, different
        urgency, and a system reporting them alike routes the second to whoever
        handles renewals. The precedence is stated and tested: TAMPERING OUTRANKS
        TRUST, since reporting the trust problem first sends an incident to the
        trust-list maintainer. MISSING AND STRIPPED ARE DIFFERENT AND THE ASSET
        CANNOT TELL YOU WHICH — an image with no manifest looks identical whether
        one was never made or a platform removed it on upload — so `stripped`
        requires `ExternalWitness`, and a validator claiming it from the asset
        alone would be inventing history. That gap is what S6.8 exists to close.
        The spec drives the signature check through the repository's REAL Ed25519
        signer: a real signature verifies and gives valid-and-trusted, one flipped
        byte verifies false and gives tampered, and a signature from another key
        verifies false in exactly the same way — which is the fact that makes the
        chain and trust checks necessary rather than decorative. -->
  - [x] S6.7.f Test official/third-party fixtures, post-sign byte mutation,
        metadata stripping, certificate lifecycle, and offline trust cache.
        Evidence: interoperability report.
        <!-- `INTEROPERABILITY_CASES` (ten) + `interoperabilityFaults`, and
        `tools/c2pa-signing.mjs` for the parts that need real cryptography.
        `self-produced-fixture-presented-as-third-party` is the fault this exists
        for: an adapter validated only against bytes it produced demonstrates that
        it agrees with itself, which every implementation does including the wrong
        ones, and generating the fixture is the cheapest way to appear to have
        tested interoperability. `state-never-demonstrated` closes the other end —
        a suite can cover every case in the list and never once produce `revoked`
        or `stripped`, and a state no test has seen is a branch nobody has
        executed (S6.5's `axis-never-perturbed` in another domain). The tool
        produces all five situations with REAL OpenSSL certificate authorities
        (two independent roots, three leaves, real `openssl verify` path
        building) and real P-256 signatures, and CONTROL 1 asserts the naive
        boolean answers FOUR of them TRUE before the state machine answers them
        four ways — three of those four trues are wrong. Verified by patching
        `credentialState`'s `stripped` branch in the BUILT dist and confirming the
        tool fails. -->
      <!-- S6.7 rollup: 4021 kit tests pass (60 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan,
      packed-consumer-smoke and test-only-import-fixtures all pass over the
      rebuilt dist; the export map regenerates to 85 entry points with no drift
      and no violations. -->

- [x] S6.8 Add a provenance-ledger fallback for formats or channels that strip
      embedded credentials. Evidence: soft-binding recovery test.
      <!-- `src/soft-bindings.ts` + `tools/soft-binding-recovery.mjs` (new Nx
      target). A soft binding returns a DISTANCE, and the danger is that "close"
      is a number and a number invites a verdict. A perceptual hash's
      false-positive rate is a property of the CORPUS SIZE rather than of the
      algorithm, so a threshold that separates fifty images perfectly returns
      matches for unrelated photographs all day at a million — and a system that
      turns the best candidate into an answer produces confident, specific,
      wrong attributions, most often for the most-published images. Six fault
      families, 45 faults, all producible; seven outcomes; 53 tests. -->
  - [x] S6.8.a Register formats/channels/transforms that strip or alter embedded
        provenance and select approved binding/recovery method. Evidence:
        channel profile.
        <!-- `ChannelProfile` + `channelProfileFaults`. Which platforms remove
        which carriers is a fact somebody can go and MEASURE, so a channel with
        no registered behaviour is a finding rather than a default — the
        alternative is that a credential's survival is discovered by a reader who
        does not find it. `behaviour-recorded-with-no-observation` is the softer
        version: a behaviour written down from a blog post is a guess with a
        table around it, and it stops being true the next time the platform
        ships. `transforming-channel-relying-on-a-hard-binding-alone` is the
        arithmetic of the hard hash in the other direction — it will not match
        after a re-encode, so a transcoding channel with only that has no
        recovery at all. A counterexample test covers a channel that preserves
        the credential and needs no fallback. -->
  - [x] S6.8.b Persist external ledger record keyed by immutable provenance/
        artifact identity with hard hash, approved soft bindings, manifest/ref,
        trust, retention, and access policy. Evidence: ledger schema tests.
        <!-- `LedgerRecord` + `ledgerRecordFaults`. THE HARD HASH STAYS: an
        unmodified copy has to be recognised exactly, and a record holding only a
        soft binding answers "probably" to a question that has a yes.
        `content-derived-record-that-is-public` is the fault people argue with — a
        perceptual hash is not a copy of the image so publishing one feels
        harmless, and it is a durable identifier that SURVIVES CROPPING, which is
        the operation somebody performs to remove identifying context; a public
        index of them is a reverse image search over everything anybody
        registered. Every threshold carries `measuredAtCorpusSize`, because the
        expected number of false positives grows with the number of records
        searched and nothing in the number itself says so. And
        `binding-computed-below-the-signal-floor` closes a hole my own probe
        found: a featureless asset produces sixty-four bits of rounding error
        that index and match exactly like a real hash, so the strength the value
        was computed at travels on the record. -->
  - [x] S6.8.c Generate media-appropriate fingerprint/watermark/perceptual hash/
        lookup hint with privacy and collision review. Evidence: binding tests.
        <!-- A REAL DCT-II perceptual hash (`perceptualHash`), a real gradient
        hash (`differenceHash`), a real `hammingDistance`, plus `BindingReview` +
        `bindingReviewFaults`. The standard construction — 32x32 box reduction,
        separable DCT-II, the low-frequency 8x8 block against its own median —
        with ONE STATED MODIFICATION: the DC term is dropped, because brightness
        is the transformation applied most often and least deliberately, and the
        next coefficient along one axis is added so the hash is 64 bits of which
        64 are informative rather than 63. Measured, not described: half-scale
        moves it 0 bits, a brightness shift 0, an eight-pixel crop 2, and two
        different images 30. The two methods are shipped because they FAIL
        DIFFERENTLY, and the spec asserts the directions rather than claiming
        them. `binding-narrower-than-its-corpus-needs` takes the MEASURED
        collision rate rather than a formula, because the effective width depends
        on how the corpus is distributed and real image corpora are nothing like
        uniform. -->
  - [x] S6.8.d Recover exact/robust matches after representative resize, crop,
        transcode, recompress, remix, screenshot, rerecord, or normalization.
        Evidence: transformation matrix.
        <!-- `TransformationOutcome` + `transformationMatrixFaults`, and
        `tools/soft-binding-recovery.mjs` for the measurements. The tool runs
        SIX REAL ffmpeg transformations over a real frame — a bicubic downscale,
        a ten per cent border crop, a JPEG round trip at q25, a screenshot round
        trip (down to 40% and back through a lossy encode), a tone curve, and an
        H.264 pass — and decodes each back to raw gray. Five keep the record
        (0, 0, 0, 2, 0 bits of 64); the CROP MOVES IT SIXTEEN AND LOSES IT. That
        is a real property of the method, so the tool asserts the matrix REPORTS
        the loss rather than that no loss occurred, and fails if nothing is ever
        lost — a tool measuring only what survives would be describing its
        corpus. `transformation-measured-on-one-sample` is the other way a matrix
        reads as complete: the variance between items under one transformation is
        larger than the difference between transformations. -->
  - [x] S6.8.e Measure false positive/negative, ambiguity, insufficient
        evidence, collision, and adversarial removal. Evidence: known-corpus
        evaluation.
        <!-- `CorpusEvaluation` + `corpusEvaluationFaults`.
        `evaluation-with-no-negative-pairs` is the first thing to look for:
        measuring only true pairs gives RECALL, which answers "does a transformed
        copy find its record" and says nothing at all about "does an unrelated
        item find one" — and the second is where the harm is, because a false
        positive attributes somebody's work to somebody else with a number beside
        it. `threshold-measured-on-a-corpus-smaller-than-the-live-index` is the
        fault that survives every review: the threshold was correct when it was
        chosen, the index grew, and nothing anywhere is attached to the growth.
        `evaluation-that-never-produced-insufficient-evidence` and
        `ambiguity-never-observed` are the S6.5 `axis-never-perturbed` idea here —
        a suite where every query resolved has either a threshold that admits
        everything or a corpus with nothing hard in it, and the multiple-match
        path that produces the wrong attribution has never run. AN ADVERSARY IS
        NOT A TRANSFORMATION: resize and recompress happen, an adversary picks
        what maximises the distance while keeping the content worth publishing,
        and a recall figure over benign transforms only is true and irrelevant. -->
  - [x] S6.8.f Handle missing/multiple/private/deleted/revoked/invalid matches
        and display method, confidence/limitations, and trust safely. Evidence:
        negative and accessible UX tests.
        <!-- `recoveryOutcome` (seven outcomes) + `presentationFaults`. The
        exact hard match is decided FIRST and no distance is consulted for it.
        `multiple-soft-matches` and `insufficient-evidence` are first-class
        answers, and the tool's CONTROL 2 shows why: a nearest-candidate matcher
        named a record at 28 bits for an image that is not in the corpus at all,
        every time it was asked, while the strict recovery returned no-match. A
        WITHHELD RECORD IS DELIBERATELY INDISTINGUISHABLE FROM NO MATCH — the
        inverse of the kit's usual rule and of S5.8's, because telling a searcher
        a record exists and may not be shown tells them the item is registered,
        and a reverse lookup is the query where the existence of the answer IS
        the answer. At the surface, `soft-match-presented-as-an-identification`
        is where the whole chain is usually lost (one sentence reading "verified
        as" turns every careful layer beneath it into a claim) and
        `distance-rendered-as-a-probability` is the same failure in numeric form:
        6 of 64 bits is not "91% confident", the conversion invents a probability
        from a quantity that has none, and the invented number is the one people
        quote. Colour-alone and text-alternative faults cover the accessible-UX
        half. -->
      <!-- S6.8 rollup: 4075 kit tests pass (53 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 86 entry points with no drift and no violations. The tool
      was verified to FAIL by patching `usable` to a constant `true` in the built
      dist, which made CONTROL 3 report the real flat-frame signal strengths of
      1.4e-13 and 1.3e-12. -->
- [x] S6.9 Add publisher adapters with plan, execute, verify, and compensate
      phases. Evidence: partial external failure is recoverable and audited.
      <!-- `src/publishers.ts` + `tools/publish-saga.mjs` (new Nx target). The
      shape almost every publisher ships is one code path with a `dryRun` flag,
      which makes the plan a MODE of publishing rather than a document: it cannot
      be stored, diffed, reviewed or approved, it goes stale with nothing to
      notice, and the one case the four phases exist for — the target changing
      between deciding and doing — is invisible by construction, because the
      deciding happened inside the doing. Seven fault families, 59 faults, all
      producible; six reconcile actions; 44 tests. Composes S2.5's
      `PublisherRuntime` port (which already refuses the executor's own report as
      a verification source) and S3.9's `ExternalOutcome`, rather than restating
      either. -->
  - [x] S6.9.a Define adapter identity/version, target capability/config probe,
        supported bundle/profile/action, authorization, idempotency, and
        external reference contract. Evidence: publisher interface.
        <!-- `PublisherAdapter` + `adapterContractFaults`.
        `adapter-that-returns-no-external-reference` is the fault that makes every
        later phase impossible: with no identifier the target assigned there is
        nothing to read back, nothing to compensate and nothing to reconcile, so
        the pipeline necessarily falls back to trusting the response — which S2.5
        already refuses. `IDEMPOTENCY_HONOURING` carries `ignores-the-key` as a
        real value because plenty of real targets do, and a registry that could
        not express it would let every one of them look safe to retry; declaring
        it is NOT a fault, and `unknown` is, because the alternative to declaring
        it is discovering it as a duplicate publication. -->
  - [x] S6.9.b Implement read-only plan resolving destinations, transforms,
        eligibility, cost, impact, conflicts, irreversible points, and rollback/
        compensation. Evidence: plan fixtures.
        <!-- `PublicationPlan`/`PublicationStep` + `publicationPlanFaults`.
        `plan-that-performed-something` is the decision at the top of the module:
        every property a plan is supposed to have — reviewable, storable,
        comparable, approvable — depends on it having written nothing.
        `plan-with-no-irreversible-point-identified` fires on a plan whose every
        external step claims full reversibility, because a plan that is genuinely
        safe and one nobody thought about look identical, and the point of no
        return is the single fact an approver most needs. Cost and reach are
        required per step for the same reason: a plan that says what it will do
        and not what it will spend or who it will reach leaves the only reviewable
        facts as the ones nobody was worried about. -->
  - [x] S6.9.c Revalidate plan hash, release lock, authorization, gates,
        provenance/rights/consent, target health, and budget at execute.
        Evidence: stale-plan tests.
        <!-- `ExecuteConditions` + `executeAdmissionFaults`, twelve checks. The
        point is that all of them were checked BEFORE and that is not enough:
        each is a fact with a timestamp, and the interval between planning and
        executing is exactly the interval an approval workflow introduces.
        Rechecking is not redundancy — the first check answered a different
        question about a different moment.
        `plan-executed-under-a-digest-nobody-approved` catches the edited plan:
        the plan is stored, somebody amends a destination, the digest moves, and
        the approval still points at the old bytes.
        `target-health-never-probed` is kept apart from
        `target-unhealthy-at-execute` because taking the health from the plan is a
        statement about a moment that has passed and a system nobody here
        controls. -->
  - [x] S6.9.d Execute idempotently with durable per-step state, retries,
        external IDs, request/response hashes, partial result, and audit.
        Evidence: execution fault tests.
        <!-- `StepExecution` + `executionFaults`.
        `retry-that-changed-the-idempotency-key` is the fault that turns a retry
        into a second publication — it happens whenever the key is derived from
        something that moves (a timestamp, an attempt number, a fresh id) and the
        resulting duplicate is indistinguishable at the target from a deliberate
        second publish. `settled-step-with-no-request-digest` is what a dispute
        needs: six months later the question is what was SENT, and a system
        holding only its own summary of what it meant to send cannot answer it.
        `sent-step-that-was-never-recorded-as-started` is the ordering bug — a
        record written after the request returns leaves a publication nothing
        knows about if the process dies in between. -->
  - [x] S6.9.e Verify by target read-back of bytes/hash, metadata, audience,
        state, permissions, discoverability, and external acknowledgements.
        Evidence: verification tests.
        <!-- `VerificationReadback` + `verificationFaults`, seven dimensions read
        SEPARATELY. `published-but-not-discoverable` gets its own fault rather
        than being one more mismatch, because it is the partial success that
        reaches production most often and answers 200 to everything: the object
        exists, the bytes are right, the permissions are right, and nobody can
        find it, and the failure surfaces weeks later as an absence. The tool
        produces exactly that against a real HTTP target — a real 201, a real
        read-back where six of seven dimensions match, and a real search
        returning zero hits. `dimension-never-read-back` treats an unread
        dimension as a finding, since an unread one reads as a matching one, and
        `verification-that-is-only-the-publish-response` is S2.5's decision
        arriving here as a checks array where no source is the target. -->
  - [x] S6.9.f Compensate reversible steps in safe order, preserve irreversible
        effects, and expose manual remediation for failed/partial compensation.
        Evidence: compensation scenarios.
        <!-- `compensationOrder` (what should happen) and `compensationFaults`
        (what did) are DELIBERATELY separate functions: a system where the same
        code does both can only ever agree with itself, which is S6.6's photocopy
        in another shape. The order is the reverse of execution and stops at the
        first irreversible step counting backwards, and everything before that
        point is PRESERVED — removing a step something irreversible depends on
        leaves a state neither the plan nor the target's history describes. Every
        preserved effect and every failed compensation becomes an instruction
        naming the step, the external reference and the destination, because a
        system that cannot undo what it did and only logs that fact is how an
        asset stays published for a year after the release that put it there was
        withdrawn. Verified by patching the `blocked` propagation out of the BUILT
        dist and confirming the tool reports that the upload would be undone
        behind an announcement the target refuses to recall. -->
  - [x] S6.9.g Reconcile unknown outcomes after timeout/callback loss/worker
        death before retry. Evidence: external outcome tests.
        <!-- `reconcilePublish` + `publishReconciliationFaults`, composing S3.9's
        `ExternalOutcome` rather than inventing a second vocabulary for the same
        four answers. The branch this exists for is an unknown outcome against a
        target that IGNORES the idempotency key: retrying is a second publication
        and not retrying may be a dropped release, both wrong in ways nobody can
        see from here, so it is settled by ASKING when a probe exists and by a
        person when it does not — never by the reconciler picking whichever error
        it finds less embarrassing. `mark-failed-from-the-probe` is in the
        vocabulary and is never CHOSEN, which the spec asserts: a probe reporting
        "failed" means nothing landed, which is a reason to retry. It exists so
        the fault checker can report a system that took it anyway, and
        `callback-loss-treated-as-a-failure` is the expensive, reasonable-looking
        version — a webhook that never arrived is a statement about the callback
        and says nothing about whether the publish happened. -->
      <!-- S6.9 rollup: 4120 kit tests pass (44 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 87 entry points with no drift and no violations. -->
- [x] S6.10 Add immutable release IDs and source revision locks. Evidence:
      post-approval edits cannot mutate a release candidate.
      <!-- `src/release-locks.ts` + `tools/release-lock-race.mjs` (new Nx target,
      races a REAL PostgreSQL). Address a candidate as "project 42's current
      release" and editing project 42 changes what the release is: every gate
      result, every signature and every credential downstream then attests to
      different content under the same name, and NONE OF THEM IS WRONG, because
      each was true when it was made. There is no moment at which anything
      failed, which is why nothing catches it. Six fault families, 38 faults, all
      producible; 34 tests. -->
  - [x] S6.10.a Create release ID independently from mutable object/project IDs
        and bind target/profile/environment/locale/territory/audience. Evidence:
        release identity tests.
        <!-- `ReleaseIdentity` + `releaseIdentityFaults`. The id is MINTED and
        carries no object id, no slug and nothing that can be renamed; what binds
        it to the world is six recorded dimensions, because "the German release"
        and "the German release for the EU store" are different things a derived
        id cannot tell apart. `release-id-that-contains-the-subject-reference` is
        the version that survives a review of the minting code — generated
        randomly and then FORMATTED as `project-42-release-3`, which gives back
        every property the minting was for.
        `two-releases-binding-the-same-dimensions` is the other direction, with a
        counterexample test showing a different territory is a different release
        and not a finding. -->
  - [x] S6.10.b Freeze exact source revisions, artifact byte refs/hashes,
        dependencies, bundle profile, manifests, contracts, policies/gates,
        approvals, and publisher config. Evidence: release lock manifest.
        <!-- `ReleaseLock` + `releaseLockFaults` over all ten inputs. THE LOCK
        NAMES REVISIONS, NEVER REFERENCES: "the policy set" is not frozen and
        "policy set at revision 41" is, and the test for every entry is whether
        two people resolving it a year apart get the same bytes — which a
        reference with no version fails while looking exactly like one that does
        not. The three inputs people leave out are `policies`, `gates` and
        `publisher-config`, and the detail says why: they feel like environment
        rather than content, and a gate definition that changed after approval
        means the release that shipped passed a check nobody can re-run. A pin
        without a digest is refused wherever the input has bytes, since a
        re-publish under the same reference is otherwise invisible. -->
  - [x] S6.10.c Create the lock atomically with approval/candidate transition
        and reject stale expected source revisions. Evidence: lock race tests.
        <!-- `LockAdmission` + `lockAdmissionFaults`, and
        `tools/release-lock-race.mjs` against a real PostgreSQL 16.14.
        `lock-written-outside-the-candidate-transition` is easy to ship precisely
        because the two writes are adjacent IN THE CODE and not in the database;
        the milliseconds between them are exactly the window in which the content
        moves. THE TOOL FOUND A DEFECT IN MY OWN FIRST DESIGN: the atomic insert
        carried `NOT EXISTS (SELECT 1 FROM release_lock WHERE held)` and produced
        THREE simultaneous winners, because that clause is a READ and reads do not
        conflict with another transaction's uncommitted write under
        read-committed. The fix is a partial unique index, and
        `held-lock-uniqueness-left-to-a-predicate` is a shipped fault kind
        because of it. CONTROL 1 now drops the index and reproduces the three
        winners from the same statement, so the finding is demonstrated rather
        than described. -->
  - [x] S6.10.d Prevent edits, restores, recomputation, metadata changes, or
        target changes from mutating the locked candidate. Evidence:
        post-approval mutation tests.
        <!-- `MutationAttempt` + `lockedMutationFaults` over eight mutation kinds.
        REFUSING IS HALF THE ANSWER: a conflict and nothing else sends somebody to
        edit the underlying object instead, which is the same mutation by a route
        the lock cannot see, so `refusal-with-no-candidate-to-go-to` demands the
        refusal name where the change belongs.
        `mutation-refused-against-a-lock-that-is-not-held` is the counterexample
        rule, with a test — a released, cancelled or expired lock freezes nothing,
        and a system that keeps refusing after it is gone is one people route
        around permanently. The database side is CONTROL 3 in the tool: a guarded
        UPDATE changes 0 rows while the lock is held, 1 row after it is released,
        and the same statement without the guard changes 1 row either way. -->
  - [x] S6.10.e Require changed input to create a new release candidate/version
        linked as replacement/supersession. Evidence: new candidate lineage
        tests.
        <!-- `CandidateLineage` + `candidateLineageFaults`.
        `changed-input-with-no-new-candidate` takes the inputs that ACTUALLY
        changed and asks whether a candidate exists for them, because the failure
        mode is not somebody bypassing the lock — it is somebody changing an input
        the lock froze and everybody agreeing afterwards that it was a small
        change. `two-candidates-superseding-one` is a fork presented as a line,
        resolved by whichever query sorts first, and
        `supersession-that-changed-nothing` catches the lock being worked around
        by re-creating the release. The cycle check walks the chain, because a
        history that loops has no first version and no current one. -->
  - [x] S6.10.f Audit lock creation, reads, failed mutation attempts,
        cancellation, expiry, and release outcome. Evidence: lock audit tests.
        <!-- `LOCK_AUDIT_EVENTS` + `lockAuditFaults`. `reads-not-audited` and
        `refusals-not-audited` are split out of the generic missing-event fault
        because they are the two everybody omits and for OPPOSITE reasons: a read
        feels like nothing happened, and a refusal feels like nothing happened
        either. A refused mutation is the only evidence the lock ever did
        anything, and a system that audits what succeeded and drops what it
        refused cannot show it was ever holding; a read matters because when a
        lock turns out to have been wrong the question is who acted on it, and a
        trail of writes alone names everything the lock did and nobody it
        reached. `expiry-recorded-with-no-outcome` is the quiet one — a lock that
        lapsed and a release that was abandoned become the same row. -->
      <!-- S6.10 rollup: 4155 kit tests pass (34 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 88 entry points with no drift and no violations. -->
- [x] S6.11 Add publish eligibility recomputation inside the transaction or
      release lock. Evidence: time-of-check/time-of-use race test.
      <!-- `src/publish-eligibility.ts` + `tools/eligibility-toctou.mjs` (new Nx
      target, races a REAL PostgreSQL 16.14). "Inside the transaction" turns out
      not to be a property of the transaction: with the recomputation and the
      lock write in ONE transaction, a consent revoked between them is still
      published at read-committed, because a transaction that only READ a row
      does not conflict with the transaction that changes it — and it is still
      published at repeatable-read and at serializable, where the re-read answers
      from the snapshot the transaction opened with. Raising the isolation level
      HIDES the change read-committed would have caught. Six fault families, 47
      faults, all producible; 54 tests. -->
  - [x] S6.11.a Resolve required gates/approvals/rights/consent/provenance/
        dependencies/target policy for exact release candidate and destination.
        Evidence: eligibility policy tests.
        <!-- `RequirementResolution` + `requirementResolutionFaults` over the
        item's own seven kinds. Decision 1: eligibility is a function of
        (candidate, DESTINATION), because the destination decides which rights
        clearance applies and which gates run at all, so
        `requirement-set-reused-across-destinations` means one publish was
        authorized by checks resolved for the other and the store rules nobody
        read are the ones that come back. `evidence-kind-neither-required-nor-
        excused` is S6.6's "unknown is a value" from the other side: a destination
        that genuinely needs no rights clearance is real and common, so the rule
        is not that every kind is required but that every kind is either required
        or EXCUSED WITH A REASON — "rights do not apply to an internal preview"
        and "nobody thought about rights" are otherwise the same empty set. There
        is a counterexample test for exactly that case. -->
  - [x] S6.11.b Re-read current source/dependency/evidence/policy/waiver/signoff
        versions rather than trusting preview/UI state. Evidence: stale input
        tests.
        <!-- `ConsultedEvidence` + `staleInputFaults`. Every value carries the
        ORIGIN it was read from, and only one of the five is evidence;
        `evidence-taken-from-the-request` is the publisher supplying the evidence
        that authorizes the publisher, which arrives looking like a saved round
        trip. Decision 2 is that a claimed version which does not match the read
        is a REFUSAL AND NOT A CORRECTION: taking the newer value publishes
        content nobody looked at under an approval that named different content,
        which is S6.10's failure with a shorter window and no step at which
        anything failed. `status-recorded-from-a-probe-that-did-not-complete`
        covers both directions of S6.7's not-performed-is-not-pass — a
        `satisfied` from a call that threw is the fail-open, and an `invalid`
        from a call that timed out is a fabricated failure that sends the
        evidence owner to investigate a defect that does not exist. -->
  - [x] S6.11.c Recompute eligibility inside the same transaction as release
        lock or under an equivalent fenced reservation. Evidence: atomicity
        integration tests.
        <!-- `AtomicSite` + `eligibilityAtomicityFaults`, and every rule here was
        MEASURED against PostgreSQL 16.14 rather than reasoned about. THE TOOL
        FOUND THE DESIGN, NOT JUST A DEFECT IN IT. `consulted-input-with-neither-
        a-row-conflict-nor-a-write-predicate` fires even when the recomputation
        and the write share one transaction, because reads do not conflict. And
        `write-predicate-relied-on-under-snapshot-isolation` INVERTS the usual
        advice: the identical `INSERT ... WHERE version = $expected` refuses
        correctly at read-committed, where the predicate is evaluated against the
        current row, and silently accepts at repeatable-read and serializable,
        where it is evaluated against the snapshot — and serializable does not
        abort, because two transactions are a dependency and not a cycle, so
        "publish then revoke" is a legal serial order. A system hardened by
        raising the level gets quietly worse. What works at every level is a ROW
        CONFLICT on every consulted input, which CONTROL 5 shows ordering a
        concurrent revoke after the lock write and raising "could not serialize
        access due to concurrent update" against one that already committed. The
        fence branch exists because the item allows an equivalent reservation,
        and `fence-token-the-writer-validates-itself` is the S6.10.c finding in a
        new shape: a caller comparing a token to its own copy before writing is a
        read-then-write with a comparison in between. -->
  - [x] S6.11.d Reject missing/stale/expired/invalid/unavailable/out-of-scope/
        unauthorized evidence with exact safe reasons. Evidence: refusal matrix.
        <!-- `EvidenceRefusal` + `evidenceRefusalFaults` over a shipped
        `STATUS_DISPOSITIONS` table, with a test asserting the shipped table
        produces zero findings for every status. Decision 4: the seven are seven,
        and their remedies are obtain, re-run, renew, investigate, wait,
        resolve-for-this-destination and ask-somebody-else — seven different
        people doing seven different things. `unavailable-reported-as-a-failed-
        check` is the one everybody ships: nobody could ask the question, and
        calling that a failure sends a release owner to argue with a gate owner
        about an outage. `invalid-treated-as-retryable` is the opposite mistake —
        evidence that does not verify verifies no better the second time, and a
        retry there is the loop a pipeline sits in overnight.
        `unauthorized-refusal-that-discloses-the-evidence` is the leak: a refusal
        that explains which assertion failed turns a publish attempt into a way
        to read evidence the caller was refused. `every-refusal-given-one-
        disposition` is the S6.7 boolean-answers-N-situations check as an
        aggregate, and it is narrowed against the S4.15 trap — four of the seven
        legitimately share the plain `refuse` disposition, so it fires only on a
        matrix that covers ALL eight statuses and answers them identically, with
        a counterexample test for the legitimate four. -->
  - [x] S6.11.e Freeze consulted evidence/versions/hashes into the release lock.
        Evidence: lock provenance assertions.
        <!-- `EligibilityProvenance` + `eligibilityProvenanceFaults`, which takes
        the consultation as a SECOND argument so the two sides come from
        different places — a provenance record generated by the pass that wrote
        the lock agrees with it by construction, which is S6.6's photocopy in a
        third shape, and `frozen-evidence-that-was-never-consulted` is what
        catches it. Decision 5: the lock freezes what was CONSULTED rather than
        what was required, because "gate G was required" cannot be re-checked a
        year later and "gate G run 88 over digest X reported pass" can. The
        consultations that found NOTHING are frozen too, and
        `freeze-that-kept-only-what-was-found` is why: keep the satisfied rows and
        drop the rest and the lock truthfully says the release was published with
        the evidence it names, which is also exactly what a release published
        with four of nine pieces of evidence says. Reuses S2.11's
        `ReleaseEligibility` for the outcome rather than inventing a second
        vocabulary for the same six answers. -->
  - [x] S6.11.f Race a source edit, consent revoke, policy change, gate stale,
        approval expiry, and target change against publication and prove none
        silently authorize. Evidence: TOCTOU tests.
        <!-- `RaceObservation` + `eligibilityRaceFaults`, and
        `tools/eligibility-toctou.mjs` runs all six against a real database:
        CONTROL 1 asserts the NAIVE path publishes over every one of them before
        anything else is measured, since a refusal of a race that never happened
        is not evidence. The tool reports the OBSERVED order rather than the
        intended one, because a row conflict turns an in-window change into an
        after-the-write one and that is the mechanism — reporting the intention
        would hide it. `change-before-the-recomputation-that-published` is the
        quieter fault and in one way the worse: a change that landed in the
        window is a timing problem, and one that landed BEFORE the recomputation
        and published anyway means that input is not in the decision at all.
        `refusal-that-does-not-name-the-change` is why a stopped publish is not
        enough — the operator presses retry, the second attempt succeeds, and
        nobody learns a consent was revoked. `event-never-raced` is S6.5's
        `axis-never-perturbed` here, and the tool proves it fires by dropping
        consent-revoke from its own matrix. A change landing AFTER the write is
        deliberately not a fault: the publish happened, the candidate is locked
        by S6.10.d, and refusing there would refuse a legitimate later edit. -->
      <!-- S6.11 rollup: 4210 kit tests pass (54 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 89 entry points with no drift and no violations.
      `tools/eligibility-toctou.mjs` passes every control against PostgreSQL
      16.14, and was verified by patching the BUILT dist twice — emptying
      `SNAPSHOT_ISOLATIONS` fails CONTROL 4 and disabling the
      no-conflict-no-predicate branch fails CONTROL 2. -->
- [x] S6.12 Add verified-in-target state based on read-back, not a successful
      write response. Evidence: corrupted or missing target content fails.
      <!-- `src/target-verification.ts` + `tools/target-readback.mjs` (new Nx
      target, runs a REAL S3-compatible object store beside a REAL http target
      that misbehaves on purpose). A 2xx is produced before the write is durable,
      before it is visible, before it is indexed, and by the component whose
      correctness is the question. Six fault families, 43 faults, all producible;
      45 tests. Composes S6.9.e's read-back rather than restating it: that owned
      the saga's verify step, this owns the contract, the binding strength, the
      seven states and the journeys. -->
  - [x] S6.12.a Define target-specific read-back endpoint/operation,
        authorization, eventual-consistency delay, retry budget, and stable
        external identity. Evidence: verification contract.
        <!-- `TargetVerificationContract` + `verificationContractFaults`.
        `retry-budget-shorter-than-the-consistency-delay` is the one that
        survives review, because every field is plausible on its own: ninety
        seconds of declared eventual consistency, three attempts over thirty, and
        the two numbers in different files. Every publish to that target then
        reports pending, the state is dismissed as noise, and the one that never
        landed looks like all the others — CONTROL 4 measures a real 404 for
        400ms and a real 200 afterwards, so the arithmetic is against a clock
        rather than a comment. `read-back-through-a-callback-that-cannot-be-
        repeated` is decision 1: a callback arrives once, and a verification that
        cannot be re-run is the one thing verification is not.
        `identity-constructed-rather-than-returned` is decision 5, and CONTROL 5
        makes it concrete by serving a complete, internally valid object at a
        path built from our own inputs. `list-and-filter` is deliberately NOT a
        fault, with a counterexample test — it is the only read some targets
        have. -->
  - [x] S6.12.b Read back target existence/state and exact bytes/hash or
        strongest supported binding. Evidence: content integrity tests.
        <!-- `ContentReadback` + `contentIntegrityFaults`, and the tool's CONTROL
        0 is the argument. Truncate an object in place and MinIO recomputes its
        entity tag over what it now holds, so "ask the target for its checksum
        and compare it to the target's content" reports SUCCESS over a
        half-written release — asserted BEFORE the real check, both before and
        after the corruption, so the control cannot go vacuous. That is
        `digest-taken-from-the-target-rather-than-from-its-bytes`, S6.6's
        photocopy in its most convincing disguise, and
        `byte-length-that-does-not-match` is kept separate because a short write
        is the corruption real targets produce and the one a self-consistent
        checksum cannot see. Decision 3 is the binding LADDER: exact bytes, an
        entity tag and "something exists here" are three different claims, and if
        all three write `verified` the column means the weakest of them
        everywhere it is read. Both directions are faults — a claim that degraded
        silently and one somebody inflated. -->
  - [x] S6.12.c Verify metadata, version, audience/permissions,
        locale/territory, rights/embargo, provenance, relations, and
        discoverability. Evidence: target field matrix.
        <!-- `TARGET_FIELDS` + `fieldMatrixFaults` over the item's own eight.
        `rights-or-embargo-never-verified` and `discoverability-never-verified`
        are split out of the generic missing-field fault because their failures
        are not defects: an embargo that did not take at the target is a release
        published early — a legal event against a target answering 200 to
        everything — and an object with the right bytes and the right permissions
        that nobody can find is the partial success that surfaces weeks later as
        an absence. CONTROL 6 produces exactly that against a real listing
        request. `field-compared-against-what-we-sent` is the quiet one: the
        observed value came from the plan or the publish response, so the
        comparison is a value against itself and every field passes on a target
        that stored nothing. -->
  - [x] S6.12.d Distinguish pending visibility, verified, mismatched, missing,
        unauthorized-to-verify, target unavailable, and verification failed.
        Evidence: state union tests.
        <!-- `VERIFICATION_STATES` + `verificationStateFaults` over a shipped
        `STATES_FROM_READ` table, so the state has to FOLLOW FROM what the read
        attempt did rather than merely be a member of the union.
        `verified-without-a-completed-read` is decision 4 and the reason the
        seven exist: CONTROL 3 accepts three real writes and refuses three real
        reads with 403, which is an ordinary target ACL, and the failure is
        STABLE — a verifier that swallows it verifies every release to that
        target forever with nothing ever read.
        `refusal-to-read-recorded-as-a-target-outage` keeps a 403 apart from a
        503: one is permanent and about us, the other transient and about them,
        and merging them retries a permission problem forever while reporting
        somebody else's incident. `unresolved-state-with-nobody-to-own-it` is
        where an exhausted retry budget has to land, with a counterexample test
        showing that naming an owner resolves it. -->
  - [x] S6.12.e Prevent publisher 2xx/callback/queued acknowledgment from
        setting verified state. Evidence: false-success tests.
        <!-- `VERIFIED_EVIDENCE` + `falseSuccessFaults`: six things get used as
        proof and one of them is proof. `verified-set-by-elapsed-time` is the
        worst because it ALWAYS SUCCEEDS — "we waited five minutes and nobody
        complained" is how a target down for a week reports every release as
        verified, and it degrades silently, since the longer the outage the more
        confident the record. `verified-written-outside-the-state-machine` is how
        all the others survive being forbidden: the transition refuses them, the
        column is still there, and a migration or a support script sets it
        directly. An operator assertion is a fault too rather than an exemption —
        S5.12's rule that an override records a decision beside the result and
        never replaces it. -->
  - [x] S6.12.f Trigger compensation/incident/manual reconciliation on
        corrupted, partial, wrong-target, or unknown read-back and audit every
        attempt. Evidence: failure journey tests.
        <!-- `VerificationJourney` + `verificationJourneyFaults` over a shipped
        `REQUIRED_ACTIONS` table, with a test asserting the table itself produces
        zero findings. Two rows are the interesting ones.
        `wrong-target-compensated-automatically`: compensation means writing to
        the target again, usually a delete, and the whole finding is that we do
        not know whose object is there — undoing a mistake at an address that is
        not ours is a second, larger mistake made faster. And an `unknown`
        read-back is reconciled and never retried, which is S6.9.g arriving here.
        `attempts-not-all-audited` is decision 6: a trail holding only the
        successful verifications describes a system that verifies when things
        work, and the reads that returned 404, 403 and a timeout are the only
        evidence the verification could ever fail — exactly the ones a log-on-
        success path drops. -->
      <!-- S6.12 rollup: 4256 kit tests pass (45 new plus one for the binding
      order); `tsc` clean on lib and spec; `nx build` clean; production-scan,
      route-contract-gate, consumer-contract-gate, api-inventory-gate,
      router-vocabulary-scan and packed-consumer-smoke all pass over the rebuilt
      dist; the export map regenerates to 90 entry points with no drift and no
      violations. `tools/target-readback.mjs` passes every control against MinIO
      and a real http target, and was verified by patching the BUILT dist twice —
      disabling the target-computed-digest branch fails CONTROL 0 and disabling
      the verified-without-a-read branch fails CONTROL 3. -->

- [x] S6.13 Add correction, supersession, unpublish, recall, and tombstone
      semantics. Evidence: downstream state and audit remain coherent.
      <!-- `src/withdrawal.ts` + `tools/withdrawal-fanout.mjs` (new Nx target,
      four REAL http targets over real sockets and a REAL PostgreSQL). The five
      are not severity levels of one action — they answer what was wrong, what to
      use instead, what people may still see, what has to stop right now, and
      what is left where the thing was. A single withdraw button collapses them
      the same way every time, into unpublish, because unpublish is the one with
      no follow-up work. Seven fault families, 55 faults, all producible; 50
      tests. -->
  - [x] S6.13.a Define correction as a linked new version preserving original,
        correction reason/scope, changed facts/artifacts, notice, and target
        update. Evidence: correction lifecycle tests.
        <!-- `Correction` + `correctionFaults`. Decision 1:
        `correction-that-edited-the-original-in-place` is the whole item, because
        the correction exists precisely BECAUSE somebody already acted on the
        wrong version, and editing in place leaves them nothing to compare
        against — the notice then announces a difference that cannot be observed.
        CONTROL 6 makes that literal against a real database: after the UPDATE,
        0 rows hold the value somebody acted on and 1 holds the corrected one,
        while an append-only version table holds both.
        `notice-that-is-not-where-the-original-was` is the version everybody
        ships — a notice on the new version reaches people who found the new
        version, and everybody following the old link sees the wrong content with
        no sign that it is wrong, which is the population the notice is for. -->
  - [x] S6.13.b Define supersession as a new release identity replacing prior
        use while retaining navigable lineage and compatibility. Evidence:
        supersession tests.
        <!-- `Supersession` + `supersessionFaults`, reusing S6.10's
        `SUPERSESSION_REASONS` rather than inventing a second vocabulary.
        Decision 2 is that supersession moves USE, not existence, and the two
        faults are opposite mistakes about the same reference.
        `superseded-release-no-longer-resolvable` breaks every citation made
        while it was current; `supersession-that-redirects-silently` is worse,
        because the citation still resolves — to different content, under the
        same name — so anybody checking a quotation finds it absent from the
        thing it is attributed to and nothing anywhere reports an error. That is
        S6.10's derived-identity failure arriving at the other end of the life
        cycle. `compatibility-not-stated` treats `unknown` as a real value with a
        counterexample test, because a supersession silent about compatibility is
        read as drop-in by every consumer that automates the upgrade. -->
  - [x] S6.13.c Define unpublish as removal from active/public presentation with
        target-specific reversibility, cache/search/catalog cleanup, and no
        history erasure. Evidence: unpublish tests.
        <!-- `Unpublish` + `unpublishFaults` over six presentation surfaces.
        `search-index-not-cleared` and `cache-not-invalidated` are split out of
        the generic surface fault for OPPOSITE reasons: the index is a SEPARATE
        SYSTEM, so removing the object does nothing to it and the withdrawn
        release stays the first result for its own title; the cache is the SAME
        SYSTEM WITH A DELAY, so it keeps serving for as long as its lifetime says
        with nothing anywhere in an error state. CONTROL 2 demonstrates the
        second against a real PostgreSQL materialized view — the table returns 0
        and the view returns 1 for the same deleted row, until an explicit
        refresh. Decision 3 is `reversibility-claimed-over-an-irreversible-
        target`: whether an unpublish can be undone belongs to the target, and a
        console offering restore over a store that burns the identifier is
        promising what nobody can deliver. -->
  - [x] S6.13.d Define recall as urgent multi-target withdrawal with severity,
        authority, notifications, stop-distribution, affected derivative
        tracking, and incident linkage. Evidence: recall rehearsal.
        <!-- `Recall` + `recallFaults`. Decision 4:
        `reconciliation-before-distribution-stopped` is the fault that READS AS
        DILIGENCE — work out exactly which targets hold it, produce a list, then
        act — and every minute of that inventory is distribution of the thing
        being recalled, while the inventory is the part that can be done
        afterwards. `recall-with-no-authority` is separated from
        `recall-authority-not-verified` because an unauthenticated urgent
        withdrawal is a denial of service with a polite name: the action is
        designed to be fast, to bypass review and to reach every target, which is
        exactly what somebody malicious wants from it. `derivative-not-tracked`
        decides whether a recall works at all, and CONTROL 4 shows why against a
        real database: a one-level `WHERE parent = $1` finds one derivative and a
        recursive CTE finds three, the two it missed being a mirror of an excerpt
        and a set built from the mirror. -->
  - [x] S6.13.e Define tombstone content, stable identity, reason/category,
        visibility, rights/privacy-safe detail, replacement link, and retention.
        Evidence: tombstone tests.
        <!-- `ReleaseTombstone` + `tombstoneFaults`. Decision 5 is that a
        tombstone is CONTENT and what it does not say is part of it.
        `address-that-answers-not-found` is the default everybody gets for free,
        and CONTROL 3 proves the problem rather than describing it: the withdrawn
        address and one that never existed returned BYTE-IDENTICAL responses
        (404, 21 bytes each), so a deliberate withdrawal renders as a broken link
        that people retry and file bugs about. `tombstone-that-names-a-person` is
        the opposite failure and the more serious one — a removal made on
        somebody's request, explained in public at the address everybody already
        has, performs exactly the exposure the removal was for, so the category is
        what has to be public and the particulars are what must not be.
        `tombstone-with-no-retention` is the quiet one: a cleanup turns the
        address back into a plain not-found and the withdrawal becomes invisible
        again. The replacement requirement is narrowed to the two categories that
        imply one exists, with a counterexample test for `expired`. -->
  - [x] S6.13.f Propagate each action to targets, catalog/search/cache,
        provenance/ledger, dependencies, analytics, notifications, and future
        eligibility. Evidence: downstream integration tests.
        <!-- `Propagation` + `propagationFaults` over the item's own nine
        channels. `future-eligibility-not-updated` is the one that UNDOES THE
        WHOLE ACTION, and CONTROL 5 produces it against a real database: with a
        recall row present, the eligibility query STILL returned the release as
        publishable, because nothing joined the recall table — so the next
        release train re-publishes recalled content while working exactly as
        designed, and no step in it is wrong. Adding the join returns 0.
        `provenance-ledger-not-updated` is the quiet one: the world says
        withdrawn and the ledger says published, and the ledger is what an audit a
        year from now reads. -->
  - [x] S6.13.g Preserve immutable audit and distinguish partial/failed/unknown
        target outcomes with retry/compensation. Evidence: multi-target fault
        tests.
        <!-- `WithdrawalRecord` + `withdrawalAuditFaults`, with `rollUpOf` as a
        SEPARATE exported function so one piece of code computes what the roll-up
        should be and another compares it to what was recorded — a single
        function doing both can only agree with itself (S6.6's photocopy again).
        Decision 7, and CONTROL 1 is the argument: four real targets, two
        accepted the withdrawal, and only ONE actually stopped serving. The
        target that answered 200 and kept serving is the outcome no response code
        can distinguish, which is why the record is per target and the roll-up is
        derived. `partial-rolled-up-as-complete` and
        `unknown-rolled-up-as-complete` are split because they are wrong
        differently: a partial withdrawal is KNOWN to be still serving somewhere
        and an unknown one MIGHT be, and the second is the one people defend —
        nothing came back saying it failed. `audit-that-can-be-rewritten` is
        here because who ordered it, when distribution stopped and which targets
        confirmed is the evidence a dispute turns on. -->
      <!-- S6.13 rollup: 4307 kit tests pass (50 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 91 entry points with no drift and no violations.
      `tools/withdrawal-fanout.mjs` passes every control against four real http
      targets and PostgreSQL 16.14, and was verified by patching the BUILT dist
      twice — removing `rollUpOf`'s partial branch fails CONTROL 1 and disabling
      the answers-not-found branch fails CONTROL 3. -->
- [x] S6.14 Add bundle importer compatibility and migration hooks. Evidence:
      supported old versions round-trip; unsupported versions fail with a
      migration path.
      <!-- `src/importer.ts` + `tools/importer-roundtrip.mjs` (new Nx target:
      REAL tar archives, REAL ajv-compiled schemas at two versions, a REAL P-256
      key, REAL gzip). A version we do not know must be REFUSED, not read
      partially — an importer is the one place where content arrives that we did
      not produce, at a version we did not choose, from a writer we cannot ask.
      Seven fault families, 49 faults, all producible; 54 tests. Composes S2.5's
      importer port (plan/validate/execute/reconcile) rather than restating it. -->
  - [x] S6.14.a Detect archive/profile/manifest/contract versions and validate
        path/resource/security limits before parsing content. Evidence: importer
        preflight tests.
        <!-- `ImportPreflight` + `preflightFaults` over ten checks. Decision 1:
        parsing IS the attack surface, so `check-performed-after-parsing` is the
        one that reads as fine in review — the check is right there in the code
        and it does refuse, after the parser has allocated for the sizes the
        archive declared and followed the paths it named.
        `compression-ratio-not-checked` is split out because it is the limit on
        nothing you can see, and CONTROL 3 measures it rather than asserting it:
        a real gzip compressed 8MB to 8.0KB, a ratio of 1026:1, so every bound on
        the archive passes and the expansion is bounded by nothing.
        `observed-value-not-recorded` is the quiet one — a check that passed and
        recorded nothing cannot be tuned, and a bound nothing approaches reads
        exactly like one nothing measures. `limit-not-declared` is narrowed to
        the five numeric checks, with a counterexample test for a version check
        that legitimately has no bound. -->
  - [x] S6.14.b Negotiate exact supported, backward-compatible, migratable,
        too-old, too-new, unknown, and unsupported versions without silent
        downgrade. Evidence: version matrix.
        <!-- `SupportWindow` + an exported `versionVerdict` that the checker
        COMPARES AGAINST rather than derives from — two functions, so a recorded
        decision is judged by something that did not make it. The order is
        argued: unknown first (a version we have never heard of is not too old or
        too new, it is off the axis), then withdrawn, and `migratable` applies
        only BELOW the floor, because inside the window we read it directly and a
        migration we could apply but do not need answers nothing.
        `too-new-read-anyway` is decision 2 and CONTROL 2 produces it: a v5 reader
        over a real v6 manifest returns a complete-looking object with no error,
        and the field the writer marked as one a reader may not skip was never
        asked for. `fields-ignored-with-no-record` is what makes a legitimate
        partial read reviewable at all. -->
  - [x] S6.14.c Define pure ordered migration hooks with from/to version,
        preconditions, transformed fields/files/refs, warnings/loss report, and
        provenance. Evidence: migration interface.
        <!-- `MigrationChain` + `migrationHookFaults`, and `observedLosses` as a
        REAL exported key-path diff. Decision 3: the loss report is COMPUTED, not
        declared, because a migration asked what it dropped answers from the same
        assumptions that made it drop it — S6.6's photocopy in the one place where
        the thing being audited is a function somebody wrote in a hurry. The diff
        compares PRESENCE, not equality: a rewritten value is the migration doing
        its job and a removed one needs somebody to have agreed, and it reports a
        key removed outright, a subtree that became a scalar, and a value nulled
        where it held content. CONTROL 4 runs a real 2→4 migration that drops
        `legacyProfile` and declares nothing; the diff finds it.
        `hook-that-is-not-pure` matters more than it looks: a migration reading a
        clock produces a different bundle every run, so S6.5's determinism is
        undone by the one step between the archive and the import. -->
  - [x] S6.14.d Preserve unknown compatible extension data and reject unknown
        required semantics. Evidence: forward compatibility tests.
        <!-- `ExtensionField` + `forwardCompatibilityFaults`. Decision 4 is that
        this is TWO RULES, and a reader with one always has exactly one of the
        two failures. Drop everything unknown and a round trip deletes an
        extension somebody else depends on, surfacing months later as a
        difference nobody can account for — CONTROL 6 does exactly that and shows
        the preserving reader returning the field byte-for-byte. Accept
        everything unknown and a field whose whole purpose is to change how the
        document is interpreted is ignored by the one component that had to
        honour it. `field-with-no-must-understand-declaration` is what makes two
        rules possible at all: without the flag the reader must pick one policy
        for every unknown field, and whichever it picks is wrong for half of
        them. `unknown-optional-field-rejected` is the over-strict direction,
        because a reader that refuses every addition means nobody can extend the
        format. -->
  - [x] S6.14.e Validate and rehash after each migration, produce new bundle/
        manifest identity, and link source/migration lineage. Evidence:
        migration integrity tests.
        <!-- `MigratedBundle` + `migrationIntegrityFaults`.
        `signature-carried-over-a-migration` is the one worth stating loudest,
        and CONTROL 5 settles it with real cryptography: a real P-256 signature
        VERIFIED over the v2 archive and FAILED over the migrated one, so
        reattaching it is not a policy choice — it files a real signature by a
        real key against bytes the signer never saw, and it survives review
        because everything about it still looks valid.
        `migrated-bundle-not-revalidated-against-the-target-schema` is the quiet
        one, and CONTROL 1 shows why with two real ajv-compiled schemas: the v2
        manifest FAILS the v4 schema and the migrated one passes it, so
        validating the output against the source schema tests the wrong contract
        — the fields the migration existed for are exactly the ones the source
        schema does not know about. -->
  - [x] S6.14.f Import as plan/preview then idempotent authorized execution with
        conflict, partial failure, rollback/cleanup, and audit. Evidence:
        importer journey tests.
        <!-- `BundleImportJourney` + `bundleImportFaults`, composing S2.5's
        `ImportPlan`/`ImportValidation`/`ImportExecution` port rather than
        inventing a second vocabulary — this records the facts about a particular
        run that the port cannot express. `partial-import-reported-as-a-failure`
        is decision 6 and the reason `partially-imported` exists as an outcome:
        a run that created half its refs and reports "failed" tells the caller to
        retry, and the retry either creates the other half twice or refuses every
        ref because it is already there, with neither recoverable without
        somebody reading the store. `execution-against-a-plan-that-moved` is
        S6.9's rule arriving here — the digest approval was bound to is the digest
        execute has to see. `unresolved-conflict-executed` is narrowed with a
        counterexample: refusing on an unresolved conflict is the correct
        outcome, not a finding. -->
  - [x] S6.14.g Return exact supported target/migration path or upgrade
        requirement for unsupported input. Evidence: failure response tests.
        <!-- `ImportRefusal` + `refusalResponseFaults`. Decision 7: the caller is
        on the far side of a version boundary — they cannot inspect this reader,
        and the only three actions that exist are produce a different version,
        apply a migration, or upgrade the reader.
        `migratable-refused-without-offering-the-migration` is the expensive one
        because the answer was ALREADY COMPUTED by the verdict, and the refusal
        that omits it sends somebody to rebuild a bundle that did not need
        rebuilding. `too-new-refusal-with-no-required-reader-version` exists
        because the caller cannot make their bundle older, so the reader version
        is the only actionable fact that case has.
        `migration-path-that-does-not-end-at-the-supported-target` catches the
        refusal that is internally inconsistent — follow it and land somewhere
        still refused. And `refusal-for-a-version-that-is-supported` is the
        counterexample rule with a test. -->
      <!-- S6.14 rollup: 4362 kit tests pass (54 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 92 entry points with no drift and no violations.
      `tools/importer-roundtrip.mjs` passes every control against real tar, real
      ajv, real gzip and a real P-256 key, and was verified by patching the BUILT
      dist twice — silencing `observedLosses` fails CONTROL 4 and disabling the
      too-new branch of `versionVerdict` fails CONTROL 2. -->
- [x] S6.15 Add golden round-trip tests for every domain bundle profile.
      Evidence: build, archive, import, verify, and rebuild are deterministic.
      <!-- `src/golden-round-trip.ts` + `tools/golden-round-trip.mjs` (new Nx
      target, builds and archives every SHIPPED profile with real GNU tar). This
      is the last item in S6 and the one that keeps the rest true — and the
      easiest in the phase to write so that it can never fail, which is why every
      control starts from the exported `WORKED_BUNDLE_PROFILES` registry rather
      than a list in the tool. Seven fault families, 40 faults, all producible;
      41 tests. **S6 IS NOW CLOSED.** -->
  - [x] S6.15.a Register minimal, full, boundary, localized, provenance-rich,
        legacy-supported, and invalid fixtures per domain profile. Evidence:
        profile fixture manifest.
        <!-- `ProfileFixture` + `fixtureRegistryFaults` over the item's own seven
        classes, checked per registered profile.
        `profile-with-no-refused-fixture` is stated separately from the missing-
        class fault because it changes what the suite PROVES: everything else
        measures whether the pipeline still accepts what it accepted yesterday,
        and only a fixture that must be refused measures whether it still
        refuses — a corpus without one goes green against a build that validates
        nothing. `legacy-fixture-that-declares-the-current-version` is the quiet
        one: a legacy fixture whose content says it is current exercises no
        migration, so it is a second copy of the full fixture under another name
        while the corpus reports seven classes. -->
  - [x] S6.15.b Build logical bundle from authoritative inputs and compare
        manifest/layout/component inventory/provenance to goldens. Evidence:
        build golden tests.
        <!-- `BuildGolden` + `buildGoldenFaults`. Decision 1 is the whole item: a
        golden is a RECORDED answer, and the three ways of losing that are named
        separately because they arrive by different routes.
        `regenerated-at-test-time` is the comparison agreeing with itself;
        `captured-on-failure` is the same thing one step removed, where the first
        red build wrote the expectation so whatever happened that day is now the
        definition of correct; and `rewrites-on-mismatch` is the mechanism that
        turns a checked-in literal into either of the other two the first time it
        goes red. `aspect-not-pinned-by-the-golden` covers the four the item names
        — an unpinned aspect can change in any way at all and the golden still
        passes. -->
  - [x] S6.15.c Archive twice under perturbed order/clock/locale/platform inputs
        and compare exact bytes/hash/entry metadata. Evidence: archive
        reproducibility tests.
        <!-- `ArchiveRun` + `archiveReproducibilityFaults`, iterating S6.5's
        `PERTURBATION_AXES` IMPORTED rather than restated: a second list of the
        things that vary between two machines would drift, and the one that
        drifted would be the one nobody was reading. The tool varies SIX of the
        seven for real — shuffled input order, touched mtimes, a Turkish locale, a
        Kiritimati time zone, a 0077 umask and interleaved writes — and asserts
        FIRST that `localeCompare` under Turkish disagrees with byte order, so the
        sort being byte order is load-bearing rather than incidental. The seventh,
        `platform`, cannot be varied on one box, and the tool asserts the checker
        REPORTS it as never perturbed rather than narrowing the axis list to hide
        it. `entry-metadata-never-compared` closes the other direction: two
        archives hash alike when a normaliser flattens modes, uids and mtimes, so
        the tool reads the tar headers and compares what they were flattened TO.
        `two-baselines-that-disagree` fires before any axis is judged, because a
        build that is not deterministic against itself makes every axis noise. -->
  - [x] S6.15.d Import and migrate where needed, then compare semantic objects,
        artifact bytes/hashes, refs, rights/consent, gates, and provenance.
        Evidence: import parity tests.
        <!-- `ParityComparison` + `importParityFaults` over the item's own eight
        dimensions. `comparison-with-one-side-derived-from-the-other` is the
        photocopy where it is hardest to see: comparing the imported object to the
        object the importer just produced from the same parse tests that a
        function is deterministic, not that the bundle survived, so both sides
        have to be read. `rights-or-consent-never-compared` is split out because
        those two are the dimensions whose loss is not a defect — a bundle that
        arrives with its rights position flattened is content being used on terms
        nobody agreed to, and it imports perfectly.
        `legacy-fixture-imported-without-a-migration` closes the loop with
        S6.15.a: a legacy fixture that took the current path exercised nothing. -->
  - [x] S6.15.e Verify schema/path/hash/signature/trust/profile/target semantics
        and expected invalid fixtures. Evidence: verification matrix.
        <!-- `VerificationRow` + `verificationMatrixFaults` over seven aspects in
        BOTH directions. `expected-refusal-that-was-accepted` is what the matrix
        exists for: everything else says the pipeline still handles what it
        handled, and only an invalid fixture that got through says a check
        stopped running — which produces no error anywhere else, ever.
        `aspect-that-did-not-run` is kept apart from a mismatch for the S6.7
        reason: an unreachable trust store and a valid chain both leave the
        signature aspect with nothing to report, and only one of them is fine.
        CONTROL 5 in the tool produces the refusal against real profiles, with
        the naive "did we write every file we meant to" check asserted to accept
        the same input first. -->
  - [x] S6.15.f Rebuild from imported state and require byte identity when the
        profile/version promises it or documented semantic identity otherwise.
        Evidence: rebuild comparison.
        <!-- `RebuildComparison` + `rebuildFaults`. Decision 4: neither answer
        works everywhere, because a profile whose archive embeds a signature
        cannot promise byte identity after a re-sign, and one promising only
        semantic identity with nothing written down has promised nothing a test
        can check. So the promise is per profile and
        `profile-that-promises-no-identity-at-all` is the finding — a rebuild
        comparison against no promise passes whatever comes out. CONTROL 6 does
        the real version: each archive is extracted and rebuilt FROM THE
        EXTRACTED BYTES, and the two are byte-identical for both shipped
        profiles. -->
  - [x] S6.15.g Fail when a registered domain profile lacks current golden
        coverage. Evidence: registry coverage gate.
        <!-- `goldenCoverageFaults(profiles, coverage)` — and the argument order
        is the entire item. It walks the SHIPPED registry and asks what coverage
        exists for each, rather than walking the coverage and reporting what it
        found; a report built the other way round finds every golden covered by
        definition, and a profile added tomorrow with no fixtures is precisely
        what it cannot see. CONTROL 1 proves that against the live registry by
        dropping one profile's coverage and requiring the finding to NAME
        `bundle.dataset-release`. `coverage-for-a-profile-that-is-not-registered`
        is the other direction and is not symmetric noise: goldens for a profile
        nobody ships spend the suite on something that cannot break, and they are
        why a coverage percentage looks healthy while a live profile has nothing.
        This is the mechanism by which S6.15 stays true after it is marked. -->
      <!-- S6.15 rollup: 4404 kit tests pass (41 new); `tsc` clean on lib and
      spec; `nx build` clean; production-scan, route-contract-gate,
      consumer-contract-gate, api-inventory-gate, router-vocabulary-scan and
      packed-consumer-smoke all pass over the rebuilt dist; the export map
      regenerates to 93 entry points with no drift and no violations.
      `tools/golden-round-trip.mjs` passes every control over both shipped
      profiles, and was verified by patching the BUILT dist twice — silencing
      `registered-profile-with-no-coverage` fails CONTROL 1 and silencing
      `axis-never-perturbed` fails CONTROL 3. **§S6 is closed with this item.** -->

### S7 — Shared application shell and working surfaces

- [x] S7.1 Build a restrained workbench shell with navigation, primary canvas,
      inspector, status region, and command palette. Evidence: responsive
      browser snapshots.
      <!-- Six regions and exactly one `main`. The kit owns everything that is
      not a matter of taste — `libs/oshun/workbench-kit/src/shell.ts`, the 94th
      entry point — and `apps/oshun/web/src/components/workbench/` renders it.
      That split is the point: the shell would otherwise be re-implemented once
      per workbench, and the parts that must not vary (which routes an actor may
      see, where focus goes when a panel closes, what a status slot is allowed to
      say, which arrangement a width gets) are computed rather than described.
      The parent's "responsive browser snapshots" are met by a real Chromium
      driving the shipped components at 640/1100/1600px, not by an image: a
      screenshot proves a machine once and `pnpm nx workbench-shell-browser
      @oshun/web` proves it every time. All seven children complete. -->
      <!-- CORRECTION to the naive reading of this item: jsdom cannot settle any
      of the three claims a shell actually makes. It has no layout engine, so
      "the arrangement fits" is unmeasurable; no Tab traversal, so "the keyboard
      reaches the work" is unmeasurable; and no computed style, so "the surface
      stays inside its budget" is unmeasurable. A shell verified only in jsdom is
      verified against a simulator of the only thing it exists to do — which is
      why the browser tool exists alongside the 33 component tests rather than
      instead of them. -->

  - [x] S7.1.a Build landmark-based global/workbench navigation from authorized
        route descriptors with current location, breadcrumbs, collapse, and
        keyboard traversal. Evidence: navigation tests.
        <!-- evidence: `libs/oshun/workbench-kit/src/shell.ts` §S7.1.a +
        `apps/oshun/web/src/components/workbench/WorkbenchNavigation.tsx`.
        `authorizedNavigation` COMPOSES S1.8's `hostNavigation` (host, audience,
        discoverability, lifecycle) and adds the two filters a menu cannot skip:
        the roles the route declares, and its flags. Flag handling finally reads
        `whenUnavailable`, which S1.8 made every route declare and nothing has
        ever consumed — and it separates "the flag service answered and knows
        nothing about this flag" (take `defaultEnabled`) from "the flag service
        did not answer" (take hide / disable-with-a-reason / fail-closed /
        fail-open). Collapsing those two makes every flag-service outage either
        a silent feature launch or a silent feature removal. `breadcrumbTrail`
        is the first reader of S1.8's `breadcrumb` mode, and it walks the
        AUTHORIZED tree so a trail cannot name an ancestor the actor may not
        open — a breadcrumb is a disclosure surface, and "Clients / Acme / Brief"
        tells you Acme exists whether or not the link works.
        `navigationTraversal` owns the single roving tab stop, and
        `navigationTraversalFaults` reports the collapsed subtree that stayed
        tabbable and the current location hidden behind a collapsed ancestor.
        `navigationFaults` carries S1.8.g's honesty check up a level: a route in
        neither the tree nor the exclusion list has been silently dropped.
        Proof: 135 kit tests (`shell.spec.ts`, every route fixture built through
        `parseRouteDescriptor`) + 33 jsdom tests + real Chromium CONTROL 2, which
        walks the document with real Tab presses and asserts the navigation
        contributes exactly ONE stop for four routes; FAULT 2 makes every item
        tabbable and the same walk reports 4. -->
  - [x] S7.1.b Build the dominant primary workspace region with domain plugin
        composition, loading boundary, selection context, and no decorative
        wrapper cards. Evidence: two-archetype fixtures.
        <!-- evidence: `workspaceFaults` in shell.ts + the editor-centric and
        operations-centric fixtures in
        `apps/oshun/web/src/components/workbench/__fixtures__/workbenchShellFixture.tsx`,
        read by BOTH the jsdom tests and the browser proof — one fixture, because
        a browser check with its own host measures a page nothing else renders.
        Faults: two surfaces claiming the primary region (two dominant canvases
        is two half-sized ones), a plugin surface composed by no declared
        extension point, a loading placeholder that shows values which look
        final, a placeholder that does not hold the layout, a loading state that
        is visual only, a cancel control for work that keeps running, a selection
        the inspector does not read, a decorative wrapper card, and a workspace
        below the 50% width share the shell thesis requires. Real-browser CONTROL
        3 measures that share from `getBoundingClientRect` at 640/1100/1600px
        rather than from the stylesheet. -->
  - [x] S7.1.c Build contextual inspector placement, pin/replace/close,
        resizable width, overflow, and focus restoration. Evidence: inspector
        interaction tests.
        <!-- evidence: `openInspector`/`pinInspector`/`closeInspector` in
        shell.ts + `WorkbenchInspector.tsx`. Replace-unless-pinned, and
        re-opening a subject keeps its ORIGINAL opener — the second opener is
        where the actor is now, the first is where they were when the entry
        appeared, and restoring to the newer one sends them somewhere they never
        left. THE FOCUS LADDER IS THE ITEM: opener, then the entry that took its
        place, then the region — never the document body, which is the default
        every framework gives you. `closeInspector` takes `liveElementIds`
        because `element.focus()` on a detached node moves focus to the body and
        reports nothing; the component checks `document.getElementById` before
        it hands the id over. Keyboard resizing is not an alternative to the
        pointer one, it is the only one a keyboard user has. Proof: jsdom
        restores focus to the opening row and drives the resize to its bound;
        real-browser CONTROL 5 confirms it with a real click, and FAULT 5 removes
        the opener from the document first and confirms focus lands in the
        inspector region rather than on the body. -->
  - [x] S7.1.d Build persistent compact status for save/sync, connectivity,
        capability/fixture, job, gate, review, publish, and stale state.
        Evidence: status matrix.
        <!-- evidence: `STATUS_SLOTS` + `STATUS_VOCABULARY` + `statusFaults` in
        shell.ts + `WorkbenchStatusRegion.tsx`. Eight slots, each with its own
        closed vocabulary, and EVERY vocabulary ends in `unknown` — a slot that
        disappears when it has nothing to report renders identically to one
        saying everything is fine, so `slot-missing-from-the-region` is a fault
        and the region always renders all eight. `state-asserted-with-no-
        observation` refuses the shell asserting a fact nobody measured;
        `unknown-state-carrying-an-observation` refuses the opposite, a
        measurement taken and discarded. `fixture-source-without-a-fixture-state`
        is the §2.2 rule: fixture data may not present itself as live. Every
        state carries a noncolour glyph, and the kit test walks all 44 states in
        all 8 vocabularies. -->
  - [x] S7.1.e Build command palette discovery, typed capability commands,
        disabled explanations, search/groups, keyboard behavior, and recent
        commands without bypassing authorization. Evidence: palette tests.
        <!-- evidence: `paletteProjection` + `paletteFaults` in shell.ts +
        `WorkbenchCommandPalette.tsx`. A command the actor may not run is
        DISABLED WITH A REASON or NOT THERE — the choice between them is a
        disclosure decision (`concealedCommandIds`), and the third option,
        offered-enabled-then-refused-by-the-server, is what
        `available-command-the-actor-may-not-run` exists to stop. Recency is
        applied AFTER authorization: a recent list records what was allowed once,
        and re-offering it is the most ordinary way a revoked grant keeps
        working (`recent-command-offered-without-reauthorization`). Role and
        capability are separate refusals with separate reason keys, so holding
        the role while the capability is not configured still says why. Search
        matches TRANSLATED labels and keywords — matching the command id makes
        every command findable in exactly one language. `RESERVED_CHORDS` refuses
        a binding the browser takes first, and a destructive command with no
        deliberate modifier is a fault. Proof: 15 kit tests + 8 jsdom tests
        including the id-only search returning nothing. -->
  - [x] S7.1.f Compose wide three-region, compact two-region, and single-region
        review arrangements with preserved selection/focus. Evidence: responsive
        browser tests.
        <!-- evidence: `arrangementForViewport` + `ARRANGEMENT_REGIONS` +
        `arrangementFaults` in shell.ts, and `WorkbenchShell.tsx` choosing the
        arrangement from that function rather than from a CSS breakpoint — so
        the layout and the check cannot drift apart, which a second copy of the
        numbers in a media query guarantees they would. Preservation is only
        observable ACROSS a transition, so `arrangementFaults` takes two
        observations: a single snapshot of a compact layout is indistinguishable
        from one that just threw the actor's selection away. A region a narrower
        arrangement drops keeps a disclosure control
        (`dropped-region-with-no-alternate-affordance`). Real-browser CONTROL 3
        renders all three at real viewport widths and FAULT 4 forces the wide
        columns at 600px. THE FINDING FROM THAT FAULT: the shell sets
        `overflow: hidden` so the page can never scroll sideways, which means an
        arrangement that does not fit is CLIPPED, not scrolled — and clipped
        content is unreachable by any means. The document-level check reported a
        clean layout over it, so the tool measures the shell's own
        scrollWidth/clientWidth as well, and asserts the document-level check
        stays silent to prove the shell measurement is the one doing the work. -->
  - [x] S7.1.g Apply restrained tokens: two type families max, one accent,
        semantic status cues, minimal borders/shadows, and cards only where the
        card is the interaction. Evidence: visual conformance snapshots.
        <!-- evidence: `restraintFaults` in shell.ts +
        `WorkbenchShell.module.css`, which IS the budget: two families, one
        accent, nine semantic status colours, one shadow reserved for the palette.
        Written as COUNTS OVER WHAT A BROWSER COMPUTED rather than as adjectives,
        because a restraint rule written as an adjective is a design review
        nobody can fail. Real-browser CONTROL 4 reads computed `font-family`,
        the `--wb-accent` custom property, the computed `color` of each rendered
        status slot and every non-`none` `box-shadow`; FAULT 3 injects a third
        family and a second accent and both are reported. TWO DECISIONS THE REAL
        BROWSER FORCED. (1) An INSET shadow is a line, not an elevation: the 2px
        inset rule marking the current route would otherwise count as a second
        elevation and fail the check on the one thing it exists to allow. (2) A
        CARD MUST DECLARE THE INTERACTION THAT MAKES IT ONE
        (`data-card-interaction`): inferring "selectable" from `aria-current`
        scored every row that was not selected RIGHT NOW as decorative, which is
        the opposite of what it is. Status tokens are read from the RENDERED
        slots, weakest instance winning per tone (S6.12's ladder), and FAULT 6
        strips a slot back to a coloured dot and confirms
        `status-token-signalled-by-colour-alone` fires. -->
        <!-- S7.1 rollup: 4540 kit tests (135 new) + 33 jsdom component tests +
        `apps/oshun/web/tools/workbench-shell-browser.mjs` (5 CONTROLs, 7 FAULTs)
        driving real Chromium 1223 over the SHIPPED components and stylesheet,
        bundled from source by esbuild with the checkers imported from the BUILT
        dist. `tsc` clean on kit lib and spec; `nx build @oshun/workbench-kit`
        clean; production-scan, route-contract-gate, consumer-contract-gate,
        api-inventory-gate, router-vocabulary-scan and packed-consumer-smoke all
        pass over the rebuilt dist (94 subpaths); the export map regenerates to
        94 entry points with no drift and no violations; `eslint` clean on the
        new app components including jsx-a11y. The tool was verified by patching
        the BUILT dist twice — silencing `more-than-one-main-landmark` fails
        FAULT 1, and silencing `status-token-signalled-by-colour-alone` fails
        FAULT 6 — and passes again over the restored build. -->
        <!-- REGISTRATION (five places): src/index.ts, package.json exports,
        tsconfig.base.json alias, the KIT_OWNED_ENTRY_POINTS list in
        scripts/v1-workbenches/generate-workbench-kit-export-map.mjs, and the
        `workbench-shell-browser` target on apps/oshun/web/project.json (the
        tool drives the app, so the target lives with the app and dependsOn the
        kit build). The app also gains `@oshun/workbench-kit` aliases in its
        tsconfig.json and vitest.config.ts. -->
- [x] S7.2 Provide route-level loading, empty, unauthorized, not-configured,
      degraded, offline, and failure states. Evidence: component and Playwright
      state matrix.
      <!-- Seven states are named here and they are usually written as seven
      independent components. THEY ARE NOT INDEPENDENT. A route load asks
      several questions at once — who is this, may they, is the capability
      configured, is the network there, did the query return anything — and the
      answers arrive out of order and disagree. Exactly one thing renders, and
      which one is a decision that in most codebases is made by whichever `if`
      somebody wrote first. `libs/oshun/workbench-kit/src/route-states.ts` (the
      95th entry point) makes it explicit: `resolveRouteState` orders the
      answers, `BLOCKER_PRECEDENCE` writes the order down as data, and
      `RouteStateBoundary` in the app renders what it decides. Two more
      distinctions carry the rest: BLOCKED and ADVISED are different answers
      (degraded is content plus a limitation, not a state instead of it), and
      PENDING IS AN ANSWER TOO AND IT IS NOT "NO" — a pending authorization read
      as an allow is optimistic disclosure, and a spinner over a refusal that is
      already known makes the refusal slower than the success. All eight
      children complete. -->
      <!-- THE FINDING THAT JUSTIFIES WRITING THE ORDER DOWN: written by hand,
      the route precedence put `step-up-required` ABOVE `unauthorized`. That
      reads sensibly — offer the stronger session first — and it disagrees with
      S4's `OUTCOME_PRECEDENCE`, which already decided that a denial beats a
      step-up: sending somebody through an authentication they will be refused
      after both wastes the authentication and hints the object is reachable.
      `routePrecedenceFaults()` now composes S4's list and fails on any
      disagreement, so the two orders cannot drift again, and S4's `unavailable`
      (authorization could not be evaluated) got its own blocker rather than
      sharing a word with a capability that is unavailable — two states with
      different causes, different remedies and different people to chase. -->

  - [x] S7.2.a Provide skeleton/progress loading that preserves layout,
        announces status, supports cancellation where real, and never displays
        fake final values. Evidence: loading tests.
        <!-- evidence: `routeLoadingFaults` in
        `libs/oshun/workbench-kit/src/route-states.ts`, composing S7.1's
        `LoadingBoundary` rather than restating it. THE ITEM-SPECIFIC FAULT IS
        `determinate-progress-that-nothing-measured`: a bar that reaches 90% on
        a timer is a number the code never computed, which is the one thing §2.2
        forbids everywhere else, and the honest control for unmeasured progress
        is an indeterminate one. Also reported: an indeterminate control
        carrying a value nothing will render, a skeleton still showing the route
        being LEFT (the actor reads the old page believing it is the new one),
        an ASSERTIVE loading announcement (loading is not an emergency, and this
        is the setting that makes a workbench unusable), a silent one, and a
        cancel control the request outlives. The shipped boundary renders a
        skeleton with no digits in it, asserted in jsdom. -->
  - [x] S7.2.b Distinguish first-use, no records, no matches, filtered-out,
        retention/deletion, and no permission-visible records. Evidence: empty
        state fixtures.
        <!-- evidence: `supportedEmptyKind` + `emptyStateFaults` + seven
        `EMPTY_KINDS`. THE CLAIM MUST BE SUPPORTED BY THE EVIDENCE THAT PRODUCED
        IT — `emptyStateFaults` compares what the surface rendered to what the
        counts allow, so `no-records` asserted while a filter is active is a
        fault rather than a wording choice. The ORDER inside `supportedEmptyKind`
        is the load-bearing part: concealment is checked BEFORE absence, because
        "there are none" is false the moment some exist that this actor may not
        see, and that particular falsehood is the one a shell tells by accident.
        `concealed-count-disclosed` refuses the other direction: how many records
        are hidden is enough to confirm one exists, so the count never renders —
        proved by a jsdom assertion that the none-visible page contains no digit
        at all, and again in the browser, where FAULT 2 injects the count and
        confirms the check can see it. `first-use-with-no-way-to-create-anything`
        applies to exactly one kind, with a counterexample test that the other
        six are not required to offer an action. -->
  - [x] S7.2.c Provide unauthenticated, unauthorized, forbidden-concealed, step-
        up-required, and expired-session behavior without protected disclosure.
        Evidence: authorization state tests.
        <!-- evidence: `authorizationStateFaults` + the boundary's concealed
        branch. CONCEALMENT IS A RENDERING, NOT A STATUS CODE: the boundary
        renders the REAL not-found component through `renderNotFound`, not a
        second page built from the same strings, so "indistinguishable" is a
        fact about one component instead of a promise about two. The kit takes
        the not-found baseline as an INPUT for the same reason — a comparison
        against a constant written in the checker would pass while the actual
        not-found page said something else. Browser CONTROL 3 compares the two
        ACCESSIBILITY TREES byte for byte (the genuine one is rendered outside
        the boundary by its own scenario), and FAULT 1 changes one heading and
        confirms the comparison sees it. Also: a refusal may not name the object
        it refuses, a step-up with no challenge is a refusal with no way
        forward, an expiry the actor did not choose may not destroy their work,
        and `return-url-carries-protected-values` refuses the return-to that
        travels through the identity provider, the history and the referrer. -->
  - [x] S7.2.d Render server `not_configured` capability, missing category,
        allowed resolver, and safe next step without setup success claims.
        Evidence: not-configured route tests.
        <!-- evidence: `notConfiguredFaults(state, presentation)`. S1.6 already
        made `not_configured` carry `missing[]` and `resolutionAuthority`, and
        NOTHING HAS EVER READ EITHER — this is their first reader, and the check
        is a comparison rather than a restatement: the page and the server state
        are supplied separately, because a page generated from the state agrees
        with it for every input (S6.6's photocopy). Faults in both directions: a
        category the server reported and the page dropped (the fixer fixes the
        ones they were told about and the capability stays down) and one the
        page invented (somebody configures what was already configured).
        `next-step-addressed-to-an-actor-who-may-not-take-it` uses
        `AUTHORITY_ROLES`, where `self-service` requires none — that is the whole
        difference, and the counterexample test proves the same page is fine
        when it tells this actor somebody else will do it. `SECRET_SHAPES` scans
        the rendered detail for a URL with credentials, an internal host, a
        bearer token and a private address, because a configuration error
        message is the most natural place in the system for a connection string
        to reach a screen. -->
  - [x] S7.2.e Render affected/retained capability, limitation, cause,
        freshness, and recovery for degraded state. Evidence: degradation tests.
        <!-- evidence: `degradationFaults(state, presentation, now)`, again a
        comparison rather than a rendering. DEGRADED WITH NOTHING RETAINED IS
        NOT DEGRADED — `usability-disagrees-with-the-state` refuses a degradation
        notice over a configured or unavailable capability outright, composing
        S1.6's three-valued `usability` (the function that already refuses to
        round `degraded` to a boolean) instead of a fourth opinion. The
        retained-half fault is the one people omit: a notice carrying only what
        broke reads as an outage and stops people using what still works, so
        `retained-behaviour-dropped-from-the-notice` is a fault and the shipped
        banner renders BOTH lists. Freshness composes `isStateFresh` rather than
        recomputing an age — two freshness rules eventually disagree and the one
        on screen wins — and `expired-degradation-presented-as-current` catches a
        banner still steering people away from work that now succeeds. -->
  - [x] S7.2.f Render offline versus server/provider unavailable distinctly and
        state readable/editable/queued/prohibited behavior. Evidence:
        connectivity tests.
        <!-- evidence: `connectivityFaults` + the resolver's connectivity rung.
        OFFLINE IS ABOUT US AND UNAVAILABLE IS ABOUT THEM — S6.12's 403/503 split
        one layer up, and it decides whether the actor restarts their router or
        waits for somebody else. Both directions are faults. Offline BLOCKS only
        what cannot be served from what is already held: a surface that can be
        read offline is content plus an `offline-readonly` advisory, because
        hiding readable work behind a network notice is the offline behaviour
        people complain about most. The four abilities are checked against what
        is actually offered: an edit control under `readable`, editing against a
        queue that does not survive a reload, writes held in memory only (a
        silent data-loss event the actor was told would not happen), and a queue
        promised where nothing could ever be queued. Browser CONTROL 5 is a REAL
        disconnection: `context.setOffline(true)` flips `navigator.onLine`, the
        `live-connectivity` scenario reads it rather than a fixture, the offline
        state renders, and reconnecting restores the content with no reload. -->
  - [x] S7.2.g Render validation, dependency, provider, conflict, limit,
        partial, internal, and unknown failure with preserved work, retry
        eligibility, remediation, and correlation. Evidence: failure tests.
        <!-- evidence: `RETRYABLE_FAILURES` + `failureFaults`. RETRY ELIGIBILITY
        IS A PROPERTY OF THE CLASS, decided once, not a button each surface
        chooses: offering retry on a validation failure teaches people that
        retry is decoration, and withholding it on a dependency failure makes
        them redo work that would have succeeded. `conflict` is the interesting
        entry — not retryable, because the state it collided with has moved, so
        the honest affordance is reload-and-reapply. A failure that discards the
        actor's input is a data-loss event wearing an error message.
        `partial-failure-presented-as-total` stops the actor repeating an
        operation and doing the successful part twice, and `partial` is resolved
        as CONTENT plus an advisory rather than as a blocker for the same
        reason. `unknown-failure-claiming-a-cause` refuses a guess that sends
        somebody to investigate a defect that may not exist, and the secret scan
        runs on `internal`/`unknown` only — with a counterexample proving a
        validation message containing an IP-shaped string is not flagged. -->
  - [x] S7.2.h Test direct load, client navigation, refresh, back/forward,
        reconnect, locale/theme/density, keyboard, and screen reader for every
        state. Evidence: Playwright matrix.
        <!-- evidence: `apps/oshun/web/tools/route-states-browser.mjs` — 8
        CONTROLs and 4 FAULTs over real Chromium, 23 scenarios covering all 12
        `ROUTE_BLOCKERS`. SIX OF THE EIGHT JOURNEYS DO NOT EXIST IN JSDOM: no
        history, no reload, no network stack to take away, no
        `prefers-color-scheme`, no Tab, no accessibility tree. THE FINDING THAT
        MADE THIS TOOL WORTH WRITING: `page.goto('...#name')` from a page already
        on that origin is a SAME-DOCUMENT navigation — nothing reloads, React
        never remounts, and focus stays where the last keystroke left it. Half
        the matrix was measuring an in-page state change while calling it a
        direct load; `directLoad()` adds a query so each one is a real document
        load, and the hash stays what the fixture reads. CONTROL 1 also compares
        the page against a SECOND evaluation of the kit in the harness process
        over the same inputs (exposed as `window.routeStateInputs`), which is why
        weakening the built dist is detectable at all. CONTROL 6 sweeps dark
        theme, reduced motion, an RTL locale, 200% text and a 360px viewport over
        every scenario and asserts no state changes and nothing overflows;
        CONTROL 7 walks every state's actions with real Tab presses and asserts
        that a state which REPLACED the content took focus while one that only
        added an advisory did not. -->
        <!-- S7.2 rollup: 4634 kit tests (94 new) + 23 jsdom boundary tests +
        the browser matrix, all green. Verified by patching the BUILT dist
        twice: mapping `conceal` to `unauthorized` fails CONTROL 1's page/kit
        agreement, and silencing the concealment-beats-absence rule in
        `supportedEmptyKind` fails CONTROL 8 — both pass again over the restored
        build. `tsc` clean on kit lib and spec and on the new app components (via
        a temporary `noCheck: false` project, proved non-vacuous by injecting a
        type error); `nx build`, the export map at 95 entry points, and all six
        kit gates clean. Registration: src/index.ts, package.json exports,
        tsconfig.base.json, the export-map generator, and the
        `route-states-browser` target on apps/oshun/web/project.json. -->
        <!-- TRAP HIT: `PRECEDENCE_FAULTS` was invented AFTER the pre-write
        export-name grep and collided with `trust-zones.ts`. The kit typecheck
        passed and `public-api.spec.ts` failed with a rolldown parse error on
        the generated index. Renamed to `ROUTE_PRECEDENCE_FAULTS`; the grep must
        be re-run over the finished file, not the planned one. -->
        <!-- CORRECTION: `resolutionFaults`, `ResolutionFault`, `FAILURE_CLASSES`
        and the over-generic `known`/`pending`/`Unknown` were also renamed for
        the same reason (`artifact-rehash`, `capability-state`). -->
- [x] S7.3 Build a live capability banner driven by server probes. Evidence:
      fixture mode cannot masquerade as live.
      <!-- `libs/oshun/workbench-kit/src/capability-banner.ts` (96th entry
      point, 56 tests) + `apps/oshun/web/src/components/workbench/
      CapabilityBanner.tsx` + `tools/capability-banner-browser.mjs`, which runs
      a REAL `node:http` probe endpoint and lets real Chromium actually fetch
      it — 9 controls, 2 faults, 23 real requests over a socket. The evidence
      clause is a claim about what happens when a REQUEST is made, and there is
      exactly one way to check it: make the request. All six children complete.
      -->
      <!-- THE LOAD-BEARING TYPE IS NOT THE BANNER STATE, IT IS THE ORIGIN OF
      THE CLAIM. Nine named origins a "live" assertion can come from, and
      `LIVE_ASSERTING_ORIGINS` has exactly one entry. Each of the other eight
      records WHY it is not evidence, as data, because a rule with no argument
      attached is one somebody removes the first time it is inconvenient — and
      `origin-with-no-recorded-rejection` makes the missing argument a fault.
      This is S6.11's "ask where the evidence came from" applied to the surface
      a person actually reads. -->
  - [x] S7.3.a Consume authenticated runtime capability/dependency endpoints
        with version, checked-at, freshness, affected/retained behavior, owner,
        and safe remediation. Evidence: network/contract tests.
        <!-- evidence: `probeContractFaults(requested, response, expectedState)`
        + `useCapabilityProbe`, which parses the response with S1.6's own
        `parseCapabilityState`. A hand-rolled `body.state === 'configured'`
        accepts `{"state":"configured"}` with no owner, no freshness and no
        verifications — the exact shape a stub endpoint returns — so the browser
        tool serves that body from a real socket and CONTROL 5 asserts the page
        refuses it. `requested` is a separate parameter from
        `response.capability` because a probe that answers about the wrong
        capability is indistinguishable from a healthy one, which is what a
        shared cache key produces. THREE REAL KIT RULES THE FIXTURES HAD TO
        SATISFY, each caught by running the parser rather than by reading it: a
        `contactPath` must be an https rota or runbook URL (an email names a
        person rather than a responsibility), a `configured` capability must
        offer remediation `none` (the kit refuses "nothing is wrong, and here is
        how to fix it"), and `failed` requires a `correlationId` because a
        completed evaluation nobody can find the trace of is a guess. -->
  - [x] S7.3.b Represent configured, not-configured, degraded, unavailable,
        failed, stale-probe, probe-loading, and probe-error independently.
        Evidence: banner state fixtures.
        <!-- evidence: `BANNER_STATES` (8) + `PROBE_STATES` (3) +
        `bannerState(outcome, now)` + `bannerStateFaults`. THE SPLIT IS THE
        ITEM: `unavailable` says the dependency is down, `probe-error` says we
        could not ask. Collapsing them is S6.11's fail-open in one direction and
        a fabricated outage in the other, so
        `probe-state-rendered-as-a-capability-state` and its mirror are separate
        faults. STALENESS IS CHECKED BEFORE THE STATE IS READ — a stale
        `configured` is not a configured capability, it is an answer that has
        stopped being one — and rendering it in a quieter colour would keep the
        claim while looking careful. Browser CONTROL 3 gets a real 500 and
        CONTROL 4 a real three-hour-old `checkedAt`. -->
  - [x] S7.3.c Display fixture source/version and persistent unmistakable
        fixture labeling in banner and working context. Evidence: fixture UI
        tests.
        <!-- evidence: `REQUIRED_LABEL_SURFACES` + `fixtureLabelFaults` +
        `FixtureBadge`. THE BADGE BELONGS IN THE WORKING CONTEXT, NOT ONLY IN
        THE BANNER: a banner scrolls away, gets dismissed, and never appears in
        the screenshot somebody pastes into a ticket, so
        `fixture-labelled-only-in-the-banner` is a fault and browser CONTROL 6
        counts the badge in both surfaces. The source NAME and VERSION are part
        of the label — "fixture" alone does not say which one — and the badge is
        not dismissible (§2.2 says persistent, and a dismissible badge is absent
        for everybody after a week). `fixture-label-rendered-while-live` closes
        the other direction: a badge over live data teaches the reader that the
        badge means nothing. -->
  - [x] S7.3.d Prevent build variables, client heuristics, successful route
        load, cached content, or demo data from asserting live state. Evidence:
        false-live negative tests.
        <!-- evidence: `LIVE_CLAIM_ORIGINS` / `LIVE_ASSERTING_ORIGINS` /
        `ORIGIN_REJECTIONS` / `liveClaimFaults`, with a negative test per
        origin. The component decides the origin from the REQUEST, not from the
        answer — a 200 says nothing about whether a credential was sent — which
        is why browser CONTROL 2 is the sharpest check in this item: the
        endpoint returns a BYTE-IDENTICAL 200 body whether or not it saw an
        `authorization` header, the page still records
        `unauthenticated-probe-response`, and the kit still refuses a live claim
        on it. An endpoint that answered differently would have proved the rule
        by accident. -->
  - [x] S7.3.e Provide accessible detail and authorized setup/status link
        without exposing secrets or internal topology. Evidence: role/redaction
        tests.
        <!-- evidence: `bannerDisclosureFaults`, which COMPOSES S1.6's
        `parseSafeDetail` for credentials rather than inventing a second
        check, and adds what that check does not look for: TOPOLOGY. An internal
        hostname, a private address, a pod name and a bare host:port are not
        credentials — `parseSafeDetail` passes all four — and each is a free map
        of the estate for anybody reading the banner. The setup link is
        authorized BEFORE it is offered, in the kit and in the component, with a
        jsdom test proving an author does not see the settings button a tenant
        admin does; a link that produces a refusal has turned one problem into
        two and the second looks like a defect in the shell. -->
  - [x] S7.3.f Refresh on dependency/flag/provider change with bounded polling
        or event update and preserve last-known age honestly. Evidence:
        transition tests.
        <!-- evidence: `refreshPolicyFaults` + `MIN_POLL_INTERVAL_SECONDS` + the
        hook's scheduler. Faults: an unbounded poll, one below the floor, one
        that keeps running in a hidden tab, no backoff after repeated probe
        errors (client load rises exactly when the dependency can least take
        it), a poll where every invalidating change already arrives as an event,
        an invalidating change with nothing listening, an age computed from
        render time, and a failed refresh that leaves the previous answer on
        screen as current — the last of which hides precisely the outage the
        refresh existed to detect. THE HIDDEN-TAB PAUSE IS PROVED IN JSDOM, NOT
        IN THE BROWSER, and the reason is recorded in the test: headless
        Chromium reports every page as `visible`, opening a second tab does not
        make the first hidden, and `Emulation.setVisibilityStateOverride` does
        not exist in this Chromium — all three were tried. The browser proves
        what it can prove: real requests arriving at a real endpoint at the
        floored rate. -->
        <!-- THE FINDING FROM THE REAL FETCH: a `now` captured when the
        component MOUNTED is EARLIER than the timestamp of a probe that answers
        afterwards, so the age is negative, `isStateFresh` correctly refuses a
        negative age, and a perfectly fresh answer renders as `stale-probe`.
        Same family as an age computed from render time, in the opposite
        direction and far harder to notice, because the banner looks
        conservative rather than wrong. The reading now carries its own ticking
        `now`, read at comparison time rather than at mount or from the last
        tick. Nothing in jsdom would have surfaced this: it needs a response
        that genuinely arrives after the render. -->
        <!-- S7.3 rollup: 4691 kit tests (57 new) + 12 jsdom banner tests + the
        real-endpoint browser tool. Verified by patching the BUILT dist twice —
        adding `unauthenticated-probe-response` to `LIVE_ASSERTING_ORIGINS`
        fails CONTROL 2, and silencing the staleness check in `bannerState`
        fails CONTROL 9 — and passing again over the restored build. CONTROL 9
        exists BECAUSE of that second patch: the first attempt at it changed
        nothing the tool read, since every other control routes through the
        page (which runs the kit compiled from source), so the tool now runs the
        BUILT kit over the same probe body fetched separately and compares.
        Export map at 96 entry points, six kit gates clean, `eslint` and a
        non-vacuous `tsc` clean on the new app files. Registration adds the
        `capability-banner-browser` target on apps/oshun/web/project.json. -->
- [x] S7.4 Build a filter/search grammar with URL serialization. Evidence:
      copy/paste and back/forward restore the exact view.
      <!-- The evidence clause sounds like a serialization problem and is not.
      It is a problem about MEANING: a string somebody pastes into a chat window
      has to mean the same thing when it is opened, by a different person, in a
      different locale, a week later, against a workbench that has shipped twice
      since. Every part of `query-grammar.ts` (97th entry point, 49 tests) exists
      because one of those five words breaks something — the canonical form for
      "the same thing", the locale-neutral identifiers for "a different locale",
      the preserved relative date for "a week later", the grammar version for
      "shipped twice since", and the same-error-for-every-hidden-field rule for
      "a different person". All seven children complete. -->
      <!-- A QUERY IS A TREE AND THE STRING IS A RENDERING OF IT. The URL, the
      migration and the editor all operate on the tree, so there is one place
      where meaning lives, and the contract is that rendering a parsed tree and
      parsing it again yields the same tree — run over the whole corpus, not
      over the entries that happened to be convenient. -->
  - [x] S7.4.a Define typed field, operator, value, grouping, negation, range,
        relative date, taxonomy, rights, quality, ownership, ID, and free-text
        grammar. Evidence: grammar specification.
        <!-- evidence: `libs/oshun/workbench-kit/src/query-grammar.ts` — twelve
        `FIELD_KINDS`, twelve `OPERATORS`, and `OPERATORS_FOR_KIND` as a CLOSED
        TABLE keyed by kind rather than a per-field list, because which
        comparisons make sense is a property of the TYPE. `>` over a taxonomy
        term is not a stricter filter, it is a question with no answer, and
        allowing it returns an empty result set whose emptiness looks like a
        fact about the data — so the six unordered kinds admit no `gt` and no
        `between`, asserted directly. Values cover literal, number-with-unit,
        absolute date, RELATIVE date, boolean, list and range; grouping is
        parenthesised; negation is `NOT` or a leading hyphen; bare words are
        free text. -->
  - [x] S7.4.b Implement tokenizer/parser with positions and stable syntax
        errors for quotes, escapes, Unicode, nesting, ambiguity, and limits.
        Evidence: parser corpus.
        <!-- evidence: `tokenize` + `parseQuery` + 14 `SYNTAX_ERRORS`, each with
        a positive test, over a 27-entry corpus of queries somebody would
        actually type. EVERY NODE CARRIES A HALF-OPEN SOURCE RANGE in UTF-16
        code units, which is what a text input's selection API uses — an error
        without a position is one nobody can fix, and an editor that finds it by
        re-scanning has a second parser that will disagree with the first.
        Unicode is not special-cased anywhere: an Arabic tag, Cyrillic text and
        an emoji are ordinary term content. PRECEDENCE IS WRITTEN DOWN AND
        TESTED — `NOT` > `AND` > `OR`, juxtaposition is `AND` — because every
        search box implements some version of this and almost none document it,
        which is how `a OR b AND c` comes to mean two different things in two
        surfaces. THE TOKENIZER BUG THE CORPUS CAUGHT: marking a token as quoted
        because a quote appeared ANYWHERE in it turned `title~"two words"` into
        a free-text search, which is the failure that looks like the filter
        simply not working. `leadingQuote` and `valueQuoted` are now separate.
        -->
  - [x] S7.4.c Validate fields/operators/values against domain capability,
        contract version, authorization, locale-neutral units, and complexity
        budget. Evidence: semantic validation tests.
        <!-- evidence: `validateQuery` + 8 `SEMANTIC_ERRORS`. THE ONE THAT WOULD
        BE A SECURITY DEFECT RATHER THAN A USABILITY ONE: a field that does not
        exist, one belonging to a capability this workbench cannot use, and one
        the actor may not filter on all produce `unknown-field` with the SAME
        words — asserted character for character in the spec. If they differed,
        the search box would be an oracle for the schema and anybody could
        enumerate it one word at a time. This is S4's conceal-versus-deny split
        inside a parser. Also: an operator the kind does not admit, a value of
        the wrong kind, a value outside the declared set, a field that arrived in
        a newer contract, `AMBIGUOUS_UNITS` (a duration written `3mo` is three
        months to one reader and three minutes to another), a range whose bounds
        are the wrong way round, and a COMPLEXITY BUDGET spent while the query is
        still a tree — refusing it there is cheaper than discovering it as a
        timeout after the work is done. -->
  - [x] S7.4.d Serialize a canonical normalized query deterministically without
        changing meaning or localizing identifiers/operators. Evidence:
        parse/serialize property tests.
        <!-- evidence: `serializeQuery` + `queriesEqual`, with the round-trip
        property run over EVERY corpus entry rather than a chosen few — a
        canonical form that survives the examples somebody picked is a canonical
        form for those examples. Also asserted: serializing twice changes
        nothing; parentheses appear only where precedence needs them; a relative
        date stays relative (`created:>now-7d` never becomes a date); equivalent
        relative spellings (`now-168h`, `now-7d`, `now-1w`) collapse to ONE
        form; and no identifier or operator is ever non-ASCII. WHAT IT
        DELIBERATELY DOES NOT DO IS REORDER: `a AND b` and `b AND a` mean the
        same thing and serialize differently, because a canonical form that
        shuffles somebody's filters is one they stop trusting the first time
        they paste a link. Determinism means the same tree always renders the
        same string, not that equivalent trees do. -->
  - [x] S7.4.e Encode safe shareable query, sort, grouping, view, and pagination
        anchor in URL while excluding protected/local-only state. Evidence: URL
        leakage tests.
        <!-- evidence: `encodeViewState` / `decodeViewState` / `urlFaults`, with
        `VIEW_PARAMS` as the allow-list and `LOCAL_ONLY_KEYS` as the explicit
        refusal — selection, scroll, draft, token, impersonation, session id and
        cursor secret — because "do not put secrets in the URL" is a habit and
        habits do not fail builds. `protected-value-in-the-url` catches the
        subtler one: a filter on a protected field discloses the value even when
        the results do not, since the FILTER is in the link.
        `anchor-is-an-offset` refuses a row number as a pagination anchor —
        anything inserted before it shifts the page, so the link points at
        different rows every time it is opened — and `url-longer-than-the-safe-
        limit` names the point where proxies and mail clients truncate, after
        which a truncated query parses into a different one. -->
  - [x] S7.4.f Restore exact valid view on paste/direct
        link/refresh/back/forward and provide migration/fallback for older or
        invalid grammar versions. Evidence: navigation tests.
        <!-- evidence: `apps/oshun/web/tools/query-view-browser.mjs` — 6
        controls, 3 faults, real Chromium. The evidence clause names three
        things jsdom does not have: an address bar, a history stack and a
        keyboard, so a test that calls `decodeViewState` and compares trees
        proves the codec and nothing about the sentence. CONTROL 3 opens the
        shared link in a SECOND real page and checks the canonical query and the
        editor contents; CONTROL 4 presses real Back, real Forward and a real
        reload. A GRAMMAR VERSION WE DO NOT KNOW IS REFUSED, NOT READ PARTIALLY
        (S6.14's rule, one layer up), and the fault run caught the host doing
        exactly that: it was echoing the refused `q` back into the editor, which
        would re-parse a foreign grammar with this one. A query that failed to
        PARSE is handed back — it is in a grammar we understand and the reader
        can fix it — and a query in a version we do not know is not. -->
  - [x] S7.4.g Provide keyboard editor, suggestions, accessible token/error
        semantics, plain form alternative, and no search-on-every-keystroke
        abuse. Evidence: accessibility/performance tests.
        <!-- evidence: `apps/oshun/web/src/components/workbench/QueryEditor.tsx`
        — a `combobox` with `aria-activedescendant`, a `listbox` of field
        suggestions accepted from the keyboard, `aria-invalid` plus a polite
        error region that names the character RANGE (from the parser, never from
        a second scan), and a `<details>` plain form that reaches the same
        canonical query without the grammar, for anybody who does not know it
        and anybody driving the page by voice. NO SEARCH ON EVERY KEYSTROKE IS A
        NUMBER, NOT A CLAIM: browser CONTROL 1 types 29 real characters and
        counts 1 search. A box wired straight to a request sends one per
        character and the answers arrive out of order, so the list settles on
        whichever prefix the network happened to return last. CONTROL 5 confirms
        the underlined range is the quote (21-34) and not the whole string, by
        comparing it to the BUILT kit's own range for the same input. -->
        <!-- S7.4 rollup: 4741 kit tests (50 new) + the browser tool. Verified
        by patching the BUILT dist twice — widening the `unterminated-quote`
        range to the whole query fails CONTROL 5, and making `queriesEqual`
        always disagree fails CONTROL 2 — and passing again over the restored
        build. THE FIRST ATTEMPT AT A PATCH FAILED TO FAIL: weakening
        `SUPPORTED_GRAMMAR_VERSIONS` changed nothing, because the page runs the
        kit compiled from source and only the symbols the HARNESS imports are
        under test. Same lesson as S7.3, and the reason both tools cross-check
        against the built kit rather than only driving the page. Export map at
        97 entry points, six kit gates, `eslint` and a non-vacuous `tsc` clean.
        Registration adds the `query-view-browser` target. -->
- [x] S7.5 Build accessible data tables with column control, density, pinning,
      resizing, sort, selection, and virtualization. Evidence: keyboard and
      screen-reader tests.
      <!-- A table is the surface people spend the most time in and the one with
      the most ways to be quietly wrong, and almost none of them are visual: a
      sort that disagrees with the server, a selection that means something
      different from what it says, a total that counts the rendered window. Each
      looks correct on screen and produces the wrong answer downstream.
      `data-tables.ts` (98th entry point, 52 tests) names them;
      `apps/oshun/web/src/components/workbench/DataTable.tsx` renders a real one
      over 12,431 rows. All eight children complete. -->
      <!-- The browser proof earned its place three times over. Reading the
      source would not have shown that the touch density left its controls at
      24px, that the pinned column shared a sticky offset with the selection
      column and slid under it, or that a width constant disagreed with the
      rendered border box by 16px. All three are invisible in a screenshot and
      all three are one horizontal scroll away from being obvious. -->
  - [x] S7.5.a Define semantic table/grid pattern by interaction need,
        accessible name/description, captions, headers, relationships, row
        identity, and loading/empty/error state. Evidence: base table tests.
        <!-- evidence: `libs/oshun/workbench-kit/src/data-tables.ts` —
        `patternForInteraction` DERIVES the pattern rather than letting a
        surface pick one: `grid` exists because cells take focus, and a table
        whose cells do not take focus gains nothing from it but the obligation.
        `grid-without-arrow-key-navigation` is the fault that matters — the role
        tells assistive technology to switch to its own navigation mode, and
        without arrow-key cell movement the keys then do nothing, which is worse
        than never having claimed it. Also: an unnamed table (a page with two
        announces "table" twice), one with no caption or description (a
        nonvisual reader has the columns and no idea what filtered them), rows
        addressed by position rather than identity, and each of loading, empty
        and error unannounced, reported separately. -->
  - [x] S7.5.b Implement visible/hidden/ordered column control with required-
        column rules, reset, persistence, and authorization-aware fields.
        Evidence: column control tests.
        <!-- evidence: `columnControlFaults`. A COLUMN THE ACTOR MAY NOT SEE IS
        NOT A HIDDEN COLUMN — hidden means "you turned it off", unauthorized
        means it is not yours to turn on, and if they share a control the column
        picker becomes a list of the fields somebody is not allowed to read.
        Both directions are faults: offered in the picker, and actually
        rendered. A hidden-but-permitted column stays in the ORDER so turning it
        back on lands it where the actor left it, and dropping it is
        `hidden-column-forgotten-instead-of-remembered`. Required columns cannot
        be hidden, the order may not name a column that does not exist, and a
        table somebody has rearranged into uselessness has a reset. -->
  - [x] S7.5.c Implement comfortable/compact/touch density without clipping,
        inaccessible targets, or page overflow. Evidence: density/zoom tests.
        <!-- evidence: `MIN_ROW_HEIGHT_PX` + `MIN_TARGET_PX` + `densityFaults`,
        and browser CONTROL 4 MEASURES all three densities with
        `getBoundingClientRect` rather than reading the stylesheet. THAT
        MEASUREMENT FOUND A REAL DEFECT: the touch density enlarged its rows and
        left its checkbox at 24px, so the one thing a touch density exists for
        was the one thing it did not do. The control size is now derived from
        `MIN_TARGET_PX[density]`. The floors are WCAG 2.2's 24x24 minimum, with
        touch held to 44. Page overflow is checked as a document-level fact: the
        table scrolls inside its own region or it is a defect. -->
  - [x] S7.5.d Implement keyboard and numeric resizing, min/max/reset, persisted
        width, overflow disclosure, and screen-reader feedback. Evidence: resize
        tests.
        <!-- evidence: `resizeFaults` + the `separator` handle on every column
        header, with `aria-valuenow/min/max`. Keyboard resizing is not an
        alternative to the pointer one, it is the ONLY one a keyboard user has,
        so `resize-is-pointer-only` is a fault rather than an enhancement.
        Browser CONTROL 6 presses real ArrowRight, then asserts the RENDERED
        column width matches `aria-valuenow` to within a pixel — a handle that
        reports a number the layout does not agree with is a control that lies
        to exactly the reader who cannot see the result. End and Home clamp at
        the declared bounds rather than running away. -->
  - [x] S7.5.e Implement left/right pinning, pinned boundaries, keyboard
        navigation, reorder interaction, and narrow-view fallback. Evidence:
        pinning tests.
        <!-- evidence: `pinningFaults` + `position: sticky` on header and body
        cells. THE BROWSER FOUND TWO BUGS HERE THAT READING COULD NOT. First,
        the start offsets were computed from the data columns alone, so the
        first pinned column claimed `inset-inline-start: 0` — the offset the
        selection column already had — and slid underneath it on a real
        horizontal scroll. Then the constant said 40px and the cell rendered at
        56px, because it was not `border-box`, so it stuck 16px from where it
        started. Both look correct in a screenshot; CONTROL 5 scrolls for real
        and asserts the pinned column moved by at most one pixel WHILE an
        unpinned one moved 285. FAULT 2 removes the sticky positioning and
        confirms the same column then travels 100+px, so the control is not
        passing by accident. The kit also refuses a pinned region past half the
        viewport, a boundary the keyboard cannot cross, and a pinned column that
        is not contiguous with its edge — on both sides. -->
  - [x] S7.5.f Implement single/multi stable sort with direction/null/locale/
        tie-break semantics and server parity. Evidence: sort tests.
        <!-- evidence: `sortFaults`, and this is the child where a table can be
        perfectly usable, perfectly accessible, and still produce an order in
        which one row appears on two pages. FOUR RULES ABOUT AGREEING WITH THE
        SERVER. A client-side sort over a server-PAGINATED list orders the rows
        that happen to be loaded, not the list — the first row of page two can
        belong before the last row of page one — and the counterexample test
        confirms the same client sort is fine when the server is not paginating.
        Nulls have a position and it must be the server's, or the two pages are
        ordered by different rules. Collation is part of the contract: an
        undeclared one is the runtime default and changes with the reader's
        locale, and one that disagrees with the database means sorting is not a
        total order. A sort with no unique tie-break is not stable across pages,
        and a tie-break that is not the LAST key decides comparisons it was not
        meant to. -->
  - [x] S7.5.g Implement single/range/additive/select-all-filtered/offscreen
        selection with eligible/hidden/permission-changing behavior. Evidence:
        selection tests.
        <!-- evidence: `SelectionSet` + `rowSelectionFaults`. "SELECT ALL
        FILTERED" IS A QUERY, NOT A LIST OF IDS — ten thousand ids do not fit in
        a request and the set moves while somebody looks at it — so it is a
        frozen query plus the count observed at freeze, and anything downstream
        reports the difference rather than pretending there was none. A count
        taken from the DOM is a lie virtualization tells, so
        `count-derived-from-the-rendered-rows` is a fault on its own, and
        browser CONTROL 3 selects a row, scrolls 300,000px until that row has
        left the DOM ENTIRELY, and asserts the count is unchanged. Ineligible
        rows counted as selected make the preview promise more than the result
        delivers; a selection hidden by the current filter is the surprise this
        exists to prevent. -->
  - [x] S7.5.h Implement virtualization with total/position semantics, stable
        focus/selection/scroll anchor, dynamic rows, and semantic nonvirtual
        fallback for print/export. Evidence: large dataset a11y tests.
        <!-- evidence: `virtualizationFaults` + `DataTable.tsx`, proved over
        12,431 rows because a virtualized table over twenty rows proves nothing
        about virtualization. `aria-rowcount` IS THE TOTAL and `aria-rowindex`
        is ABSOLUTE: browser CONTROL 2 scrolls deep and confirms the first
        rendered row announces a position in the LIST (row 4,800-odd of 12,431)
        rather than in the window, which is the difference between describing
        the data and describing the implementation. PRINT DROPS THE WINDOW —
        `page.emulateMedia({media:'print'})` is a real print rendering, and
        CONTROL 7 asserts 18 rows on screen become 400 in print and that the
        pinned column stops being sticky so it is not printed over the rest.
        Without the `beforeprint`/`matchMedia` listener the print stylesheet
        alone cannot bring back rows React never rendered. -->
        <!-- S7.5 rollup: 4794 kit tests (52 new) +
        `apps/oshun/web/tools/data-table-browser.mjs` (7 CONTROLs, 2 FAULTs)
        over 12,431 rows in real Chromium. THE BROWSER FOUND THREE DEFECTS THE
        SOURCE DID NOT SHOW: the touch density leaving its controls at 24px, the
        pinned column sharing an offset with the selection column, and a width
        constant that disagreed with the rendered box. Verified by patching the
        BUILT dist twice — silencing the `aria-rowcount` check fails FAULT 1,
        and raising the comfortable row floor to 120px fails a measured CONTROL
        4 assertion — and passing again over the restored build. Export map at
        98 entry points, six kit gates, `eslint` and a non-vacuous `tsc` clean.
        Registration adds the `data-table-browser` target. -->
- [x] S7.6 Build kanban or stage-board primitives only where stage movement is
      the core interaction. Evidence: non-drag move controls and transition
      refusal UI.
      <!-- A board is the most requested workbench surface and the least often
      earned. It is a good surface for exactly one job — moving objects between
      stages — and a bad one for everything else it gets used for, because the
      thing it is best at is a column per stage, which looks like insight and is
      only ever a `GROUP BY`. So the item's own first child is a GATE, and this
      is the first S7 parent where the leading question is whether to build the
      thing at all. `stage-boards.ts` (99th entry point, 88 tests) answers it
      from MEASURED movement over the S2.1 stage graph rather than from a
      request, and `apps/oshun/web/src/components/workbench/StageBoard.tsx`
      renders one. All seven children complete. -->
      <!-- The claims here are facts about an interaction OVER TIME, which is
      why the proof is `apps/oshun/web/tools/stage-board-browser.mjs` (12
      CONTROLs, 3 FAULTs) rather than a jsdom render: whether a card is still in
      its source column WHILE a request is in flight, whether a real HTML5 drag
      lands on a column the server did not declare, whether the keyboard path
      produces a byte-identical board to the pointer path, where focus is
      afterwards, and what a genuinely offline browser does with a move. Verified
      by patching the BUILT dist twice — flipping `conflict.retryAllowed` to
      true, and raising the `CONFIRMATION_BANDS` floors — and confirming CONTROL
      7 and CONTROL 8 fail, then pass again over the restored build. -->
  - [x] S7.6.a Require a domain applicability decision proving stage movement,
        not monitoring or decorative status, is the primary object interaction.
        Evidence: board registration rationale.
        <!-- evidence: `boardApplicabilityFaults(decision, graph)` in
        `libs/oshun/workbench-kit/src/stage-boards.ts`. It takes the rationale
        AND the S2.1 stage graph SEPARATELY, because they can disagree and the
        disagreement is the finding — S6.6's photocopy rule applied to a UI
        registration, so a claim cannot agree with itself. THE SHARPEST RULE IS
        THE GRAPH ONE: a registration that says people move objects, over a
        graph in which every `Transition.trigger` is `system`/`schedule`/
        `upstream-event`, is a request for drag handles on a report
        (`no-actor-triggered-transition`), and a graph where fewer than half the
        non-terminal stages have an actor-triggered way out is a set of places
        objects arrive at (`most-stages-cannot-be-moved-from`). The counts are
        refused unless they are `measured-from-the-audit-log`, because the
        belief that people work the board is exactly the belief a board creates.
        Floors, each with its reason: 14 days, 50 objects,
        `MIN_ACTOR_MOVES_PER_OBJECT` = 2 (one move is a lifecycle and the
        surface for a lifecycle is a status column), and half the sessions
        moving something (below that people come to look). `MAX_BOARD_STAGES` is
        DERIVED — `BOARD_REFERENCE_VIEWPORT_PX / BOARD_COLUMN_MIN_WIDTH_PX` =
        1440/220 = 6 — and browser CONTROL 2 MEASURES the derivation: six
        columns fit and a seventh overflows the track. -->
  - [x] S7.6.b Render typed stages, allowed/terminal state, counts,
        WIP/capacity, ordered items, freshness, and authorization from server
        contracts. Evidence: stage board fixtures.
        <!-- evidence: `boardColumnFaults(columns, graph, now)`, 19 faults, and
        browser CONTROL 1 feeds it the columns read straight out of the DOM. A
        COLUMN COUNT MUST BE THE STAGE'S TOTAL, NOT THE ITEMS LOADED INTO IT —
        S7.5's `aria-rowcount` defect one level up and worse, because the count
        is the number people plan with, so a column saying 12 while holding the
        first 12 of 900 is the difference between a WIP limit and a WIP
        suggestion. A STAGE THE ACTOR MAY NOT SEE IS NOT AN EMPTY STAGE: an
        empty column is a claim about the data and the truth is a claim about
        the reader, so `deny` renders "not yours to read" and `conceal` renders
        NOTHING (browser CONTROL 12 drives both). Terminality is READ from the
        graph, never restated by the column, and a column that disagrees is
        itself the finding; dead ends come from `deadEnds(graph)`. The
        truncation note fires in BOTH directions, because a note that is always
        there is furniture and stops working on the column where it is true. The
        clock is read at COMPARISON time (S7.3): an observation timestamped
        after `now` is `observation-is-in-the-future`, not a fresh column. -->
  - [x] S7.6.c Implement drag preview/drop only for server-declared candidate
        transitions and preserve source until accepted. Evidence: drag tests.
        <!-- evidence: `dragFaults(interaction, now)`, 15 faults, and browser
        CONTROLs 3 and 4 with a REAL HTML5 drag. THE SOURCE COLUMN KEEPS THE
        ITEM UNTIL THE SERVER ACCEPTS: the component writes an item's stage in
        exactly one place, the branch handling an `accepted` result, so a
        refusal needs no compensation — the board never claimed the move
        happened. CONTROL 3 proves it with a 400ms server: mid-flight the card
        still reports `data-stage="packing"`, `data-board-state` is unchanged,
        and it is marked pending; then the accept moves it. `source-removed-at:
        drag-start` is the defect that looks fine in every screenshot and is
        indistinguishable, when the refusal lands, from somebody else moving the
        object back. DROP IS PERMITTED EXACTLY WHERE THE SERVER SAID: CONTROL 4
        holds a drag open and reads the columns that opted in — exactly the
        three the server declared — then drops on a fourth and asserts the board
        is byte-identical afterwards. `candidates-recomputed-on-the-client` is a
        second implementation of the transition graph that drifts on the first
        guard nobody mirrored, and a candidate list is perishable
        (`CANDIDATE_FRESHNESS_SECONDS` = 60). -->
  - [x] S7.6.d Provide select-plus-move command, target list, keyboard
        shortcuts, and accessible move dialog as fully equivalent non-drag
        paths. Evidence: keyboard/screen-reader tests.
        <!-- evidence: `nonDragParityFaults(reaches, dialog, shortcuts)` and
        browser CONTROL 5. PARITY IS CLAIMED PER OPERATION, NOT PER SURFACE:
        `BOARD_OPERATIONS` names all seven, and "the keyboard can move a card"
        is true of a board where the keyboard cannot choose the position, cannot
        move a selection and cannot read the refusal. The equivalence is
        STRUCTURAL rather than a pair of paths somebody keeps in sync — the drop
        handler and the keyboard command call one `performMove`, and CONTROL 5
        makes the same move both ways and compares `data-board-state` character
        for character. The target list applies S4's conceal/deny split: a target
        refused for a reason the actor may know MUST be listed WITH the reason
        (omitting it sends them back to try the drag again) and a concealed one
        must not be listed at all, because a greyed row carrying its name is the
        disclosure. Shortcuts compose S7.1's `RESERVED_CHORDS`; the one-key
        binding lives on the card rather than on the document, so CONTROL 10
        types "order 42m0" into the filter and no dialog opens. -->
  - [x] S7.6.e Preview guard/evidence/approval/impact/cost requirements and
        require confirmation proportionate to transition risk. Evidence:
        transition preview tests.
        <!-- evidence: `transitionRiskScore` + `RISK_POINTS` +
        `CONFIRMATION_BANDS` + `requiredConfirmation` + `transitionPreviewFaults`
        (16 faults). CONFIRMATION IS PROPORTIONATE IN BOTH DIRECTIONS.
        Under-confirming an irreversible publish is obvious; OVER-confirming a
        routine move is the same defect with a longer fuse, because a board that
        asks "are you sure" for every drag trains everybody to dismiss the one
        that mattered. The score is a published table rather than a chain of
        `if`s, so a reviewer argues with a number, and blast radius is
        logarithmic (`blastRadiusPoints`) so a large routine bulk move does not
        outrank an irreversible single one. Guards compose S2.2's
        `NON_VERDICT_STATUSES`: a guard that could not run rendering as a tick
        is S6.11's fail-open in a preview. Browser CONTROL 8 runs
        `requiredConfirmation` from the BUILT dist over the same risk the page
        rendered and compares — a routine move asks for nothing, a dispatch
        demands the strength the kit computes, the button stays disabled until
        the challenge is met, a refused challenge leaves the object where it
        was, and a workbench with NO configured challenge says so and refuses
        rather than downgrading to the confirm button it does have. -->
  - [x] S7.6.f Handle accepted, conflict, refused-with-reasons, stale evidence,
        unauthorized, partial bulk, offline, and failed transitions without
        optimistic false state. Evidence: result matrix.
        <!-- evidence: `MOVE_OUTCOME_RULES` (the matrix as data) +
        `moveResultFaults` + `moveMatrixFaults`, and browser CONTROL 7 drives
        SEVEN of the eight through the real component and CONTROL 9 drives the
        eighth with `context.setOffline(true)`. THE EIGHT ARE NOT FOUR PAIRS.
        `conflict` is a successful move by somebody else, so offering a retry
        offers to overwrite it and the honest offer is the re-read;
        `unauthorized` requires a re-read even though nothing about the object
        changed, because the move was OFFERED, which means either the grants
        changed or the candidate list was wrong. The component reads
        `retryAllowed`/`refreshRequired`/`announcement` FROM the rule, so
        patching `conflict.retryAllowed` in the BUILT dist makes CONTROL 7 fail
        (verified). Politeness is checked in both directions. `offline` must not
        become a background intention: CONTROL 9 asserts the board is unchanged,
        a retry is offered because the request never left, and RECONNECTING DOES
        NOT SILENTLY DELIVER THE MOVE. `moveMatrixFaults` is separate because a
        board can render every outcome it has ever SEEN perfectly and fall
        through to the generic failure toast for the two it has not. -->
  - [x] S7.6.g Preserve focus/selection after move and expose semantic
        table/list parity for every board fact and operation. Evidence:
        nonvisual journey.
        <!-- evidence: `focusPreservationFaults` + `boardParityFaults`, browser
        CONTROL 6 and CONTROL 11, and
        `apps/oshun/web/src/components/__tests__/StageBoard.spec.tsx`. FOCUS IS
        RESTORED BY IDENTITY, NEVER BY INDEX — after a move the object at that
        position is a different one, so the next keystroke acts on something
        nobody chose. CONTROL 6 reads `document.activeElement` for real: after
        an accept it is the same card in its new column, after a refusal it is
        the refusal message (which is where the object is NOT), and never the
        body. THE REAL BROWSER FOUND A BUG IN THE RULE ITSELF: the first refusal
        it drove had no selection at all, and
        `refused-item-dropped-from-the-selection` fired over an empty set — a
        rule asserting about a set nobody had built. Fixed and covered by a
        counterexample test. Parity is a table over `BOARD_FACTS`: a fact
        conveyed only by WHICH COLUMN a card sits in is a fact a list does not
        have, so CONTROL 11 asserts the stage is a VALUE in the row, that all
        eight other facts are columns in the list, and that a move can be
        started from it — an equivalent view that cannot act is a read-only
        copy. -->
        <!-- S7.6 rollup: 4883 kit tests (88 new, 99th entry point) + 12 jsdom
        tests + `apps/oshun/web/tools/stage-board-browser.mjs` (12 CONTROLs, 3
        FAULTs) in real Chromium. THE BROWSER EARNED ITS PLACE ON THE FIRST RUN:
        `refused-item-dropped-from-the-selection` fired over a move made with no
        selection. `packed-consumer-smoke` earned its place too — `Math.floor`
        at module scope is work at import time that `"sideEffects": false` tells
        a bundler it may drop, so `MAX_BOARD_STAGES` subtracts the remainder
        before dividing instead. Six kit gates, `eslint` and a non-vacuous `tsc`
        (proved by injecting a type error) clean. Registration adds the
        `stage-board-browser` target. -->
- [x] S7.7 Build a stable inspector router. Evidence: selection persists across
      refresh and invalid/deleted selections recover safely.
      <!-- An inspector looks like a detail view and behaves like a second
      application: it has its own routing, its own loading, its own
      authorization, its own history and its own focus, and every one of those
      is somewhere a workbench quietly loses what somebody was looking at.
      `inspector-routes.ts` (100th entry point, 54 tests) names the rules and
      `apps/oshun/web/src/components/workbench/InspectorRouter.tsx` obeys them
      structurally rather than by checking: ONE function builds the URL and it
      walks the kit's five-key allow-list, so a draft or a token has no path
      into the address bar; ONE branch produces the concealed refusal, so the
      three answers that must be identical cannot drift apart. All seven
      children complete. -->
      <!-- Half of this item is REQUESTS and half is NAVIGATION, and jsdom has
      neither, so the proof is
      `apps/oshun/web/tools/inspector-router-browser.mjs` (10 CONTROLs, 3
      FAULTs) against a real `node:http` server: `page.reload()` throws the tree
      away for real, `page.goBack()` is a real popstate, and
      `page.on('request')` counts what left the browser rather than what a mock
      was asked for. THE BROWSER FOUND TWO REAL DEFECTS — a malformed id was
      left in the address bar forever because the early return skipped the
      clearing rule, and a concealed refusal erased its own explanation one
      render after clearing the selection that produced it. Verified by patching
      the BUILT dist twice: `RECOVERY_RULES.filtered.clearsSelection` and
      dropping `subview` from `ROUTE_STATE_KEYS` each fail a MEASURED control,
      and both pass again over the restored build. -->
  - [x] S7.7.a Register inspector routes by typed object/subobject/anchor,
        capability, authorized data loader, component, and compatibility
        version. Evidence: inspector registry tests.
        <!-- evidence: `inspectorRegistryFaults(routes, anchorKindsByObject)` in
        `libs/oshun/workbench-kit/src/inspector-routes.ts`. The anchorable parts
        of an object arrive SEPARATELY from the routes, so a route anchoring at
        a `block` in an object that has no blocks is a finding rather than a
        self-consistent pair — S6.6's photocopy rule. The sharpest rules are
        about the LOADER: one that is registered as not applying authorization
        relies on its caller having checked, and A DEEP LINK HAS NO CALLER; and
        one loader serving several object kinds means one authorization rule
        covers all of them, so the narrowest kind sets the rule for the widest.
        Two routes resolving the same object and subview is a finding because
        which one a URL opens then depends on registration order, so the same
        link shows different people different panels. The compatibility window
        is checked in both directions: inverted, and a window one major wide,
        which makes every link shared before the last payload change unopenable
        rather than degraded. -->
  - [x] S7.7.b Encode only safe workbench/object/subview/anchor/compare state in
        route and keep drafts/secrets/transient credentials local. Evidence: URL
        state tests.
        <!-- evidence: `ROUTE_STATE_KEYS` + `inspectorUrlFaults`, and browser
        CONTROL 1. THE URL IS A COPY — pasted into chat, kept in a bookmark,
        logged by every proxy, sent as a `Referer`, read by whoever finds it —
        so the route may carry WHICH object is being looked at and nothing about
        what is in it. The vocabulary is an ALLOW-LIST, because the failure mode
        is always one more key that seemed harmless and a deny-list only catches
        the ones somebody already thought of. `permission-decision-in-the-url`
        is the one worth naming twice: `?canEdit=1` reads as state and behaves
        as an authorization anybody can type. Sensitivity composes S3's
        `PRESENCE_DISCLOSING_CLASSES` — for `protected-source` the KEY BEING
        PRESENT is the disclosure, whatever the value says. CONTROL 1 loads a
        link carrying `token` and `rogue`, and the address bar afterwards holds
        neither; a draft typed into the panel survives a subview change and does
        NOT survive a reload, which is what local-only means. -->
  - [x] S7.7.c Load by stable identity with tenant/object/property authorization
        and handle loading/partial/stale/conflict/degraded state. Evidence:
        loader tests.
        <!-- evidence: `inspectorLoaderFaults(load, now)`, 12 faults. IDENTITY
        IS STABLE OR THE LINK ROTS: a slug from a title breaks every shared link
        the first time somebody fixes a typo, and an index into a list makes the
        link open a DIFFERENT object without failing, which is worse.
        AUTHORIZATION IS THREE QUESTIONS — tenant, object, and property — and
        the third is the leak nobody sees, because a payload carrying fields the
        actor may not read looks exactly right on screen; the only thing
        stopping the disclosure is that the panel does not draw them. A check
        after the read is a finding at both levels: the data has already crossed
        the boundary and how long the refusal takes answers whether the id
        exists. `partial-rendered-as-complete` turns an outage into an edit —
        the section that failed to load renders as a section somebody emptied.
        Browser CONTROL 10 drives a partial and a stale load through the real
        server and reads the notices back. -->
  - [x] S7.7.d Preserve inspector selection, tab, scroll, and focus on refresh,
        back/forward, workspace navigation, and compatible revision update.
        Evidence: navigation tests.
        <!-- evidence: `PRESERVATION_RULES` + `preservationFaults(before,
        after)` + `HISTORY_RULES` + `historyDispositionFaults`, and browser
        CONTROLs 2 and 3. Preservation is only observable ACROSS a transition,
        so the finder takes a before AND an after (S7.1's lesson): one snapshot
        of a panel that threw its selection away looks the same as one that
        never had it. THE ROW WORTH ARGUING ABOUT IS THE INCOMPATIBLE REVISION
        UPDATE, which keeps the object and must NOT keep the subview — a panel
        holding the name of a section it can no longer render is a heading over
        nothing, which is worse than moving. `scroll-restored-before-the-content`
        is the silent one: applied before the content that gives the panel its
        height, the position clamps to zero and the restoration reports success.
        CONTROL 2 scrolls to 420, reloads for real, and reads 420 back. CONTROL
        3 measures `history.length`: one object selection adds one entry, three
        tab changes add none, Back returns to the previous SELECTION, and the
        dispositions handed to the kit are DERIVED from those measurements
        rather than restated. -->
  - [x] S7.7.e Recover from invalid, malformed, unsupported, moved, deleted,
        filtered, archived, tombstoned, permission-hidden, and cross-tenant
        selection. Evidence: recovery matrix.
        <!-- evidence: `RECOVERY_RULES` (ten problems as data) +
        `recoveryFaults` + `indistinguishabilityFaults` + `recoveryMatrixFaults`,
        and browser CONTROLs 4, 5 and 8. THE TEN ARE NOT SEVERITIES, and "clear
        the selection" is the WRONG recovery for most of them: a filtered object
        exists and the current view hides it, so clearing loses what somebody
        was working on to a control they could have changed; an archived one is
        readable and not editable; a tombstoned one has the RECORD OF ITS
        REMOVAL as its content; a moved one is FOLLOWED, with permanent-redirect
        semantics and a `replace` rather than a push, because a location that no
        longer resolves is not a place to go Back to. DELETED,
        PERMISSION-HIDDEN AND CROSS-TENANT ANSWER IDENTICALLY, and CONTROL 5
        proves it twice: the three HTTP responses are asserted byte-identical
        (status, content-length, body) AND the three rendered panels are
        compared character for character, because either alone leaves the other
        free to be an oracle. CONTROL 8 types `Robert; DROP TABLE stories` into
        the route and asserts the server received NOTHING — an id that is not an
        id can only produce a log line and an error rate somebody
        investigates. -->
  - [x] S7.7.f Restore focus to invoker/selected object/fallback after close or
        invalidation and announce the change. Evidence: focus tests.
        <!-- evidence: `inspectorFocusFaults` and browser CONTROL 9. The panel
        is removed from the document, so `focus()` on a node inside it silently
        moves focus to the body and looks in every log like a successful
        restoration — the component checks `isConnected` before calling it and
        falls back when the invoker is gone. `focus-returned-to-an-invoker-that-
        is-gone` is the case the declared fallback exists for: the row that
        opened the panel is exactly the row a deletion removes.
        `announcement-does-not-name-the-object` is the other half — "panel
        closed" arriving after a list has re-rendered says nothing about which
        of several panels or which object. CONTROL 9 reads
        `document.activeElement` after a real close and finds the invoking row,
        not the body. -->
  - [x] S7.7.g Prevent inspector routes from mutating state on resolution and
        avoid hidden N+1/unbounded fetch behavior. Evidence: network and
        no-mutation assertions.
        <!-- evidence: `inspectorResolutionFaults` +
        `PERMITTED_RESOLUTION_WRITES`, and browser CONTROLs 6 and 7 counting
        REAL requests. RESOLVING A ROUTE MAY NOT WRITE: if it does, the Back
        button is a mutation, a link preview is a mutation and a crawler is an
        editor. Exactly one write is permitted and it is named — an append-only
        read audit, which is not the object, does not change what any other
        reader sees and is not covered by the object's entity tag; a "last
        viewed" stamp is all three. CONTROL 6 drives a load, a tab change, a
        reload, a selection and a Back and asserts ZERO non-GET requests, at the
        browser AND at the server. CONTROL 7 renders the same panel over 5 items
        and over 500 and asserts the request count is IDENTICAL — the shape is
        the finding, not the figure, which is why the ceiling is a baseline plus
        `MAX_REQUESTS_OVER_BASELINE` rather than zero. -->
        <!-- S7.7 rollup: 4938 kit tests (54 new, 100th entry point) + 11 jsdom
        tests + `apps/oshun/web/tools/inspector-router-browser.mjs` (10
        CONTROLs, 3 FAULTs) against a real `node:http` server in real Chromium.
        The browser found two defects reading the source did not show. A dist
        patch is only a verification when it changes a MEASURED verdict:
        WIDENING `ROUTE_STATE_KEYS` proved nothing and NARROWING it failed the
        control, which is the same asymmetry S7.5 found with a floor. Six kit
        gates, `eslint` and a non-vacuous `tsc` clean. Registration adds the
        `inspector-router-browser` target. -->
- [x] S7.8 Build structured field and block-editor primitives with validation,
      dirty state, autosave, undo/redo, and revision metadata. Evidence:
      conflict journey.
      <!-- This is the surface where a piece of work lives between the moment somebody
      type it and the moment it is somewhere durable, so every defect in it
      costs them something they wrote — and almost none of them look like
      defects: a save indicator a quarter of a second early, a dirty flag that
      counts the server trimming a space, an undo that reaches back past
      somebody else. `block-editors.ts` (101st entry point, 62 tests) names them
      and `apps/oshun/web/src/components/workbench/BlockEditor.tsx` makes four
      of the rules STRUCTURAL rather than checked: `dirty` is
      `serialize(local) !== serialize(base)` with no counter anywhere, "saved"
      is written in the one branch that handles a 200, the queue is in
      `localStorage`, and undo pushes a new operation against a stack fenced at
      every remote edit. All eight children complete. -->
      <!-- The evidence asked for is a CONFLICT JOURNEY, which is a sequence of
      things happening to one document over time, so the proof is
      `apps/oshun/web/tools/block-editor-browser.mjs` (10 CONTROLs, 3 FAULTs)
      against a real `node:http` server holding a real revision.
      THE BROWSER FOUND A REAL DEFECT: the undo entry captured its selection
      inside the CHANGE handler, by which time the browser had already replaced
      the selected text, so undo restored a caret at 9 where the actor had a
      range at 8..13. Reading it in `beforeinput` is the fix. Verified by
      patching the BUILT dist twice — dropping `local-storage` from
      `DURABLE_STORES`, and adding a revision fact the banner does not render —
      each failing a measured control, and both passing again over the restored
      build. -->
  - [x] S7.8.a Register versioned typed field/block schema, stable identity,
        editor renderer, semantic renderer, validation, diff, migration, anchor,
        and serialization. Evidence: block registry tests.
        <!-- evidence: `blockRegistryFaults` in
        `libs/oshun/workbench-kit/src/block-editors.ts`. A BLOCK WITHOUT A
        SEMANTIC RENDERER IS CONTENT THAT ONLY EXISTS IN THE EDITOR — export,
        print, search, the feed and the API all read the semantic form, and a
        block that has only an editor form is invisible to every one of them.
        IDENTITY IS NOT DERIVED FROM CONTENT: a block id computed from its text
        changes when somebody fixes a typo, and every anchor, comment, reference
        and deep link pointing at it dies silently. The migration check WALKS
        the chain from `oldestStoredVersion` rather than counting migrations,
        because the failure is always a gap — two migrations look like plenty
        and neither starts at the version a stored document is saved at. Also:
        one renderer serving both views, an undeclared round trip, anchors the
        serializer drops, and a validator shared between two block types (the
        rules for the strictest apply to the loosest and the codes name the
        wrong type). -->
  - [x] S7.8.b Build type-appropriate controls with labels/help, units/formats,
        required/nullable/default, read-only/unauthorized, and accessible
        semantics. Evidence: field fixture matrix.
        <!-- evidence: `CONTROL_FOR_KIND` + `fieldControlFaults`, and browser
        CONTROL 1 reads the rendered control back for every kind. The control is
        DERIVED from the kind, and a date in a free-text box is the most
        expensive instance: it parses differently in two locales and what comes
        back is a different day rather than an error. AN UNAUTHORIZED FIELD IS
        NOT A READ-ONLY FIELD — read-only shows the value and refuses the edit,
        unauthorized must not show it — and the component renders a DIFFERENT
        ELEMENT rather than a disabled control, so CONTROL 1 asserts the value
        appears nowhere in the document at all. A number states its unit
        (millilitres and litres look identical in a text box), a date states its
        format, `required` and `nullable` together is a contradiction one side
        will enforce, a default that fails its own validation makes a new block
        invalid before anybody touches it, and the validation message is
        ASSOCIATED with the control rather than merely drawn near it. -->
  - [x] S7.8.c Implement add/remove/move/reorder/nest/split/merge/reference
        operations through domain commands rather than arbitrary JSON mutation.
        Evidence: structured operation tests.
        <!-- evidence: `BLOCK_OPERATIONS` + `INVERSE_OPERATION` +
        `blockCommandFaults(commands, rules, rawMutations)`. The raw mutations
        arrive as their own argument, so a change the editor made outside the
        vocabulary is the finding rather than an absence: a patch cannot be
        inverted for undo, cannot be authorized per operation, cannot carry the
        anchors a split has to divide, and cannot be replayed against a document
        that moved. `split-or-merge-loses-an-anchor` is the one that costs a
        newsroom a comment thread — the anchor is still in the database and
        points at a block that no longer exists. The allowed parents and the
        depth come from the document model passed in, not from the command, and
        a cross-document reference with no pinned revision quietly re-quotes
        whatever its author last wrote. -->
  - [x] S7.8.d Run local field/cross-block validation for feedback and
        authoritative service validation on save; preserve stable reason/
        remediation. Evidence: shared invalid fixtures.
        <!-- evidence: `validationParityFaults(fixtureIds, local, service,
        decisions)` and browser CONTROL 8. THIS IS THE ONE THAT SETTLES THE
        QUESTION: two validators tested separately both pass their own tests,
        and whether they say the same thing about the same document is a
        different question. The finder runs BOTH over ONE corpus and compares
        the REASON CODES, because a client reporting `title-too-long` where the
        service reports `headline-length` cannot point the refusal at the field
        somebody has to fix. Both failure directions are faults: a client pass
        treated as the decision (the client is the part an attacker controls),
        and a save the client refused to send that the service would have
        accepted (somebody cannot save valid work and the workaround is another
        browser). A reason code must be a slug — prose cannot be matched,
        counted or translated. -->
  - [x] S7.8.e Track dirty state against base revision across local changes,
        server normalization, remote edits, save, failure, and restore.
        Evidence: dirty-state tests.
        <!-- evidence: `dirtyStateFaults(observation)` and browser CONTROLs 2, 4
        and 10. DIRTY IS A COMPARISON AGAINST THE BASE REVISION, NOT A COUNT OF
        KEYSTROKES: CONTROL 2 types a character and deletes it and reads
        `data-dirty="false"` back, which a counter cannot produce. THE SERVER
        NORMALIZING WHAT IT STORED IS NOT A LOCAL CHANGE — CONTROL 4 saves
        `"Bridge  vote   carried  "`, the server stores it single-spaced, and
        the editor shows the stored form and reports NO changes; counting that
        difference makes the next autosave send the normalization back to be
        normalized again. A failed save restores the flag and leaves the base
        revision alone (CONTROL 10), a commit advances it, and somebody else
        saving must not clear a dirty flag this editor is holding. -->
  - [x] S7.8.f Implement durable autosave/offline queue/failure recovery with
        explicit state and no saved indicator before commit. Evidence: autosave
        fault tests.
        <!-- evidence: `autosaveFaults` + `DURABLE_STORES`, and browser CONTROLs
        3 and 5. "SAVED" IS A PROMISE and it may not precede the commit it
        describes: CONTROL 3 holds the response open for 600ms and reads
        `in-flight` off the editor while the request is still open, then `saved`
        only once the revision moved. A QUEUE THAT DOES NOT SURVIVE A RELOAD IS
        NOT A QUEUE: CONTROL 5 goes offline, queues an edit, RELOADS THE PAGE
        and finds the work still there with its idempotency key and the revision
        it is a change to — and asserts nothing drained itself on load. Every
        queued entry carries a key (a resend after a timeout that actually
        succeeded would otherwise duplicate a block) and an expected revision
        (the queue drains exactly when somebody else has been editing). The
        retries are capped and the queued content is recoverable, because the
        last honest thing an editor can do is let somebody copy what it is
        holding. -->
  - [x] S7.8.g Implement operation-aware undo/redo with focus/selection
        preservation and no server history rewrite or side-effect replay.
        Evidence: undo boundary tests.
        <!-- evidence: `undoFaults` and browser CONTROLs 6 and 7. UNDO IS A NEW
        FORWARD OPERATION — rewriting server history says the change never
        happened, which is false for everybody who saw it and for every export
        taken since. AND IT STOPS AT AN EDIT BY SOMEBODY ELSE: the stack carries a
        FENCE at every remote commit, and CONTROL 7 has a second author commit
        for real on the server, waits for the poll to notice, then presses
        Ctrl+Z and asserts the undo is REFUSED WITH A REASON rather than
        silently reverting them. CONTROL 6 is where the browser earned its
        place: selecting characters 8..13, typing over them and undoing restored
        a caret at 9, because the entry captured its selection inside the change
        handler — after the browser had already replaced the text. Reading it in
        `beforeinput` is the fix, and jsdom would have agreed with the bug. A
        new edit clears the redo branch, and the stack is capped. -->
  - [x] S7.8.h Display current/base revision, author/time/reason/save state and
        detect expected-revision conflict with compare/preserve/resolve actions.
        Evidence: conflict end-to-end journey.
        <!-- evidence: `REVISION_FACTS` + `CONFLICT_ACTIONS` +
        `conflictJourneyFaults`, and browser CONTROL 9, which is the journey the
        item asks for: type a headline, have a second author commit for real,
        save, take the 409, and walk compare -> resolve. A CONFLICT MUST
        PRESERVE THE LOCAL WORK — every other outcome here is arguable and this
        one is not — so the control asserts the typed text is in the box at
        every step, including after the resolution. The comparison shows BOTH
        sides (showing only what is stored makes the decision from memory), all
        three actions are offered, the resolution is written as a NEW revision
        (rev-4 -> rev-5) rather than an overwrite, and it is timestamped so a
        later reader can tell which edits the reconciliation had in front of
        it. -->
        <!-- S7.8 rollup: 5001 kit tests (62 new, 101st entry point) + 11 jsdom
        tests + `apps/oshun/web/tools/block-editor-browser.mjs` (10 CONTROLs, 3
        FAULTs) against a real `node:http` server that holds a revision,
        normalizes what it stores, honours `if-match` and `idempotency-key`, and
        can be committed to by a second author. The browser found the
        selection-capture defect. Six kit gates, `eslint` and a non-vacuous
        `tsc` clean; the scanner hits on "placeholder" are the HTML attribute
        the rule is ABOUT and carry `stub:legitimate`. Registration adds the
        `block-editor-browser` target. -->
- [x] S7.9 Build tree and hierarchy editing with cycle prevention, keyboard
      reorder, and impact preview. Evidence: tree property tests and a11y.
      <!-- A tree is the one workbench structure whose invariants cannot be
      checked by looking at the thing being changed: whether a move creates a
      cycle is a fact about the whole graph, whether a sibling order is intact
      is a fact about rows nobody read, and whether a move is safe is a fact
      about everything hanging underneath it. So `hierarchies.ts` (102nd entry
      point, 41 tests) takes the TREE as well as the command in almost every
      rule, and the tests GENERATE trees rather than hand-writing them. All
      seven children complete. -->
      <!-- TWO tools, because the item asks for two kinds of evidence.
      `libs/oshun/workbench-kit/tools/hierarchy-cycle-race.mjs` races a real
      PostgreSQL 16.14 and produces the finding the item is named for: TWO LEGAL
      MOVES MAKE AN ILLEGAL TREE, at READ COMMITTED and at REPEATABLE READ
      alike. `apps/oshun/web/tools/hierarchy-tree-browser.mjs` drives the
      `tree` pattern with real keys, a real focus ring, a real scrollbar and a
      real modal. Verified by patching the BUILT dist twice — weakening
      `moveWouldCycle` to a one-level comparison, and silencing the cycle
      detector — each failing a measured control. -->
  - [x] S7.9.a Define stable node ID/type, parent, sibling order, depth,
        properties, allowed parent/child relations, roots, and limits. Evidence:
        tree contract tests.
        <!-- evidence: `treeContractFaults` in
        `libs/oshun/workbench-kit/src/hierarchies.ts`. The two halves of the
        model are checked AGAINST EACH OTHER: a type declared a root whose
        allowed parents do not include the root means the contract disagrees
        with itself about where a tree starts, and a type that may sit nowhere
        is a promise the model cannot keep. The ceilings are refused when they
        are meaningless (a depth of one is a list, a fan-out of zero forbids
        every child) and when they are absent — an unbounded tree makes the
        recursive read that renders it bounded by whatever somebody
        imported. -->
  - [x] S7.9.b Implement authorized add, rename/edit, duplicate, move/reparent,
        reorder, archive/delete, restore, and reference operations with expected
        revision. Evidence: command tests.
        <!-- evidence: `TREE_OPERATIONS` + `SUBTREE_OPERATIONS` +
        `treeCommandFaults(command, tree)`. A MOVE NEEDS TWO EXPECTED REVISIONS:
        one for the node and one for the DESTINATION, because the parent can
        move, be archived, or change what it accepts between the read and the
        write. It also needs two authorizations — a move is a write to where it
        is going as much as to what is moving — and `move-authorized-only-on-the-
        node` is the one every implementation misses. Every operation that
        changes a subtree must be atomic over it: half a reparent is a shape
        nobody designed and no screen can explain. A detach to the root is
        called out separately, because promoting a subtree is a different act
        with different authorization. -->
  - [x] S7.9.c Prevent self-parent, descendant-parent, cycle through concurrent
        updates, duplicate/order corruption, invalid root, and depth/fan-out
        overflow. Evidence: property and real-DB race tests.
        <!-- evidence: `treeIntegrityFaults(tree, contract)` +
        `moveWouldCycle` + `proposedMoveFaults`, driven by PROPERTY tests over
        generated trees and by a REAL PostgreSQL. The property tests build trees
        by only ever attaching to an existing node — so a cycle is impossible by
        construction and anything reported is the finder being wrong — then
        redirect every node to each of its own descendants and require a finding,
        then check the cheap pre-flight rule against the expensive whole-tree
        walk over EVERY PAIR of nodes (roughly 5,700 of them). THE DATABASE
        FINDING IS THE ITEM: session A moves `a` under `b` while session B moves
        `b` under `a`; each validates against a snapshot in which the other has
        not happened, each is correct, both commit, and the committed tree has a
        cycle NEITHER OF THEM CREATED. REPEATABLE READ does not help — a
        stronger snapshot changes what each transaction sees and neither was
        wrong about what it saw. SERIALIZABLE does, because each read the row
        the other wrote (unlike S6.11, where two writes were merely a
        dependency), and so does one lock taken on the tree BEFORE deciding.
        CONTROL 6 shows that locking the destination path is not the same thing:
        the node being moved is not on it, so the recipe either leaves the cycle
        or deadlocks. -->
  - [x] S7.9.d Provide tree keyboard pattern plus explicit move before/after/
        into/out, numeric position, and target picker equivalent to drag.
        Evidence: keyboard/screen-reader tests.
        <!-- evidence: `treeKeyboardFaults` and browser CONTROLs 1–5. THE
        PLACEMENTS ARE TYPED COMMANDS, not a simulated drag: before, after,
        into, out and a numeric position each have their own control, and the
        component has no drag at all — the pointer path is the same dialog, so
        the two cannot diverge. CONTROL 2 presses each of the seven keys the
        role obliges FROM A ROW WHERE IT HAS SOMEWHERE TO GO (ArrowUp and Home
        at the first row are no-ops by definition, which would have made two of
        the seven vacuous) and asserts the tree state changed. CONTROL 3 walks
        Tab from a control before the tree to one after it and finds at most one
        stop inside — the single-tab-stop rule, which is what stops a large
        hierarchy being a keyboard trap in practice. CONTROL 4 expands the tree
        past its scroller and types "zoo" to reach Zoology, which is off screen.
        The destination list comes from the kit's walk, so CONTROL 5 finds it
        excludes the node and its descendants and still offers everything
        else. -->
  - [x] S7.9.e Preview affected descendants, inherited policy/taxonomy,
        references, permissions, gates, schedules, URLs, and destructive or cost
        effects. Evidence: impact fixtures.
        <!-- evidence: `IMPACT_DIMENSIONS` + `impactPreviewFaults`. Nine
        dimensions, every one of them invisible in a tree control and every one
        of them travelling with the node. A dimension nobody COMPUTED is a
        separate finding from one nobody SHOWED, because silence reads as
        "nothing happens". The count is refused when it comes from the rendered
        tree — collapsing a branch would make a move look smaller than it is —
        and an irreversible consequence must be stated AND must cost more than
        one click, because the tree is the surface people click fastest. The
        preview is timestamped and expires, so one computed when a branch was
        expanded is not shown for a move made later. -->
  - [x] S7.9.f Handle lazy/virtualized nodes, loading/unknown child counts,
        remote edits, moved/deleted target, conflict, partial failure, and
        rollback. Evidence: state matrix.
        <!-- evidence: `TREE_STATES` + `treeStateFaults`. AN UNKNOWN CHILD COUNT
        IS NOT ZERO: a lazily loaded node whose children have not been fetched
        renders identically to a leaf, and somebody archives a branch believing
        it is empty — so the unknown state is its own presentation AND the
        subtree actions are withheld until the subtree is known, because the
        preview that would make them safe cannot be computed. A partial failure
        must be rolled back OR itemized per node, and the per-node outcomes must
        cover the request. A destination that MOVED is followable and one that
        was DELETED is not; rendering both as an error makes the recoverable
        case look permanent. -->
  - [x] S7.9.g Provide semantic list/breadcrumb/search and preserve focus,
        expansion, selection, and scroll through operations. Evidence:
        accessible journey.
        <!-- evidence: `treeJourneyFaults` and browser CONTROLs 6–8. THE
        BROWSER TAUGHT THE RULE: preserving a raw `scrollTop` across a move is
        NOT preserving the reader's place, because the move inserts rows above
        it — restoring the number leaves them four rows from where they were,
        and the browser's own scroll anchoring is meanwhile trying to do the
        same job from the other side, so the two together land a row out. The
        component owns it (`overflow-anchor: none`) and restores by NODE and
        offset; CONTROL 6 measures the node the reader was looking at moving
        5px while `scrollTop` moved 96px. Focus goes back to the moved node by
        identity, and it is applied AFTER the modal closes, because closing a
        dialog hands focus back to whatever opened it and would otherwise undo
        the restoration silently. CONTROL 8 reads the list view: the PATH is a
        column, which is what the indentation was for, and a move can be started
        from it. -->
        <!-- S7.9 rollup: 5043 kit tests (41 new, 102nd entry point) + TWO
        tools. `hierarchy-cycle-race.mjs` (6 CONTROLs, 1 FAULT) against real
        PostgreSQL 16.14, stable over three consecutive runs;
        `hierarchy-tree-browser.mjs` (8 CONTROLs, 2 FAULTs) in real Chromium.
        Verified by patching the BUILT dist twice. Six kit gates, `eslint` and a
        non-vacuous `tsc` clean. Registration adds the `hierarchy-cycle-race`
        and `hierarchy-tree-browser` targets. -->
- [x] S7.10 Build graph viewing with semantic list/table parity. Evidence:
      graph-only information is zero.
      <!-- The parent states its own acceptance in five words, and it is a much
      harder claim than "there is also a table". So `graph-views.ts` (103rd entry
      point, 110 tests) takes the PICTURE and the SEMANTIC VIEW as two separate
      arguments in every parity rule and compares them: a derivation cannot
      disagree, and finding the disagreement is the entire job. All eight
      children complete. -->
      <!-- THREE ARTIFACTS. The kit module; `GraphView.tsx` +
      `GraphView.module.css` rendering the same model twice from one state; and
      `apps/oshun/web/tools/graph-view-browser.mjs`, 43 controls in real
      Chromium. The browser is not optional here for a reason no other S7 item
      had: whether the layout carries meaning can ONLY be settled by laying the
      same graph out twice and finding that nothing the view SAYS changed, and
      nothing runs in jsdom. Verified by patching the BUILT dist twice —
      RAISING the headroom floor above what the machine measures, and silencing
      the label comparison — each failing a measured control. -->
      <!-- THE SECOND PATCH DID NOT FAIL UNTIL THE TOOL EARNED IT. Every control
      passed over a surface that is CORRECT, so the parity rules never reached
      their reporting branches and silencing one changed nothing. The tool now
      also runs each rule over an input broken in the way it exists to catch,
      built from what the browser rendered. Same lesson as S7.3, S7.4 and S7.7,
      and the sharpest statement of it yet: a dist patch is only detectable if
      some control DRIVES THE FAILING CASE, not merely if it imports the
      symbol. -->
  - [x] S7.10.a Register typed node/edge/port/property/direction/version,
        group/hierarchy, status, source, and authorization projection. Evidence:
        graph view contract.
        <!-- evidence: `graphContractFaults` in
        `libs/oshun/workbench-kit/src/graph-views.ts`. The rule worth having is
        AN EDGE MAY NEVER BE MORE VISIBLE THAN ITS ENDPOINTS: if the node type is
        CONCEALED from a reader who may not see it and the edge is merely DENIED,
        the refusal to show the edge admits the node exists, which is exactly the
        disclosure the concealment was bought to prevent. Composed from S4's
        dispositions rather than re-decided. Alongside it: a directed edge must
        leave an `out` port and arrive at an `in` one or the arrowhead draws the
        opposite dependency; an undirected type whose two ends accept different
        node types is directed in disguise; and every status vocabulary must be
        able to say `unknown`, because a vocabulary that cannot spells it as
        absence and absence renders identically to "fine" (S7.1). -->
  - [x] S7.10.b Render bounded
        pan/zoom/fit/minimap/search/filter/highlight/path/ impact layout without
        making spatial position the sole meaning. Evidence: visual interaction
        tests.
        <!-- evidence: `viewportFaults` + `spatialMeaningFaults`, and CONTROL 2
        and CONTROL 3 of the browser tool. THE ZOOM BOUNDS ARE DERIVED, NOT
        CHOSEN: the floor is `MIN_NODE_TARGET_PX / NODE_DIAMETER_PX` = 0.25,
        where a node stops being a 24px pointer target, and the ceiling is
        integer division of the 640px narrow breakpoint by the node diameter =
        6, where one node stops fitting the narrowest supported viewport. The
        browser clicks the controls until they stop and checks where they
        stopped. "Position is not the sole meaning" is settled by RUNNING THE
        LAYOUT TWICE under different seeds and comparing everything the surface
        states, character for character and in order — a listing whose ORDER came
        out of the simulation is its own finding, and a run in which nothing
        moved is reported too, because it settles nothing. -->
  - [x] S7.10.c Expose nodes as navigable table/tree with identical labels,
        types, properties, status, groups, inbound/outbound counts, and details.
        Evidence: node parity tests.
        <!-- evidence: `nodeParityFaults` + `degreeOf`, and CONTROL 1 and CONTROL
        7. The comparison is CHARACTER FOR CHARACTER, not "equivalent": a picture
        reading `Ingest » v3` beside a table reading `Ingest (version 3)` is two
        names for one object and a reader moving between the surfaces cannot tell
        they are the same thing. A DEGREE IS THREE NUMBERS — visible, withheld,
        and whether it is known at all — because a count that includes an
        authorization-removed neighbour leaks its existence while a count that
        silently omits it renders a node with hidden neighbours identically to a
        leaf. A CONCEAL contributes to none of them and a DENY contributes to
        `withheld`, so "and three more you may not read" is sayable and "there is
        a fourth service" is not. An unfetched neighbourhood reads `unknown`
        rather than 0 (S7.9's unknown-child-count, one dimension up and worse: a
        graph loads a neighbourhood at a time, so most of the view is
        legitimately unfetched and every one of those nodes draws as an
        isolate). -->
  - [x] S7.10.d Expose edges as navigable table/list with source, target, type,
        direction, version, status, and properties. Evidence: edge parity tests.
        <!-- evidence: `edgeParityFaults`. An arrowhead that disagrees with the
        listed direction is the sharpest one — which way a dependency runs is the
        single fact an edge exists to state — and it is driven in the browser by
        flipping the arrow on what was rendered and confirming the built kit
        catches it. A versioned edge type whose row carries no version says this
        points at the target rather than at one particular target. An edge to a
        node the projection does not hold is a DISCLOSURE, not a rendering bug:
        the line is then the only evidence the node exists, which is precisely
        what removing it was for. An endpoint named by id where the node table
        calls it something else makes the two lists impossible to cross-reference
        by eye. -->
  - [x] S7.10.e Synchronize selection, focus, filters, path/neighbor expansion,
        inspector, and deep link bidirectionally. Evidence: synchronization
        tests.
        <!-- evidence: `synchronizationFaults`, and CONTROL 4 and CONTROL 5.
        There is no synchronization in the component at all, which is the point:
        ONE state and two renderers, because two states with a sync between them
        is a race and the reader sees whichever won. The kit rule exists to catch
        an implementation that did it the other way. A DEEP LINK CARRIES THE
        SELECTION AND NEVER THE VIEWPORT — a pan and a zoom are where one
        reader's simulation happened to settle, so a shared link would land the
        recipient on a different layout of a graph that has since moved (S7.9's
        "a scroll position is not a place", one dimension up). Keys are an
        ALLOW-LIST per S7.7. A selection the filter removed is COUNTED rather
        than dropped, or a bulk action reaches things nobody can see. -->
  - [x] S7.10.f Provide keyboard/nonpointer traversal and explicit relationship
        operations where editable. Evidence: nonvisual journey.
        <!-- evidence: `traversalFaults` + `relationshipCommandFaults`, and
        CONTROL 6. THE KEYBOARD PATH THROUGH A GRAPH IS THE TABLE, NOT THE
        PICTURE, and that is a finding rather than a shortcut: there is no
        accessible traversal of a force-directed layout because "the node to the
        right" is a fact about the simulation. So the obligation is discharged
        over the semantic view — the browser tabs in and arrows down until every
        node row has been reached, presses Home and End, and selects with Enter.
        `relationshipCommandFaults` refuses an endpoint type the contract
        forbids, a port that does not exist, a parallel edge where the type
        forbids one, a versioned edge with no version and an unversioned one
        carrying one, a write with no expected revision, and a command that can
        only be AIMED rather than typed. -->
  - [x] S7.10.g Handle huge/partial/cyclic/disconnected/permission-redacted/
        stale graphs with honest counts and no layout denial of service.
        Evidence: resilience/performance tests.
        <!-- evidence: `resilienceFaults` + `neighbourhood` + `componentsOf`, and
        CONTROL 8 and CONTROL 9. A CYCLE IS A LEGITIMATE GRAPH HERE — the exact
        opposite of S7.9, where a cycle was the bug — so every walk terminates on
        one and SAYS it came back round rather than reporting a short path. "No
        layout denial of service" is a DEGRADATION, not a cap: past the ceiling
        the picture is not drawn and the tables are untouched, which is only
        defensible BECAUSE parity is total, so the budget and the parity claim
        justify each other and a degradation that also thins the table is the
        named failure. The browser measures the real shipped layout at the
        ceiling and at twice it, and the counts are read out of the rendered
        header: the authorized total (not what survived the layout), the
        component count, and the age. -->
        <!-- THE CROSSING POINT IS A PROPERTY OF THE MACHINE, NOT OF THE CODE.
        The obvious control — "the layout fits the budget at the ceiling and
        exceeds it at twice the ceiling" — is measurable and FALSE on a fast
        development box while the ceiling is still right; the crossing simply
        happens further out. Picking the ceiling from wherever the fastest
        machine crosses is picking it from the machine least in need of
        protection. So the ceiling is stated against two machine-independent
        things instead: the SHAPE of the curve (quadratic, so doubling the nodes
        must cost markedly more than twice as much), and the HEADROOM
        (`LAYOUT_HEADROOM_FACTOR`, how much slower a reader's machine may be and
        still fit). Measured 164ms at 1500 nodes here, 6.1x headroom against a
        declared floor of 4x, budget first exceeded at 4x the ceiling. Take the
        MINIMUM of several runs: scheduler noise only ever ADDS time, so the
        minimum estimates the capability and the mean measures the load — two of
        these tools at once took the same layout from 194ms to 356ms and failed
        the check over nothing. -->
  - [x] S7.10.h Compare every displayed visual encoding/fact/action to the
        semantic view and report zero graph-only information. Evidence: parity
        audit.
        <!-- evidence: `parityAudit` returning `graphOnlyFacts` and
        `graphOnlyActions`, and CONTROL 10 — which reads the encodings the
        surface ACTUALLY renders out of computed style and geometry (are the
        fills all the same? the stroke widths? the radii?) rather than out of a
        list somebody maintained, because the audit is only as complete as its
        inventory and an encoding absent from the declaration is itself a
        finding. An encoding declared DECORATION whose rendering varies with the
        data is the worse case: information whose author denies it exists, so
        nobody ever gives it a column. -->
        <!-- POSITION IS ITS OWN CASE, AND WRITING THIS FUNCTION IS WHAT SHOWED
        IT. Decisions 1 and 2 read like one rule and are not. Every other
        encoding is either decoration (and therefore constant) or a fact (and
        therefore mapped to a column) — but a layout ALWAYS varies with the data,
        moving nodes according to the edges being the entire job, and it can
        never have a column because a coordinate states nothing. Run through the
        general rule, position makes every honest surface fail as "decoration
        that varies with the data"; nine of the first ten tests written here
        failed on exactly that. So `parityAudit` takes the layout-independence
        VERDICT as an argument, and an audit made without one reports
        `position-not-settled-by-a-layout-run` rather than passing. A `position`
        pointed at an `x` column is refused separately: it satisfies the parity
        check while stating nothing a reader can use. -->
- [x] S7.11 Build timeline editing with keyboard/timecode controls and semantic
      track list parity. Evidence: trim, move, split, and zoom journeys.
      <!-- THE MIRROR OF S7.10, AND THAT IS THE WHOLE DESIGN. There, spatial
      position was the one encoding that could never have a column, because a
      layout coordinate states nothing. Here the horizontal axis IS the data: a
      clip drawn eight pixels right of its own timecode has told the eye a
      different time from the one the field prints, and at 100 px/s that is a
      fifth of a second no amount of source-reading finds. So `timelines.ts`
      (104th entry point, 146 tests) takes the MEASURED picture and the RENDERED
      table as two separate arguments and compares them, and the browser tool
      measures the boxes rather than reading the style strings back. All eight
      children complete. -->
      <!-- THREE ARTIFACTS. The kit module; `TimelineEditor.tsx` +
      `TimelineEditor.module.css` rendering one state twice with 36 jsdom specs;
      and `apps/oshun/web/tools/timeline-editor-browser.mjs`, 71 controls in real
      Chromium against a real `node:http` server. Verified by patching the BUILT
      dist three times — silencing the position comparison, NARROWING the
      link-key allow-list, and RAISING the frame-target floor above what the
      machine renders — each failing a different measured control, and the tool
      green again once the dist was restored. -->
      <!-- THE BROWSER FOUND FOUR DEFECTS SOURCE-READING DID NOT. (1) The
      semantic table's `nowrap` timecode cells pushed the whole PAGE sideways at
      360px and 640px, because a flex item defaults to `min-width: auto` and
      refuses to shrink below its content. (2) `max-width: 100%` resolves against
      the CONTENT box, so a region with a 1px border lands at 102%. (3) A
      VISUALLY-HIDDEN SPAN IS `position: absolute` AT ITS STATIC POSITION, so
      without a positioned ancestor it escapes the scroll container its own
      content lives in — the table scrolled correctly the whole time and the
      document scrolled anyway, by the width of a box nobody can see. (4) A state
      cell reading "unlocked" makes `includes('locked')` true, so any consumer
      matching on substrings reports every open clip as locked. -->
  - [x] S7.11.a Define time base, frame/sample/beat units, frame rate/sample
        rate/ tempo map, rounding, origin, duration, tracks/lanes, clips/events,
        and stable IDs. Evidence: timeline contract.
        <!-- evidence: `timelineContractFaults` in
        `libs/oshun/workbench-kit/src/timelines.ts`. THE DECISION EVERYTHING ELSE
        RESTS ON: time is an exact integer in a base that divides every rate,
        never a float of seconds. 29.97 is not a frame rate; 30000/1001 is, and a
        float second loses a frame roughly every half hour of NTSC — invisible
        until two departments' exports disagree. The base is the FLICK,
        705,600,000 per second, the unit published in 2018 for exactly this
        problem, because it is an exact integer multiple of every broadcast frame
        period INCLUDING the /1001 rates and of every standard sample period from
        8 kHz to 192 kHz. Asserted: 23,543,520 flicks x 30,000 is 1001 seconds to
        the flick. Alongside it: the exact-arithmetic ceiling is stated and a
        longer document is refused rather than silently losing its last digits; a
        rate the base does not divide returns `null` rather than a rounded
        answer; and an identity derived from position or label is refused,
        because a move is not an exceptional event on this surface, it is the
        product. -->
  - [x] S7.11.b Implement keyboard transport, seek, step, selection, range,
        zoom/fit, marker, track, clip/event traversal, and shortcut collision
        handling. Evidence: key map tests.
        <!-- evidence: `keyMapFaults`, `resolveChord`, `DEFAULT_KEY_BINDINGS`,
        and CONTROLS 4, 5, 7 and 8. A SINGLE-CHARACTER SHORTCUT IS A TYPING
        HAZARD, and the rule is wider than a bare letter: Shift does not rescue
        it, because Shift+ArrowLeft extends a text selection and Backspace is
        what somebody fixing a typo reaches for first. So the hazard is every
        chord with no Control, Alt or Meta, and each must declare itself
        suppressed while a field has focus. The shipped map PASSES ITS OWN
        CHECKER — a default map its rule rejects is a rule nothing has ever run —
        and it keeps J-K-L and I/O because those are the conventions of the
        domain. The reserved list COMPOSES S7.1's rather than restating it: a
        timeline shortcut that closes the browser tab is exactly as bad as one
        that traps focus. The zoom bounds are DERIVED — the floor is the whole
        timeline in the narrowest supported canvas, the ceiling is one frame
        filling it — and the browser clicks until they stop and checks where. -->
  - [x] S7.11.c Provide exact start/end/in/out/duration/offset/speed/value
        fields with unit conversion, validation, snapping, and accessible
        labels. Evidence: timecode control tests.
        <!-- evidence: `timecodeFieldFaults`, `parseTimecode`,
        `framesToTimecode`, `applySnap`, and CONTROLS 9 and 15. DROP FRAME IS A
        LABELLING SCHEME, NOT A RATE: no frames are dropped, two frame NUMBERS
        are, at the top of every minute except every tenth. Two consequences the
        code carries — a drop-frame and a non-drop-frame timeline over the same
        media have IDENTICAL durations, and `00:01:00;00` is not a time but a
        typo a lenient parser turns into a one-frame error nobody ever finds,
        because the number it prints back is not the number that was typed. The
        SMPTE known values are pinned (frame 1800 prints `00:01:00;02`, 17982
        prints `00:10:00;00`, 17981 prints `00:09:59;29`, and the 59.94 pair) and
        every frame number across eleven minutes round-trips. AN END TIMECODE IS
        AMBIGUOUS UNTIL THE VIEW SAYS WHICH END IT MEANS — last frame or first
        frame after — so a field declares which, and one that declares neither is
        the finding. A field that snaps must SAY it snapped, so `applySnap`
        returns the target and the distance. -->
  - [x] S7.11.d Implement move, trim, split, merge, duplicate, ripple/overwrite,
        reorder, enable/mute/lock, and domain plugin operations with undo.
        Evidence: operation known-value tests.
        <!-- evidence: `applyOperation` returning its own inverse, plus
        `overlapFaults` and `desynchronisedLinks`, and CONTROL 7. A CLIP HAS
        THREE TIMES AND A SPEED, and conflating any two of them scrubs the media:
        a MOVE changes `start` alone, a HEAD TRIM changes `start`, `duration` AND
        `sourceIn`, and the source it consumes is the timeline delta multiplied
        by the SPEED. Forgetting that factor is the single most common real
        defect in this domain and it is a known-value test — a split at four
        seconds into a 2x clip advances the right piece by eight seconds of
        source, not four. The inverse is produced by the operation rather than
        reconstructed by a stack, because only the operation knows what it
        actually did; an overwrite that destroyed material reports that it has NO
        single-operation inverse rather than offering a wrong one. A LINK IS
        BROKEN BY A RIPPLE THAT CROSSES IT — the audio under a shot the picture
        track rippled past comes out misaligned by exactly the rippled amount —
        so the result names the groups it pulled apart, compared BEFORE against
        AFTER so a group already out of sync is not blamed on this edit. -->
  - [x] S7.11.e Provide track/event table/list with every identity, time/value,
        status, relationship, and operation available nonvisually. Evidence:
        semantic parity tests.
        <!-- evidence: `timelineParityFaults`, and CONTROLS 1, 2, 3 and 17. The
        comparison is between a MEASURED box and a PRINTED timecode: the browser
        reads `getBoundingClientRect` against the scrolling content box rather
        than reading the component's own `style.left` back, because comparing a
        model to itself settles nothing. Every rule then gets a NEGATIVE control
        over an input broken the way it exists to catch, built from what was
        rendered — a clip nudged 8px, a width shrunk a quarter, a label with a
        trailing space, a row removed, a drawn clip removed, a link the picture
        shows and the row omits. THE ASYMMETRY RUNS ONE WAY: the picture may show
        less than the table, because a clip scrolled out of the canvas is
        legitimately undrawn, and never more — so a row inside the VISIBLE window
        with nothing drawn for it is its own finding. A row that offers nothing
        is not a fault; a row that offers nothing AND does not say which of the
        three reasons it is — read-only track, locked item, not yours — renders a
        protected item identically to a broken control. -->
  - [x] S7.11.f Synchronize playhead/range/selection/zoom/filter/inspector
        between visual and semantic views. Evidence: synchronization tests.
        <!-- evidence: `synchronizationFaults`, and CONTROLS 11 and 12. There is
        no synchronization in the component at all, which is the point: ONE state
        and two renderers, because two states with a sync between them is a race
        and the reader sees whichever won. The kit rule exists to catch an
        implementation that did it the other way. A DEEP LINK CARRIES A TIME AND
        NEVER A VIEWPORT — the mirror of S7.10's rule rather than a contradiction
        of it: a playhead and an in/out range are positions in the DATA and worth
        sending to somebody, while the zoom and the horizontal scroll are facts
        about how wide the sender's monitor was. Keys are an ALLOW-LIST per S7.7,
        and the browser hands the keys the page REALLY wrote to the built kit.
        READING THE TABLE DOES NOT SEEK: arrowing down the semantic list is how a
        nonvisual reader inspects a timeline, and if it also moved the playhead
        they could not read without editing what everybody else is watching. The
        filter EVALUATES the parsed tree and refuses an unknown field BY NAME —
        S7.10's lesson, since a filter that collects terms contributes none for a
        comparison and `[].every()` is `true`, so the box silently shows
        everything on exactly the input somebody typed most deliberately. -->
  - [x] S7.11.g Handle variable frame rate/tempo, drop frame, long duration,
        overlaps, boundaries, locked/unauthorized items, remote edits, and
        conflicts. Evidence: boundary fixture matrix.
        <!-- evidence: `boundaryFaults`, `durationPrintFaults`, `beatAtFlicks` /
        `flicksAtBeat` / `barBeatAtFlicks`, and CONTROLS 10, 15, 16 and 17. ROUND
        FROM THE SEGMENT ORIGIN, NEVER FROM THE PREVIOUS BEAT: most tempi have no
        whole-flick beat, and rounding an increment and adding it drifts linearly
        while rounding the whole offset does not accumulate at all — asserted by
        showing the naive error growing tenfold with the beat count while ours
        stays inside half a flick. A BAR NUMBER ACCUMULATES ACROSS METER CHANGES,
        which the first bar test caught as a real bug: numbering from the current
        segment's meter alone renumbers every bar in the piece the moment
        somebody inserts a meter change. A DURATION IS A COUNT AND A POSITION IS
        A WALL CLOCK, so a span printed through the drop-frame label scheme reads
        ten minutes as nine fifty-nine. AN OVERLAP IS A TRACK PROPERTY, not an
        error — checked against the lanes the track declared. A timeline past the
        24-hour wrap is refused because two instants in it print the same label.
        A CONCEAL is absent from the document the view is built from and a DENY
        is counted, so "and one you may not read" is sayable and "there is a
        fourth clip" is not. -->
  - [x] S7.11.h Run trim, move, split, zoom, undo, autosave, conflict, and
        nonpointer journeys under supported viewports. Evidence: Playwright
        tests.
        <!-- evidence: `apps/oshun/web/tools/timeline-editor-browser.mjs`, 71
        controls, Nx target `timeline-editor-browser`. The whole trim-split-undo
        journey runs from the harness control BEFORE the timeline with ZERO
        pointer events, and each step is checked in BOTH surfaces: the two halves
        of a split are drawn edge to edge with no gap and no overlap, and a head
        trim moves the drawn left edge by exactly the pixels that time maps to.
        THE TYPING HAZARD IS DRIVEN FOR REAL — `00:00:03:00` typed into the
        timecode field, then two ArrowLefts and a Backspace, and nothing split,
        nothing deleted, the playhead unmoved and the caret where a field owes
        its reader. The conflict journey uses a real `node:http` server the page
        really polls: the request count proves it is watching rather than having
        watched once, the remote value is one only the server knows, and undo
        then stops at the fence rather than re-applying a state the server never
        had. Measured at 360px, 640px and 1280px — where the page must not scroll
        sideways while the canvas and the table each must. -->
- [x] S7.12 Build comparison surfaces for text, metadata, images, video, audio,
      3D, graphs, and gate results through typed adapters. Evidence: each
      adapter has a golden comparison fixture.
      <!-- S2.5 ALREADY DECIDED WHAT A DIFFERENCE IS, and nothing here re-decides
      it: every adapter emits `DiffOperation`s, because one contract is the only
      thing that makes the review surface one surface. `comparisons.ts` (105th
      entry point, 99 tests) adds the eight typed adapters and the one claim that
      makes any of them falsifiable — a GOLDEN is a recorded input PAIR plus the
      recorded answer plus the adapter version it was taken under. All ten
      children complete. -->
      <!-- THREE ARTIFACTS. The kit module and its eight goldens in
      `evidence/v1-workbenches/workbench-kit-comparison-goldens.json`, recorded
      over fixtures that live beside the spec reproducing them; `CompareSurface.tsx`
      rendering any outcome with 26 jsdom specs; and
      `apps/oshun/web/tools/compare-surface-browser.mjs`, 44 controls in real
      Chromium. Verified by patching the BUILT dist twice — silencing the cursor
      repeat detector and emptying `SURVIVING_ANCHOR_TARGETS` — each failing a
      measured control, and the tool green again once restored. -->
      <!-- THE FIRST PATCH DID NOT FAIL UNTIL THE TOOL EARNED IT, the fourth
      sighting of S7.10's lesson. Silencing the repeat detector changed nothing
      because none of the six negative controls DROVE a repeat; adding one made
      the same patch fail. The browser also found a real focus defect and a real
      gap in two adapters, both described in the children below. -->
  - [x] S7.12.a Define common compare contract for left/right/base identity and
        revision, adapter/version, aligned change list, selection, filters,
        unknown/unsupported/redacted state, and accessible summary. Evidence:
        compare interface.
        <!-- evidence: `compareContractFaults`, `accessibleSummaryOf`,
        `goldenFaults` and `goldenRegistryFaults` in
        `libs/oshun/workbench-kit/src/comparisons.ts`. A GOLDEN WITHOUT THE
        ADAPTER VERSION PINS NOTHING: the same inputs through a later adapter
        legitimately produce a different answer, so a golden that cannot say
        which version it was taken under is either failing forever or being
        quietly re-recorded, and both make it a test nobody can fail. A version
        mismatch is therefore reported as UNTAKEN rather than failed. Alongside
        it: TWO REVISIONS ANSWER "WHAT IS DIFFERENT" AND THREE ANSWER "WHO
        CHANGED IT", so a merge control offered over a two-way comparison asks
        for a choice without the fact that decides it; a redaction declares
        whether the FACT of the change is itself the secret, because usually
        "changed, value withheld" is the useful sentence and sometimes the one
        bit is what the redaction was bought to protect; and the accessible
        sentence is written in exactly one place, or the nine surfaces would say
        the same thing nine ways. -->
  - [x] S7.12.b Implement text adapter for block/line/word/character and
        semantic structured changes with whitespace/locale policy. Evidence:
        text goldens.
        <!-- evidence: `compareText`, `graphemes`, `prepareText`, and the
        `text/one-line-rewritten` golden. A CHARACTER DIFF OPERATES ON GRAPHEME
        CLUSTERS, NOT CODE UNITS — a family emoji is one grapheme, five code
        points and eight code units, and splitting it renders as mojibake, from
        which the reader correctly concludes the tool is broken. NORMALIZE AND
        SAY THAT YOU DID: `é` composed and decomposed are the same text and
        different bytes, so comparing unnormalized reports a change nobody made
        while normalizing silently hides a real round-trip loss — both sides get
        reported. Case folding is LOCALE-DEPENDENT and asserted on the Turkish
        dotless i, and folding with no locale is refused BY NAME rather than
        picking somebody else's language. The alignment is S2.5's own
        `longestCommonSubsequence` rather than a second implementation. -->
  - [x] S7.12.c Implement typed metadata adapter for field presence/value/unit/
        enum/list/relationship changes with redaction. Evidence: metadata
        goldens.
        <!-- evidence: `compareMetadata`, `convertQuantity`, and the
        `metadata/runtime-restated-and-rating-raised` golden — which is the whole
        argument in one fixture: 90 minutes rewritten as 5400 seconds is ONE
        PRESENTATION change and the rating that really moved is one semantic one.
        A comparison without unit conversion makes every unit migration look like
        a data migration. A SET HAS NO ORDER, so reporting one reordered is
        reporting a change that does not exist; a SEQUENCE reordered is real, and
        checking membership BEFORE order is what keeps "somebody moved chapter 3"
        from rendering as "somebody rewrote the chapter list" — a real bug the
        tests caught. Absent, null and default stay three different facts (S2.5's
        rule, kept), and a reference RETARGETED is a different change from its
        target having MOVED, because the two send the reader to different
        places. -->
  - [x] S7.12.d Implement image adapter for bytes/hash/dimensions/color/profile/
        metadata plus aligned overlay/swipe/pixel/perceptual differences and
        nonvisual metrics. Evidence: image goldens.
        <!-- evidence: `imageMetrics`, `meanStructuralSimilarity`, `compareImages`
        and the `image/one-pixel-and-a-profile` golden. A PIXEL DIFF OF ZERO DOES
        NOT MEAN THE IMAGES ARE THE SAME: colour space, ICC profile, bit depth
        and EXIF orientation all survive a pixel-identical comparison and at
        least one of them changes what a viewer sees, so the envelope is compared
        separately and "identical pixels, different profile" is its own finding.
        EVERY VISUAL COMPARISON OWES A NUMBER — a swipe and an overlay are two
        ways of showing a difference to somebody who can see it, and the
        changed-pixel count, the peak channel delta, the PSNR and the bounding
        box are what a reviewer says out loud. PSNR is `10 log10(255^2/MSE)`
        asserted at a known 28.13 dB and INFINITE for identical images rather
        than capped; SSIM is Wang et al. over 8x8 windows, asserted at a known
        0.99548 over a uniform shift that changed every one of 64 pixels — which
        is the demonstration that a pixel count and a structure measure are
        different questions. -->
  - [x] S7.12.e Implement video adapter for stream/codec/duration/frame rate/
        tracks/metadata and time-aligned visual/audio/event differences.
        Evidence: video goldens.
        <!-- evidence: `compareVideo` and the `video/conformed-to-25` golden.
        THE ALIGNMENT IS BY TIME, NEVER BY FRAME INDEX: frame 100 of a 24 fps
        master and frame 100 of a 25 fps conform are 4.167 s and 4.000 s, so a
        per-frame comparison indexed by number compares unrelated pictures and
        reports the whole film as changed. The same decision S7.11 made about a
        timeline, and the golden is the case that proves it — the same 240 frames
        at two rates, where the count is identical and the film is not. Duration
        is computed from the EXACT rational rate, so 30,000 frames at 30000/1001
        is exactly 1001 seconds. A sampled frame with no counterpart at that time
        is named in `notCompared` rather than skipped, and a track set with no
        shared id says so rather than guessing which track became which. -->
  - [x] S7.12.f Implement audio adapter for bytes/format/duration/channels/
        loudness/peak/waveform/spectrum/events and synchronized audition with
        text/data alternative. Evidence: audio goldens.
        <!-- evidence: `integratedLoudness`, `truePeakDbtp`, `rmsEnvelope`,
        `compareAudio` and the `audio/six-db-louder` golden. Real ITU-R BS.1770-4:
        the two published K-weighting stages, 400 ms blocks at 75% overlap, an
        absolute gate at -70 LUFS and a relative gate 10 LU below what survived
        it. THE COEFFICIENTS ARE ASSERTED AS DATA and the RESPONSE IS MEASURED —
        the measured curve reproduces the published one (flat through the
        midband, rolling off below 100 Hz, plateauing at the +4 dB shelf), which
        is what catches a transposed digit that typechecks and produces a meter
        confidently wrong. The rest are properties of the definition rather than
        a calibration figure somebody half-remembered: doubling every sample
        moves exactly 6.02 dB, an identical second channel exactly 3.01, a
        surround channel exactly 10 log10(1.41), an LFE exactly nothing, and
        digital silence is negative infinity rather than a large negative number.
        THE GATE IS DEMONSTRATED, not described: two seconds of tone followed by
        six of silence measures within half a dB of two seconds of tone alone,
        where an ungated meter would be six dB out. A SAMPLE PEAK IS NOT A PEAK,
        so the true peak is oversampled — and the interpolation UNDERSTATES it,
        which is documented rather than hidden. An audition with no numeric
        alternative is refused: listening is the whole comparison for a hearing
        reader and none of it for anybody else. -->
  - [x] S7.12.g Implement 3D adapter for stage/scene graph, geometry, topology,
        transforms, materials/textures, skeleton/animation, variants/refs, and
        OpenUSD validation. Evidence: 3D goldens.
        <!-- evidence: `compareStages`, `windingReversed`, `COMPOSITION_ARCS` and
        the `stage-3d/parent-moved-child-did-not` golden — a fixture whose entire
        content is the finding: the child's authored transform is identical, its
        world position changed because the parent moved, and attributing that to
        the child sends a modeller to edit a value nobody touched. The golden
        records ONE operation, on the parent. Alongside it: composition arc
        strength is USD's own LIVRPS order, and a CHANGED ARC WITH AN UNCHANGED
        VALUE IS A REAL CHANGE, because the same answer through a stronger arc
        survives edits the weaker one loses. REVERSED WINDING passes every count
        check and renders the mesh inside out, so it is detected face by face
        including rotation. A VARIANT SELECTION IS NOT A GEOMETRY EDIT — both
        variants still exist and somebody chose the other one. Transforms are not
        compared at all across differing up axes, and geometry is not compared
        across differing unit scales, because a point at 1.0 is a different place
        in each stage; both are named in `notCompared` rather than silently
        skipped. -->
  - [x] S7.12.h Implement graph adapter for node/edge/property/group/path and
        layout-versus-semantic changes. Evidence: graph goldens.
        <!-- evidence: `compareGraphs` and the `graph/tidied-and-one-rename`
        golden. S7.10 established that a coordinate states nothing; this is that
        rule one step on. A LAYOUT CHANGE IS NOT A GRAPH CHANGE, so somebody
        tidying a diagram produces ONE operation, marked presentation, carrying
        the count of nodes that moved — rather than forty-seven entries that make
        the review unreadable and train everybody to skim it. `layoutOnly` is the
        answer to "is there anything here at all", and it goes false the moment
        one label changes. The golden holds exactly that pair: two nodes moved and
        one renamed, one presentation change and one real one. -->
  - [x] S7.12.i Implement gate adapter for raw values/units, evaluator/corpus/
        model, policy/threshold, verdict, freshness, evidence, waiver, and
        remediation. Evidence: gate goldens.
        <!-- evidence: `compareGateRuns`, `verdictCauses` and the
        `gate/bar-raised-not-work-worsened` golden. A VERDICT THAT FLIPPED
        WITHOUT THE VALUE MOVING IS A POLICY CHANGE: "the build got worse", "we
        raised the bar" and "we changed how we measure" are three different
        conversations and only one is about the work, so a changed verdict is
        decomposed into value-driven, threshold-driven, evaluator-driven,
        corpus-driven and model-driven — EVERY cause that applies, not the first,
        because "we raised the bar AND the number got worse" is common and
        reporting one of them is how half a regression goes unnoticed. Two
        numbers from two different evaluators are not a trend, and drawing an
        arrow between them is the most common way a quality dashboard lies. THE
        SAME VERDICT OVER DIFFERENT EVIDENCE IS A DIFFERENT CLAIM, and a surface
        that renders only the verdict says nothing happened. `verdictCauses`
        returning EMPTY is the answer when nothing observable changed — an
        earlier version had a fallback that made the list never empty, so
        `verdict-change-with-no-cause` could never fire, which is the shape of
        every unwired gate. -->
  - [x] S7.12.j Provide stable difference navigation, side switching,
        synchronized context, comments/decision linkage, keyboard/screen-reader
        parity, and large-diff pagination across adapters. Evidence: common
        compare journeys.
        <!-- evidence: `compareNavigationFaults`, `linkageFaults`,
        `differenceCursor`, `differenceAnnouncement`, `CompareSurface.tsx`, and
        `apps/oshun/web/tools/compare-surface-browser.mjs` (Nx target
        `compare-surface-browser`). The word doing the work in the item is COMMON:
        the surface takes an outcome and knows nothing about what was compared, so
        the browser renders a text, a metadata and a gate comparison and asserts
        the navigation controls are character-for-character the same set. THE
        ORDER IS THE RESULT'S ORDER, ALWAYS, because every conversation about a
        review is conducted in ordinals, and pagination is a WINDOW onto that
        order — the page is DERIVED from where the cursor is, and the ordinals
        stay absolute so page two starts at eleven. A COMMENT HANGS ON A STABLE
        ANCHOR OR IT DOES NOT HANG: an operation id is built from a path, so it
        survives recomputing the comparison and not the next edit, and when a
        comparison carries no anchor the surface says so and offers no box rather
        than one that silently drifts. -->
        <!-- THE BROWSER FOUND TWO REAL DEFECTS. (1) FOCUS DIED AT EVERY PAGE
        BOUNDARY: `.focus()` called in the handler that moved the cursor targets a
        row that is not rendered until the page catches up, so focus fell to the
        document body and the next arrow key went nowhere — a keyboard reviewer
        stranded twelve differences into a sixty-difference review with no way
        back in except the mouse. Moved into an effect keyed on the page.
        (2) ONLY THE TEXT ADAPTER EMITTED ANCHORS, so a reviewer could comment on
        a sentence and not on a gate verdict; a metadata key and a gate id are
        both stable identity, and both adapters now anchor from the path each
        operation already carries, in ONE place rather than at each push. Also
        settled here and nowhere else: the two context panes really scroll
        together without the echo scrolling the first one back, the panes FOLLOW
        the cursor while a side switch deliberately does NOT move them, and the
        DOM holds one page of a review with 118 differences in it. -->
- [x] S7.13 Build the decision-record drawer. Evidence: selected and rejected
      variants remain linked to the decision.
      <!-- S2.7 ALREADY DECIDED WHAT A DECISION RECORD IS — its lifecycle stages,
      the options that lost, evidence pinned by hash, supersession that never
      edits. `decision-drawer.ts` (106th entry point, 50 tests) adds only the
      presentation, and the acceptance sentence is much harder than it reads: the
      hard word is REMAIN. A rejected option in the DOM behind a collapsed
      section is linked to the decision the way a footnote is linked to a book.
      All seven children complete. -->
      <!-- THREE ARTIFACTS. The kit module; `DecisionDrawer.tsx` rendering every
      option as a SIBLING with 28 jsdom specs; and
      `apps/oshun/web/tools/decision-drawer-browser.mjs`, 49 controls in real
      Chromium (Nx target `decision-drawer-browser`). Verified by patching the
      BUILT dist twice — silencing the reachability comparison and NARROWING
      `OUTCOME_STATES` — each failing a measured control, and green again once
      restored. The second patch changed nothing until a control DROVE the
      `retained-current` case; adding one earned it. Fifth sighting of S7.10's
      lesson. -->
      <!-- THE BROWSER FOUND A REAL DEFECT. S7.13.e asks for a round trip with
      the selection PRESERVED, and a real `goBack()` remounts the component: the
      selected option lived in React state alone, so a reader who went to check a
      number against its source came back to the top of the record with nothing
      selected. The selection now lives in the address bar (`replaceState`, so
      selecting is not a navigation) — S7.11's playhead rule, one surface on. -->
  - [x] S7.13.a Display decision identity/type/scope/status/owner/effective
        version/review date and affected objects/capabilities. Evidence: header
        fixtures.
        <!-- evidence: `drawerHeaderFaults` in
        `libs/oshun/workbench-kit/src/decision-drawer.ts`, and CONTROL 3. The
        header is taken as its OWN argument rather than derived from the record,
        for the reason every S7 parity rule takes two: a derivation cannot
        disagree, and finding the disagreement is the job. AN EFFECTIVE VERSION
        WITH NOTHING ATTACHED IS A NUMBER — "3" answers nothing and "from version
        3 of the ingest workbench" is the sentence somebody needed — so a bare
        one is refused separately from a wrong one. A STAGE RENDERED AS A WORD
        THAT IS NOT A STAGE ("live", "current", "done") leaves a reader unable to
        tell whether the decision binds anybody. And a header with no review
        statement is how a temporary exception becomes permanent without anybody
        deciding that: a date or a stated reason there is none, never blank. -->
  - [x] S7.13.b List every option/variant with stable ID, summary, provenance,
        score/evidence, status, and compare action. Evidence: option fixtures.
        <!-- evidence: `optionListFaults`, and CONTROL 1. Each field is refused
        by name because each absence reads as something else: no provenance and a
        thing somebody proposed is indistinguishable from a thing a tool
        generated, which are different arguments; no score STATEMENT and a blank
        cell reads as a score of nothing, where "not scored" is a fine answer;
        no compare action and "why not this one" — the question the record exists
        to answer — has no control that answers it. An option the reader may not
        see is COUNTED on screen (S7.10's degree and S7.11's track total, third
        application): a silent drop renders a decision with alternatives
        identically to one taken without any. -->
  - [x] S7.13.c Mark selected, rejected, retained-current, unavailable, and
        deferred options without hiding any considered option. Evidence: outcome
        state tests.
        <!-- evidence: `DRAWER_OPTION_STATES`, `STATE_FOR_STATUS`,
        `outcomeStateFaults` and `drawerStateOf`, and CONTROLS 1, 2 and 9. THE
        VOCABULARY MUST BE ABLE TO SAY "WE KEPT WHAT WE HAD" AND "IT WAS NOT ON
        OFFER", and S2.7's three statuses cannot: recording a stay-put as
        `chosen` loses that nothing changed, and recording a vendor who withdrew
        as `rejected` attributes to the team a judgement they never made. So five
        presentation states over three, each mapped to exactly one record status,
        and a rendered state that disagrees with the filed one is itself a fault —
        the drawer may translate and may not contradict. THE REACHABILITY RULE IS
        MEASURED, NOT ASSERTED: `stepsToReach` is counted in the browser (visible
        on load is zero, inside a closed disclosure is one), and a rejected option
        further away than the selected one is the finding. That is the difference
        between a record that answers the question and one that technically
        contains the answer. -->
  - [x] S7.13.d Display rationale, trade-offs, consequences, risks, actors/
        authority, reviews, timestamps, and immutable evidence refs. Evidence:
        detail tests.
        <!-- evidence: `decisionDetailFaults`. AN EMPTY RESIDUAL RISK IS A CLAIM,
        not an absence — it says "we say nothing is still wrong" — so rendering
        nothing at all lets the reader assume nobody looked, and the surface
        states the claim instead. A DELEGATED ACTOR IS TWO PEOPLE and the
        delegated AUTHORITY is a third fact: who was at the keyboard and whose
        authority the decision carries answer different questions, and only the
        second says whether it binds anybody. A REDACTED ACTOR IS NOT AN UNKNOWN
        ONE — somebody was there and their identity was removed for a stated
        cause. Every evidence reference must carry a version AND a digest, or it
        points at whatever that document says today, which is the failure pinning
        was bought to prevent. REDACTION APPLIES TO THE RATIONALE, NOT ONLY THE
        EVIDENCE: the reason can name the thing the evidence was redacted to
        protect, so redacting one and rendering the other spends the budget and
        keeps none of it. -->
  - [x] S7.13.e Navigate to compare/evidence/source and back with preserved
        selection/focus and authorization/redaction. Evidence: drawer journey.
        <!-- evidence: `drawerNavigationFaults`, and CONTROLS 4, 5 and 8. THE
        DEFECT THE BROWSER FOUND: a real `page.goBack()` remounts the component,
        and the selection lived in React state alone, so the reader who went to
        check a number came back to the top of the record. Now in the address bar
        via `replaceState` — selecting an option is not a navigation, and a Back
        button that stepped through every option somebody clicked would make the
        real journey unreachable. A CONCEALED DESTINATION IS NOT OFFERED AT ALL:
        a refused link admits it exists, which is the disclosure the concealment
        was bought to prevent (S7.10's edge rule, applied to a link). A DENIED one
        is also not offered, for the different reason that a control which
        refuses when pressed teaches its reader to expect refusals. -->
  - [x] S7.13.f Support propose/review/approve/reject/supersede/reverse only
        when authorized, with exact validation and audit. Evidence: mutation
        tests.
        <!-- evidence: `decisionMutationFaults`, `MUTATION_FROM_STAGES`, and
        CONTROL 7. AUTHORIZED, VALIDATED, AUDITED, IN THAT ORDER — and the stage
        list is part of the validation rather than a comment: `supersede` runs
        only from `effective`, because superseding a proposal is not a
        supersession, it is a withdrawal. The surface offers a control only where
        BOTH the authorization and the stage admit it, and the browser reads the
        offered set at three different stages to check. A refusal must carry the
        exact reason naming the stages it could have run from, because a refusal
        nobody can act on is a dead control with extra steps. An accepted
        mutation with no audit entry makes the archive unfalsifiable, and an
        audit entry for a REFUSED one records something that did not happen —
        both are named. A REVERSE THAT EDITS THE ORIGINAL is the worst of them:
        reversing a decision is a new decision, and editing the old one erases
        the reasoning of the day, which is the one thing the archive is for. -->
  - [x] S7.13.g Render missing/inaccessible/stale evidence and supersession
        lineage honestly. Evidence: degraded state fixtures.
        <!-- evidence: `degradedStateFaults`, and CONTROLS 6 and 8. EVIDENCE HAS
        FOUR RESOLUTIONS AND ONLY ONE IS FINE: resolved, missing, inaccessible to
        this reader, and stale — where stale means the digest no longer matches
        what the author read. Rendering all four as a link tells the reader the
        reasoning still rests on what it rested on, which is precisely what the
        surface cannot know, so only `resolved` is an anchor and the browser
        checks the degraded ones are not even in the tab order. Both directions
        are refused: calling it resolved when the digest moved, and calling it
        stale when nothing changed and somebody is sent to re-read an unchanged
        document. A SUPERSEDED RECORD STAYS READABLE — it is not a redirect, it
        renders its own options and its own reasoning behind a link forward, and
        the link is TWO-WAY or somebody arriving at the current decision cannot
        find the reasoning it replaced. -->
- [x] S7.14 Build the gate panel with freshness, evidence, policy version,
      owner, remediation, waiver, and rerun state. Evidence: state matrix.
      <!-- evidence: `libs/oshun/workbench-kit/src/gate-panel.ts` (114 tests,
      the 107th entry point), `apps/oshun/web/src/components/workbench/
      GatePanel.tsx` (+ module CSS, 28 jsdom tests) and
      `apps/oshun/web/tools/gate-panel-browser.mjs` (61 controls in real
      Chromium). S2.9/S2.10/S2.11 already decided what a gate IS, what a RUN
      recorded and when a waiver authorises anything; none of that is
      re-decided, and the whole module composes them — the fourth sighting of
      S7.12's rule that the kit is large now, so look for the module that
      already owns the vocabulary before writing one.
      THE PARENT ACCEPTS ON A STATE MATRIX, AND THAT IS THE RIGHT ACCEPTANCE,
      because every expensive failure of a gate console is one state rendered as
      another. Two of those get named here for the first time. STALE AND EXPIRED
      ARE DIFFERENT ANSWERS BECAUSE THEY ARE DIFFERENT ACTIONS: S2.10's
      `evidenceFreshness` carries both under one word with a `why`, and the
      reader's next move differs — `expired` is a clock, nothing is known to have
      changed and a rerun will very probably say the same; `stale` is a CHANGE, a
      dependency moved or the answer was given against another revision, and a
      rerun should be expected to say something else. Rendering them identically
      makes "it is a bit old" the mental model for a rights check whose consent
      record was withdrawn this morning. And AN EXPIRED PASS IS NOT A PASS —
      decision 10, the one that changes an outcome rather than a sentence.
      Handing S2.10's `resolveEffect` the VERDICT of an answer that no longer
      stands produces `satisfied`, the summary counts it, and the release ships.
      So a row whose answer does not stand contributes S2.8's `stale` NON-VALUE
      and the definition's own `whenEvidenceUnavailable` decides what that costs,
      which is the catalogue's already-written answer to "what do we do when this
      gate cannot answer" applied to the case where it once could. -->
  - [x] S7.14.a Display gate ID/name/severity/applicability, current target
        revision, owner/escalation, and required/optional status. Evidence:
        definition fixtures.
        <!-- evidence: `gateHeaderFaults`, and CONTROL 11. SEVERITY IS PER-STAGE
        AND OBLIGATION IS `canBlock`, WHICH ARE TWO DIFFERENT QUESTIONS. Printing
        `defaultSeverity` is right most of the time and wrong in the one place
        somebody is making a decision, because a gate that is a warning while
        drafting and a blocker at publish has two correct answers. And a
        `blocker` in a SHADOW lifecycle stops nothing: a row that says "required"
        over it sends somebody to fix something that was never going to hold
        anything up, which is how a shadow rollout — whose entire purpose is to
        find out what a gate WOULD have done — becomes indistinguishable from an
        enforcement it never was. The browser drives the shadow case end to end
        and reads `optional`, a lifecycle line naming the shadow, and an effect
        of `noted` rather than `blocks`. Also refused in both directions: a row
        for a gate the catalogue does not apply here (a check the reader will
        wait for and nobody will run) and a gate that applies rendered as not
        applicable, including the case where the LEDGER says `not-applicable` and
        the CATALOGUE disagrees. -->
  - [x] S7.14.b Display raw values/units/uncertainty, evaluator/corpus/model,
        policy/threshold version, verdict, reason, and completion time.
        Evidence: result fixtures.
        <!-- evidence: `resultDisplayFaults`, `uncertaintyBand`,
        `marginWithinUncertainty`, and CONTROL 9. A VERDICT INSIDE ITS OWN
        UNCERTAINTY IS NOT A VERDICT. S2.8 keeps the reading and its uncertainty
        apart precisely so the pair can be read together, and 0.82 ± 0.05 against
        a threshold of 0.80 is a pass whose margin is smaller than its own noise —
        invisible in every screenshot, survives code review because the number
        really is above the threshold, and it is why a gate flaps between pass and
        block on identical input while everybody looks for the bug in the
        evaluator. The band is the 95% interval of the MEAN (`stddev / sqrt(n) ×
        1.95996`), the standard ERROR and not the standard deviation: the question
        is how well the run pinned the value down, not how much the population
        varies, and using the raw stddev would widen every interval by sqrt(n) and
        caveat verdicts that are perfectly well separated. Strict inequality at the
        ends, so a band rounded to the threshold does not count as straddling it.
        Both directions are faults — a flat verdict over a straddling margin, and a
        hedge on one that clears, because a caveat on every verdict is a caveat on
        none. Around it: a bare number can be compared with anything, a model name
        without the provider that served it is a stable label over a thing that
        changed, and a corpus that gained rows is a different reference set. -->
  - [x] S7.14.c Display fresh/stale/expired/missing/pending/running/unavailable/
        unsupported/failed state with invalidating dependency and age. Evidence:
        freshness matrix.
        <!-- evidence: `GATE_PANEL_STATES`, `panelStateFor`, `panelStateFaults`,
        `NON_VALUE_TO_PANEL_STATE`, `STATE_CONTRIBUTES_NON_VALUE`, and CONTROLS 1
        and 2. THE NINE STATES ANSWER ONE QUESTION — *why is there no usable
        answer, and who can change that* — which is what makes them a vocabulary
        rather than a list. THE PANEL VOCABULARY IS NARROWER THAN THE RECORD'S,
        SO NOTHING MAY BE DROPPED IN THE MAPPING: this is S7.13's rule from the
        other side, where five words covered three and the danger was
        contradiction; here nine cover S2.8's eight non-values plus two freshness
        reasons, and the danger is LOSS. `insufficient-data` and `failed` both
        land on `failed` and are two different people's work, so the reading
        carries `sourceNonValue` and the mapping is total and is data. A VERDICT
        MAY NEVER BE PRINTED WITHOUT ITS STANDING — an answer given against inputs
        that have since moved reads exactly like today's answer, which is the
        single most common way a gate system stops working while every light stays
        green — and the browser settles it as a LAYOUT fact: the two are on the
        same line, within 24px, because the same two strings a scroll apart are a
        green light and a footnote. A `stale` row must NAME what moved or it has
        silently become an expired one. AN IN-FLIGHT RUN DOES NOT RETRACT THE
        ANSWER ON SCREEN (a row that flipped to `running` would say there is
        nothing to read) and the answer does not hide the rerun. -->
  - [x] S7.14.d List evidence refs with type/version/hash/access state and safe
        navigation. Evidence: evidence tests.
        <!-- evidence: `evidenceListFaults`, `EVIDENCE_ACCESS_STATES`, and
        CONTROL 5. THE LIST IS OVER WHAT THE DEFINITION REQUIRED, NOT OVER WHAT
        THE RUN RECORDED — S2.10's second decision, one surface up. A list built
        from the records in hand cannot distinguish "the consent record was
        checked" from "the consent record was never asked for", and under that
        list the way to make a rights gate look thorough is to collect less.
        `digest-mismatch` is the fourth access state and the one a naive surface
        cannot express: the reference resolves, the document is there, and it is
        not the document the run read — so a link to it says the verdict still
        rests on what it rested on, which is precisely what the panel cannot know.
        Only `resolved` may be an anchor, and the browser proves the degraded ones
        are not in the tab order by REAL Tab traversal rather than by an attribute
        (with a companion control that resolved references elsewhere ARE reachable,
        so the first control is not vacuous). SAFE NAVIGATION MEANS THE PANEL
        RESOLVES A REFERENCE THROUGH ITS OWN ROUTE: an evidence reference is a
        string an EVALUATOR wrote, and an anchor built from it is an open redirect
        with a trust badge on it. -->
  - [x] S7.14.e Display remediation steps, affected fields/objects,
        documentation, appeal/review, and recompute prerequisites. Evidence:
        remediation tests.
        <!-- evidence: `remediationFaults`, `whoToContact`, `contactFor`,
        `recomputePrognosis`, and CONTROL 8. THE PANEL'S NINE STATES DO NOT DECIDE
        WHO TO CONTACT — THE UNDERLYING REASON DOES, and this is the first thing
        in the kit to READ four of S2.9's five owners. `failed` from a broken
        evaluator is an incident for its owner; `failed` from insufficient input is
        work for whoever submitted; `unsupported` is a question for whoever decided
        this gate applies here at all; a blocking verdict is for the remediation
        owner, who in practice is neither of the first two. One "contact" line for
        all of them pages the wrong team and puts a sentence about a platform in
        front of a person whose problem is editorial. THE RECOMPUTE PROGNOSIS IS
        THE SECOND FIRST-READER: `policy.deterministic` existed only to be compared
        between a catalogue and a contribution, and S2.9's own warning says a
        non-deterministic gate published as deterministic is one whose re-run is
        treated as a confirmation. Here that sentence becomes a control — a green
        DETERMINISTIC check offers no rerun because it would spend the evaluator's
        budget to reprint the same sentence, and the identical row on a
        non-deterministic gate DOES offer one. Futility is part of LEGALITY rather
        than advice, or the panel and its checker disagree about that exact row.
        Refused both ways: an offer that cannot change anything, and a withheld one
        where a dependency moved and the reader could have moved it themselves. -->
  - [x] S7.14.f Display waiver/override scope/reason/approver/evidence/start/
        expiry/residual risk and out-of-scope/stale status. Evidence: waiver
        matrix.
        <!-- evidence: `waiverPanelFaults`, and CONTROL 10. A WAIVER IS RENDERED
        ON THE BLOCK IT COVERS, NEVER INSTEAD OF IT. S2.10 already refuses to call
        `waived` a pass, and a surface can undo that decision simply by rendering
        the exception alone — after which no reader can tell a check that passed
        from one somebody signed for. AND A WAIVER THAT NO LONGER AUTHORISES MUST
        STILL RENDER (S2.11's fifth decision): one that vanishes takes with it the
        fact that somebody once thought this was acceptable, which is the fact an
        incident review is looking for. Both are faults, and so is the middle case
        — an expired or out-of-scope waiver renders in full, with its approver, its
        dates and its residual risk, and is not presented as covering anything.
        The browser drives `?waiver=active` and `?waiver=expired` over the same
        row: the first renders `waived` with the `block` still on screen beside it,
        the second renders `blocks` with the whole exception still readable. The
        scope has to be printed or an out-of-scope waiver looks like an arbitrary
        refusal, and a severe accepted risk with nobody acknowledging the
        escalation is named where S2.11 can only refuse it at write time. -->
  - [x] S7.14.g Authorize and run/recompute/cancel/retry/appeal/waive actions
        through durable jobs with cost/impact confirmation. Evidence: action
        journeys.
        <!-- evidence: `gateActionFaults`, `actionIsLegal`, `ACTION_FROM_STATES`,
        and CONTROL 7. AN ACTION IS A DURABLE JOB, AND A CONTROL THAT RETURNS A
        VERDICT IS CLAIMING AN ANSWER IT DID NOT GET — `ActionOutcome` carries a
        `verdict` arm SO IT CAN BE REFUSED. A gate is evaluated by somebody else,
        later; a panel that flips the verdict on click has invented a synchronous
        evaluator and rendered a number no durable record holds. Without an
        idempotency key a double-click is two runs and two bills. The order is
        S7.13's and is not negotiable — AUTHORIZED, then LEGAL FROM THIS STATE,
        then confirmed proportionately, then DURABLE — and confirmation is
        proportionate in BOTH directions: a free action behind a typed confirmation
        trains everybody to dismiss the one that mattered, and a waiver, which is a
        signature on a risk somebody else carries, behind a single click is not a
        decision anybody made. `run` is only for a gate that has never answered and
        `recompute` only for one that has: the same job, two sentences, and a panel
        offering "run again" over a check that never ran once is describing a
        history that did not happen. THE BROWSER FOUND A REAL DEFECT HERE — see the
        rollup. -->
  - [x] S7.14.h Provide keyboard/screen-reader navigation, noncolor status,
        compact grouping, and no pass-only aggregation hiding blockers.
        Evidence: accessibility tests.
        <!-- evidence: `panelAccessibilityFaults`, `panelHeadline`,
        `isUnknownRow`, and CONTROLS 3, 4, 5 and 6. THE HEADLINE IS BUILT FROM THE
        COUNTS, NOT CHECKED AGAINST THEM. A rule of the form "the headline must
        mention the number of blockers" is SATISFIED BY "1 of 3 checks passed" —
        every count is right there as a substring — and that sentence is exactly
        the failure this cell exists to stop. So the kit produces the line, the
        surface renders it, and a panel that writes its own is a disagreement
        rather than an argument. The order is the content: blockers, then gaps
        (because a gap is a decision nobody made and a waiver is one somebody
        signed — S2.10's `ELIGIBILITY_STRICTNESS` already ranks them that way),
        then waivers, and the pass count LAST. Zero is SPOKEN. COMPACTING IS FINE;
        COMPACTING A BLOCKER OUT OF SIGHT IS NOT — and `collapsed` is MEASURED in
        the browser rather than declared: the tool presses the collapse control
        and reads rendered heights, so the claim is about boxes rather than about
        a boolean, and the group's `open` is DERIVED so no control can force it
        shut. Tab order is real traversal. Non-colour status is proved under
        `forcedColors: 'active'` — a real rendering in which the browser overrides
        the palette — with a companion control that the colour channel really was
        flattened, so the proof is not vacuous. -->
        <!-- S7.14 rollup: 114 kit tests (107th entry point, 5565 kit tests
        total) + 28 jsdom tests + 61 real-Chromium controls, each rule with a
        NEGATIVE control driving the case it exists to catch. DIST-PATCH
        VERIFICATION: silencing `verdict-printed-without-its-state` in the BUILT
        dist failed its negative control, and narrowing `UNKNOWN_STATES` so an
        outage stops counting as a gap failed the summary comparison; restoring
        the dist returned all 61 to green.
        THE BROWSER FOUND A REAL DEFECT. Clicking "retry" on a row that ALREADY
        had an answer queued a job — and the row's state did not move, because by
        design an in-flight run must not retract the answer on screen. So every
        costly control stayed on offer and a second click was a second job and a
        second bill. The state was the wrong place to look: `PanelReading` now
        carries `inFlightJobId` SEPARATELY from the state, because they are two
        different facts and a row has to carry both — the answer keeps standing
        while a replacement is on its way. `recomputePrognosis` consults it first,
        so "wait rather than queue another" outranks every other reason. Source
        reading could not have found this; the two halves each looked correct. -->

- [x] S7.15 Build the provenance/lineage inspector. Evidence: ingredients,
      transformations, hashes, signatures, validation, and gaps are visible.
  - [x] S7.15.a Display exact artifact/revision identity, byte hash/algorithm/
        size/type, release refs, and manifest/ledger binding. Evidence: artifact
        fixtures.
        <!-- THE DIGEST'S ORIGIN IS THE CLAIM, NOT THE DIGEST. Four different
        things produce the same sixty-four characters and they render
        identically: bytes hashed just now, a number copied out of the manifest
        under examination, a checksum the store keeps for its own purposes, and
        a value the caller passed in. Only the first is evidence about the bytes
        on screen; the second is the document agreeing with itself, which is
        what a tampered manifest is best at, and the third is S6.12's finding —
        truncate an object in MinIO and it recomputes its own ETag over what it
        now holds. So `EVIDENTIAL_DIGEST_ORIGINS` has exactly one member,
        `DIGEST_ORIGIN_LIMITS` states what each of the other three cannot
        settle, and the header carries `digestOriginRendered` separately because
        the RECORD always knows where its digest came from and the SURFACE is
        where that gets dropped. A floating reference cannot head the page at
        all: S1.4's `resolutionContract` already says a signature over a channel
        name commits to nothing, so every panel below it would be about bytes
        nobody named. Both lengths are on screen because a short read is the
        corruption real stores produce and the one a self-consistent checksum
        cannot see. -->
  - [x] S7.15.b Traverse ingredients and derivatives with relation,
        version/hash, source/origin, rights/consent hint, availability, and
        redaction. Evidence: lineage graph tests.
        <!-- LINEAGE IS A DAG, SO A CYCLE IS A DEFECT AND A DIAMOND IS NOT —
        the opposite call from S7.10, where a cycle was a legitimate graph. Two
        byte sequences cannot each have been made from the other, and a walk
        that silently absorbed one would report a finite history for a record
        that has none; the same photograph placed twice is ordinary and a finder
        that flagged it would be switched off in a week. `lineageCycles` is an
        iterative three-colour DFS so a node already FINISHED is a second path
        and only a node still ON THE STACK is a way back. AN ASSET HAS AT MOST
        ONE PARENT, because the parent is whose history this history continues.
        A DEGREE IS THREE NUMBERS and a concealed neighbour is in NONE of them
        (S7.10's rule, and S5.3 already named the classes where the existence of
        the row IS the disclosure) while a withheld one IS counted, because a
        denial is a refusal somebody can appeal. The walk carries its budget,
        what it stopped at and what it did not reach — an ingredient list that
        ran out of budget reads as the complete set of things this was made
        from. -->
  - [x] S7.15.c Display ordered capture/edit/generate/transform/merge/validate/
        approve/export/publish actions and parameters. Evidence: action history
        fixtures.
        <!-- THE ORDER IS THE RECORDED ORDER AND A TIMESTAMP NEVER SORTS IT.
        Action timestamps come from whichever machine performed the action;
        across a generator in one region and an approval clicked in another they
        are not comparable, and sorting by them shuffles somebody's history into
        a plausible-looking wrong order. A clock that runs backwards against the
        record is REPORTED — it is skew or a fabricated entry, both worth seeing
        — and it re-sorts nothing. A GAP IS A ROW: positions are contiguous and
        an absence occupies its position with its cause, because six actions
        with a hole where the seventh was reads as six actions. `parameters`
        distinguishes `{}` ("ran on its defaults") from `null` ("the record does
        not say"), which is the pair a component collapses the moment it writes
        `parameters ?? {}`. `ACTION_REQUIREMENTS` is data: generation needs its
        model AND its provider, since two deployments of one model version
        answer differently; a merge names what came together; an approval cannot
        be a service account; and a publication with its only approval AFTER it
        is a record of somebody approving what the world already had. -->
  - [x] S7.15.d Display
        tool/app/plugin/provider/model/template/policy/environment versions and
        authenticated/delegated actors safely. Evidence: provenance detail
        tests.
        <!-- ALL EIGHT SLOTS ALWAYS RENDER (S7.1's rule: a vocabulary that
        cannot say `unknown` spells it as absence, and absence renders
        identically to fine). A panel showing five versions because three were
        not recorded has told the reader five things were involved. And the
        three absences are three different sentences: `not-recorded` is work
        somebody has to finish, `not-applicable` is a claim about this pipeline
        that must say why, and an EMPTY CELL is both at once — which is the
        fault that costs the most and shows the least. `describeActor` is the
        first surface reader of S1.4's refusal of display data in an identity: a
        signed provenance record is permanent and travels with the asset, so a
        name or an address in it cannot be un-published. A delegated act answers
        two questions and a line carrying one of them answers neither; a
        cross-tenant service has NO tenant rather than one nobody recorded; and
        a redacted actor is "identity removed (cause)", never "unknown". -->
  - [x] S7.15.e Display signature/binding/chain/trust/revocation/timestamp/
        assertion/ingredient validation independently. Evidence: validation
        state matrix.
        <!-- COMPOSE S6.7, DO NOT RE-DECIDE IT. `content-credentials.ts` already
        published the ten checks, the six outcomes and the nine states; S7.15 is
        the presentation, and the presentation is where the boolean comes back.
        EVERY CHECK RENDERS ITS OWN OUTCOME AND THE STATE NEVER RENDERS WITHOUT
        THE CHECK THAT DECIDED IT — `CredentialStateResult` has carried
        `decidedBy` since S6.7, so a surface has no excuse. A check with no
        result renders `not-performed` rather than disappearing, because an
        absent row reads as no problem found. Revocation and the hard binding
        are `PRIMARY_CHECKS`: an unreachable revocation responder silently
        becomes a trust decision for every asset validated during the outage,
        and that rule is held at the PRESENTATION layer too, since the honest
        outcome beside a sentence reading "not revoked" is the string a reader
        takes away. `ingredient-availability: partial` over forty ingredients
        tells nobody anything — the useful fact is which. -->
  - [x] S7.15.f Display missing/stripped/unavailable/unsupported/untrusted/
        invalid/ambiguous gaps with recovery method and limitations. Evidence:
        gap fixtures.
        <!-- RECOVERED IS NOT BOUND. When a channel has stripped the credential a
        perceptual match finds a record for bytes that LOOK like these, and
        rendering it the way a hard binding is rendered asserts the one thing a
        hard binding exists to establish. `recoveryWorth` composes S6.8's
        outcomes: only `exact-hard-match` states these bytes, and a match to a
        REVOKED or DELETED record is `none` rather than weak — a weak yes is how
        a withdrawn registration keeps answering. Only `stripped` gets a method
        that produces a candidate; the other six resolve to `none` deliberately,
        because "retry when the responder is back" is waiting, and calling it a
        recovery method makes an outage look like a procedure. `stripped`
        REQUIRES AN EXTERNAL WITNESS: missing and stripped are byte-identical
        from inside the asset, so the second word accuses something of removing
        the credential and needs a ledger record or a measured channel profile
        behind it. And `ambiguous` is two answers, not no answer — showing one
        of them is choosing at random. -->
  - [x] S7.15.g Provide semantic table/tree parity, bounded graph traversal,
        protected-source redaction, keyboard navigation, and evidence export
        authorization. Evidence: accessible/security tests.
        <!-- THE PARITY HERE IS SYMMETRIC, WHICH INVERTS S7.10 AND S7.11. There
        a coordinate stated nothing and the picture was allowed to show less
        than the table; here the NESTING IS THE RELATION — indentation is the
        claim "this was made from that" — so the two views are two renderings of
        one statement and any difference is one of them being wrong. Both are
        built from ONE `statement` string, because two independently assembled
        sentences drift. The order check is the one people leave out: a table
        sorted by name over a tree in walk order gives two readers two
        histories, each internally consistent. THE EXPORT IS BUILT FROM THE
        READING AND AUTHORIZED SEPARATELY FROM THE VIEW: what may be read on
        screen inside a tenant-scoped session is not what may be handed to
        somebody outside it, and an export that re-queries runs with the
        exporter's authority over a fresh answer, so it can hold rows the reader
        never saw and is not covered by the redaction the surface applied.
        Counted at a real `node:http` server: pressing export made ZERO
        requests. And where the object is concealed there is no control at all,
        because a control that refuses admits the object exists. -->
        <!-- S7.15 rollup: 177 kit tests (108th entry point, 5743 kit tests
        total) + 36 jsdom tests + 96 real-Chromium controls, each rule with a
        NEGATIVE control driving the case it exists to catch, built from what
        the browser rendered. DIST-PATCH VERIFICATION both ways: widening
        `EVIDENTIAL_DIGEST_ORIGINS` in the BUILT dist so a manifest-supplied
        digest counts as evidence failed its control, and silencing
        `concealed-node-rendered-as-a-row` failed its; restoring the dist
        returned all 96 to green.
        THE BROWSER CORRECTED A MEASUREMENT AND FOUND A FIXTURE THAT PROVED
        NOTHING. The first version of the adjacency check asked whether the
        digest and its origin were OVERLAPPING BOXES, and at 1280px a sha256 is
        71 characters that fill their own line, so the origin lands on the line
        below and the check called one statement two. What actually separates a
        statement from a footnote is the BLOCK and the DISTANCE — same `dd`, gap
        under one line — and detaching the origin into another block in the real
        document breaks both, which is what makes the pair load-bearing.
        Separately, the "an unasked check still has a row" control was answered
        by a fixture that answered all ten, so it was measuring nothing; it now
        drives a page with a check genuinely missing. Property tests over
        fixed-seed generated DAGs check the walk's depth against an independent
        shortest-path relaxation and the cycle finder against graphs that cannot
        contain one. -->
- [x] S7.16 Build job and queue views with progress, dependency graph, logs,
      cost, retry, cancel, and failure detail. Evidence: live fault-injection
      journey.
  - [x] S7.16.a Build permission-aware queue list with job type/version, target,
        priority, state/stage, queued age, progress, owner, resource/provider,
        cost, deadline, and stable pagination/filtering. Evidence: queue tests.
        <!-- `queued` COVERS TWO THINGS ON SCREEN AND ONLY ONE OF THEM IS A
        QUEUE. A job whose `runnableAt` is in the future is waiting for a CLOCK,
        not for a worker, and rendering it beside the ones that are waiting for
        a worker is how a queue of four looks like a queue of forty and the
        worker pool gets the blame. So `RUNNABLE_STATES` is WIDER than S3.9's
        six (S7.13's rule) and `RUNNABLE_STATE_SOURCE` maps every one of its
        eight members back to exactly one record state, so it can say more and
        can never contradict. The other addition is `stalled`: A LEASE THAT HAS
        LAPSED IS NOT A WORKER, and "running on worker-7" over a job nothing is
        holding is the single most misleading cell a job console can draw —
        the state is true, the worker is true, and both are statements about the
        past. `runnableStateOf` consults S3.9's own `workerHoldsLease`, so the
        clock-skew margin comes with it. AN AGE IS READ AT COMPARISON TIME
        (S7.3's finding): the surface ticks its own clock, because a `now`
        captured at mount makes every age wrong by however long somebody has
        been looking, and the jobs queued longest are the ones a decision is
        about to be made about. A DEADLINE IS A STANDING, never an instant —
        the reason a deadline is on the row is to say whether it has passed.
        Keyset over (priority, enqueuedAt, jobId), because an offset over a
        draining queue skips and repeats exactly the rows being worked on. -->
  - [x] S7.16.b Build job detail with immutable inputs, attempts, lease/worker,
        timing, checkpoints, outputs, provenance, audit/correlation, and safe
        support refs. Evidence: detail fixtures.
        <!-- THE INPUTS ARE A REFERENCE AND A HASH, NEVER A COPY. A panel holding
        a copy is a copy that can disagree with the reference, and it is the one
        the reader believes, on a page anybody with the job id can open;
        "immutable" is a promise until the hash beside it checks it. EVERY
        ATTEMPT, not the last: three timeouts then a validation error is a
        different story from one validation error and only the second is the
        submitter's. And A SUPPORT BUNDLE THAT CARRIES THE INPUTS copies a
        customer's data into a ticketing system with a different retention policy
        and a different audience, at the exact moment nobody is thinking about
        retention — so `SupportReference` carries `carriesInputs` and
        `carriesSecrets` as facts the checker reads rather than as promises. -->
  - [x] S7.16.c Visualize parent/child/dependency/critical-path graph with
        semantic table parity and required/optional/blocking state. Evidence:
        graph parity tests.
        <!-- REQUIRED, OPTIONAL AND BLOCKING ARE THREE FACTS AND ONLY ONE IS
        ABOUT NOW. Required is the edge's contract, optional is what may fail
        without failing the parent, blocking is what is holding the parent up at
        this moment; a graph that draws the first and the third alike makes every
        ordinary dependency read as an incident. THE BROWSER SETTLED THE FOURTH:
        FINISHED IS NOT SATISFIED. A required child that was dead-lettered has
        finished — it is holding nothing up — and it has not met the requirement
        and never will, so drawing it `satisfied` says the parent is fine at the
        moment the parent is about to fail. `SATISFYING_STATES` is one member
        beside `FINISHED_STATES`, written down next to it precisely because the
        two are so easy to conflate. The parity here is ASYMMETRIC, unlike
        S7.15's lineage tree: a job graph is drawn with a layout and a coordinate
        states nothing (S7.10), so a bounded picture may show fewer nodes than
        the table and only the reverse is a fault. And the critical path is a
        PREDICTION from expected durations that moves as the run proceeds, so it
        says so or it reads as the route the work took. -->
  - [x] S7.16.d Display completed/total/indeterminate progress by stage and
        aggregate without invented percentages or false completion. Evidence:
        progress fixtures.
        <!-- THE BROWSER FOUND A REAL DEFECT HERE AND jsdom AGREED WITH THE BUG.
        The fill element carried no inline size when the fold was indeterminate
        — which is correct as an intention and wrong as CSS, because a
        block-level child with no declared width FILLS ITS CONTAINER. The honest
        "we do not know how far along this is" rendered as a FULL BAR, the exact
        reading an unknown denominator must never produce. In jsdom the inline
        style is a string and the assertion that it was absent passed; in a real
        browser the box was 1244px wide. `inline-size: 0` is now the default and
        the determinate case overrides it, so the only way to get a filled bar is
        to have a fraction to fill it with. A FULL BAR IS ALSO NOT COMPLETION:
        S3.9 deliberately produces no `done` from the progress table because
        completion is a STATE decided by whatever commits the last effect, and a
        worker that dies after its final report leaves every counter at its total
        — the job that never wrote its result is then one nobody is looking for
        and its outputs are ones nobody deletes. -->
  - [x] S7.16.e Display privacy-safe structured logs/events with levels,
        timestamps, attempt/stage, filters, bounded streaming, download policy,
        and no content/secrets. Evidence: log security tests.
        <!-- A LOG LINE IS THE LEAKIEST SURFACE IN THE PRODUCT. It is written by
        a handler, in a hurry, about the thing that just went wrong — which is
        usually the input. So the console renders STRUCTURED events over a
        declared field ALLOW-LIST (S5.5's rule for response headers, one level
        up) and the renderer walks the list rather than the event, so a handler
        that attached the prompt gets it dropped at the surface rather than at
        the reviewer: measured in the browser as the string never appearing in
        the rendered document at all. A DROPPED LINE MUST BE VISIBLE — an
        unbounded tail is a leak in the browser and a load on the server, and a
        bounded window that discards silently renders as a shorter, calmer log,
        so an operator reading it during an incident concludes nothing much
        happened in the gap. And a download is a SECOND authorization, because a
        file that leaves the system is a different question from a panel that is
        read inside a session. -->
  - [x] S7.16.f Display estimated/reserved/actual/refunded/variance cost and
        resource usage with units/rate version. Evidence: cost fixtures.
        <!-- A REFUND IS NOT A NEGATIVE COST AND A VARIANCE IS NOT A NUMBER
        ANYBODY STORES. Netting a refund against a charge loses that money moved
        twice — the only thing an accounts query cares about — and makes a job
        that was charged and refunded indistinguishable from one that was never
        charged. `costVariance` derives it, and a stored variance that disagrees
        with the two numbers it came from is the finding; it REFUSES across
        currencies and across scales rather than subtracting, because two amounts
        in different currencies have no difference without a rate and inventing
        one would put a made-up exchange rate on an accounting screen. An
        estimate is an INTERVAL (S5.7's rule) and a point is a claim nobody made.
        Every amount carries its rate version or it cannot be re-derived, and the
        day a price changes there is no way to tell a job that cost more from one
        that was priced differently. -->
  - [x] S7.16.g Authorize cancel/retry/resume/replay/abandon with impact,
        idempotency, confirmation, eligible scope, and result. Evidence: control
        journey tests.
        <!-- ONE PREDICATE decides what is offered and what a press may do
        (S7.14's rule), so a withheld control and a checker calling it withheld
        cannot come from two opinions. A CONTROL PRODUCES A JOB, NEVER A STATE:
        a console that sets a job to `queued` has decided what a scheduler
        decides, with none of the fencing, budget or idempotency that goes with
        it, and the job it "retried" is now competing with whatever the last
        worker is still doing — measured in the browser as the row not moving and
        the server seeing zero requests. Impact is part of the offer, because
        cancelling a parent cancels its children and the confirmation has to say
        how many; confirmation is proportionate in BOTH directions (S7.6), since
        over-confirming trains everybody to dismiss the one that mattered. A bulk
        control freezes its scope AND the count at the freeze (S7.5). -->
  - [x] S7.16.h Display failure class/reason/retained outputs/retryability/
        remediation/dead-letter/unknown outcome without stack leakage. Evidence:
        fault-injection browser journey.
        <!-- `unknown-outcome` IS THE MOST IMPORTANT THING A FAILED JOB CAN SAY
        AND THE ONE MOST OFTEN RENDERED AS AN ORDINARY FAILURE. Every other kind
        tells the reader what did not happen; this one tells them nobody knows.
        S3.9 already carries `unknownOutcome` on the classification and this is
        its first reader — and `controlIsLegal` consults it, so with an
        at-most-once effect the retry is not offered at all, because retrying is
        how one payment becomes two and doing nothing is how it becomes none.
        The browser drives BOTH deliveries over the SAME failure and gets two
        different control sets, which is the whole point of the clause. A
        RETAINED OUTPUT is on screen and DECLARED PARTIAL, or a stage that did
        not finish reads as a result and nobody deletes it. And a stack frame is
        a leak with three payloads — a path names the deployment layout, a module
        frame names a version somebody can look up an advisory for, and an
        interpolated frame sometimes carries the value — so the reader gets a
        class, a reason KEY and a correlation id. -->
        <!-- S7.16 rollup: 142 kit tests (109th entry point, 5886 kit tests
        total) + 49 jsdom tests + 88 real-Chromium controls, each rule with a
        NEGATIVE control driving the case it exists to catch, built from what the
        browser rendered. DIST-PATCH VERIFICATION both ways: widening
        `SATISFYING_STATES` in the BUILT dist so a dead-lettered required child
        counts as met failed its control, and silencing the ready/deferred split
        failed its; restoring returned all 88 to green.
        THE BROWSER FOUND TWO REAL DEFECTS AND jsdom AGREED WITH ONE OF THEM. (1)
        The indeterminate progress fill had no inline size, which is correct as
        an intention and wrong as CSS: a block-level child with no width fills
        its container, so "we do not know" rendered as a full bar. jsdom asserted
        the inline style was absent and passed; the real box was 1244px.
        (2) `edgeEmphasis` checked FINISHED before REQUIRED, so a dead-lettered
        required child was drawn `satisfied` — the parent is about to fail and
        the graph said it was fine. Only success satisfies; a required edge whose
        child finished without succeeding falls back to `required`, because the
        requirement stands and is now unmeetable.
        Also corrected a measurement: a percentage inline size resolves against
        the CONTENT box, so comparing the fill against the bar's border box fails
        by exactly the border (S7.11's `max-width: 100%` finding from the other
        side). -->

- [x] S7.17 Build audit history with safe redaction and export. Evidence:
      authorization tests distinguish ordinary users and auditors.
  - [x] S7.17.a Build immutable chronological/correlated event query by target,
        actor, action, outcome, revision, policy, job/release, and time with
        stable pagination. Evidence: query tests.
        <!-- THE QUERY IS ITSELF A DISCLOSURE AND ITS DIMENSIONS ARE NOT ALIKE.
        "What happened to this object" is ordinary; "what did this person do" is
        surveillance of a colleague, and it is the same screen with a different
        field filled in. So `SENSITIVE_DIMENSIONS` names it and the surface
        renders it as a WORD rather than a disabled control — measured in the
        browser as not a button and not in the tab order, because a disabled
        control teaches its reader that this console refuses things. THE
        SEQUENCE IS THE ORDER AND THE TIMESTAMP IS A FIELD (S7.16's lesson, one
        surface on): `at` comes from whichever node wrote the record. And
        PAGING BACK THROUGH AN APPEND-ONLY LOG IS STABLE BY CONSTRUCTION while
        paging forward is not — the exact opposite of S7.16's queue, where every
        row is moving; nothing behind the cursor can change, so only the forward
        direction has to say there is more. -->
  - [x] S7.17.b Display actor/delegate, tenant-safe context, action, prior/new
        revision, reason, decision/policy, result, evidence refs, and
        correlation without copying protected content. Evidence: event fixtures.
        <!-- S4.12 made "without copying protected content" STRUCTURAL: a
        mutation carries a `parameterDigest` and there are no parameters to
        copy, and a refusal has NOWHERE to put a new revision. Both promises are
        broken later in exactly one place — a console that joins to the request
        store and offers a disclosure triangle, and a row that prints a dash
        where a new revision would go. So the checker looks for an expand
        control and the browser asserts there is no `<details>` element anywhere
        in the rendered document. A single-tenant history prints no tenant
        column, because a column of one repeated value trains people to stop
        reading the context line. -->
  - [x] S7.17.c Group request/command/audit/event/job/provider/publish chains
        and preserve raw event drilldown for authorized roles. Evidence:
        correlation view tests.
        <!-- COMPOSES S5.4's THIRTEEN LINK KINDS rather than inventing a second
        vocabulary. `purged` AND `missing-linked-event` ARE DIFFERENT ABSENCES
        and on the wire they are the same shape — a link with no record behind
        it — separated by exactly one thing, a tombstone. `absenceOf` is that
        one thing as a function, and the two faults point opposite ways:
        rendering a missing link as purged turns a broken write into a retention
        policy, and rendering a purge as missing sends somebody to investigate a
        deletion that was requested. The raw event is what the projection was
        built by removing things FROM, so opening it is a separate question from
        reading the projection. -->
  - [x] S7.17.d Apply object/property, protected-source, learner/biometric,
        rights, tenant, support, legal-hold, and role redaction at query/result.
        Evidence: ordinary-user/auditor negative tests.
        <!-- REDACTING AT THE RESULT ALONE IS AN ORACLE. A query that may FILTER
        on a field the reader may not READ answers the question with its COUNT:
        ask for events where the protected source is X, get seven back, and the
        redaction has been defeated without a single value crossing the wire. So
        `AXIS_APPLIED_AT` is data and three of the eight axes are `both`, and a
        concealed field is refused BY NAME in the same words an unknown one gets
        (S7.4's rule, one layer up) so the error is not a schema oracle. A LEGAL
        HOLD WIDENS RETENTION AND GRANTS NOTHING: it stops a purge, it says
        nothing about who may read what it preserved, and a console that treats
        "under hold" as "available to the litigation reader" has invented an
        authorization out of a retention rule — over exactly the records
        somebody has already decided are contentious. Support access is
        time-boxed AND itself audited, in this log. -->
  - [x] S7.17.e Distinguish redacted, inaccessible, retained, purged, integrity-
        invalid, late, and missing-linked-event states. Evidence: state
        fixtures.
        <!-- SEVEN STATES, SEVEN WORDS AND SEVEN RECOURSES, all asserted
        DISTINCT — the vocabulary has seven members precisely because the
        reader's next move differs, and a surface that shares a sentence between
        two of them has thrown that away. `redacted` and `inaccessible` are the
        pair that costs the most: the first is gone for everybody and the second
        is gone for you, and one word for both sends somebody to request an
        access grant for a value that no longer exists. That check fires only
        when BOTH are on screen, because that is the only arrangement in which a
        reader could have told them apart and was not given the chance. A purge
        carries its tombstone or it is indistinguishable from a write that did
        not land; a late entry says where it belongs, because a reader scanning
        in order will not see it where they expect it. -->
  - [x] S7.17.f Export a versioned checksummed snapshot with query scope,
        generated-by/time, schema, redaction policy, page completeness, and
        access controls. Evidence: export tests.
        <!-- PAGE COMPLETENESS IS THE FIELD EVERYBODY FORGETS AND THE ONE THE
        EXPORT IS FOR. A snapshot that hit a limit and stopped is a partial
        history in a file with a checksum on it, and everything about the way it
        looks says complete — S7.15's truncation rule applied to the artifact
        that gets filed as evidence. THE REDACTION POLICY VERSION TRAVELS WITH
        IT for the slower version of the same problem: the same query under a
        later policy returns different rows, so two exports of one unchanged
        history look like the history changed. Authorized separately from the
        view and labelled a copy, per S7.15; built from the rows on screen,
        proved by a real `node:http` server seeing ZERO requests when the button
        is pressed. -->
  - [x] S7.17.g Verify audit chain/signature/hash integrity and surface failures
        without hiding remaining history. Evidence: tamper tests.
        <!-- A BROKEN CHAIN MUST NOT HIDE THE REST OF THE HISTORY. The failure
        this exists to prevent is a console that verifies, finds a break at
        entry 400 and shows nothing — it has answered "is this log intact" by
        making the log unreadable, at the exact moment an investigator needs the
        399 entries that ARE intact and the ones after it that are still
        evidence of something. Measured in a real browser as every entry on both
        sides of the break having a non-zero box, and the ones beyond it
        carrying a label; the label survives `forcedColors: 'active'` because it
        is a border STYLE, with a companion control proving the colour channel
        really was flattened. THREE QUESTIONS, ASKED SEPARATELY — a hash that
        does not match, a link that does not follow and a signature nothing
        covers are three different incidents. And AN UNVERIFIABLE ENTRY IS NOT
        AN INVALID ONE: no checkpoint covers it yet, and reporting that as a
        failure produces an incident every time the log is read faster than it
        is signed. -->
        <!-- S7.17 rollup: 93 kit tests (110th entry point, 5980 kit tests
        total) + 29 jsdom tests + 69 real-Chromium controls, each rule with a
        NEGATIVE control driving the case it exists to catch, built from what
        the browser rendered. DIST-PATCH VERIFICATION both ways: emptying
        `SENSITIVE_DIMENSIONS` in the BUILT dist so an actor query needs no
        grant failed two controls, and weakening `AXIS_APPLIED_AT` so
        protected-source needs the result layer only failed two more; restoring
        returned all 69 to green.
        THE HARNESS TRAP WORTH RECORDING: the first break was driven at the LAST
        sequence in the fixture, so nothing was ever beyond it and the
        "entries after the break are labelled" control was answered by an empty
        set. A break needs entries on BOTH sides to be a break at all, and the
        control now asserts that first. Also hit the tsc-bypass trap: building
        the dist with `npx tsc --outDir` (Nx was broken by worktree duplicates)
        does not copy `package.json`, so `packed-consumer-smoke` refused the new
        subpath until it was copied across. -->

- [x] S7.18 Build bulk-action preview and result surfaces. Evidence: mixed
      eligibility, partial failure, cancel, and retry journeys.
      <!-- COMPOSE S7.5 + S4.12 + S3.9 + S7.16 + S7.6, DO NOT RE-DECIDE THEM.
      Seventh sighting of the S7.12 rule: S7.5 published the selection (and the
      rule that "everything matching" is a frozen QUERY plus the count at
      freeze), S4.12 the per-item result and the arithmetic that makes `partial`
      real, S3.9 the job states, failure kinds, delivery guarantees,
      cancellation and the noncancellable region, S7.16 which failures are the
      caller's to fix, S7.6 the risk table and the confirmation bands. The kit
      is large now; look before writing. -->
  - [x] S7.18.a Freeze selected IDs or versioned query and show selected,
        currently matched, hidden/inaccessible, and changed-since-selection
        counts. Evidence: concurrent selection tests.
        <!-- A FROZEN QUERY IS A FROZEN ANSWER, NOT A SAVED SEARCH. The reason
        to freeze is that the preview and the execution must be about the same
        set, so a surface that re-runs the query when the actor presses go acts
        on objects created after the preview was read — never counted, never
        checked for eligibility, never in the confirmation. It is re-evaluated
        to REPORT the difference and never to widen the action, and a query
        freeze with no snapshot token cannot even do that, because its answer
        is unreproducible.
        "CHANGED SINCE SELECTION" IS FOUR EVENTS, and the one a query re-run can
        never find is the object that STILL MATCHES and has been rewritten end
        to end — so the detection mode is a field rather than an implementation
        detail. Hidden-by-your-filter and not-yours are TWO counts, because one
        clears and the other does not, and one line for both sends somebody to
        clear a filter that will never bring the rows back.
        AND THE SUBSETS OVERLAP, SO A PRINTED REMAINDER IS ARITHMETIC THAT DOES
        NOT HOLD — including when it ADDS UP. `revision-moved` sits inside
        `currentlyMatched` as well as `changedSinceFreeze` and
        `no-longer-visible` inside `inaccessible`, so a clean sum can be a
        double-count cancelled out by something the categories missed. That is
        why the rule has a membership half as well as an arithmetic one, and why
        the browser tool drives the membership half separately: the rendered
        counts already trip the arithmetic one, and an untested half is one a
        patch to the built kit walks straight through (verified: it did). -->
  - [x] S7.18.b Display eligible/ineligible/unknown/already-satisfied items with
        object-specific authorization/state/dependency/policy/capacity reasons.
        Evidence: eligibility fixtures.
        <!-- A REASON BELONGS TO AN OBJECT, NOT A BUCKET. "17 ineligible:
        insufficient permission" is the failure this exists to prevent — the
        reader fixes the one reason on screen, presses go, and watches sixteen
        refuse for four other reasons. The five reason kinds differ in what the
        reader does next, so only the one whose remedy is WAITING may carry a
        clock: a retry-after on a policy refusal is an invitation to hammer a
        decision no clock changes, and on an authorization refusal it tells
        somebody their access is about to arrive.
        A CONCEALED ITEM IS NOT AN INELIGIBLE ITEM, because a refusal is a
        disclosure. `already-satisfied` is its own bucket and is not a success:
        nothing will change, so counting it as completed claims work nobody did,
        but acting anyway is not free either — the audit record, the
        notification and the bill are not idempotent. And `unknown` is an
        answer: the surface says which way it will be treated, and attempting
        the unknowns under an action that cannot be undone is the one
        combination that is never allowed. -->
  - [x] S7.18.c Preview resulting state, dependencies, gates, notifications,
        external effects, cost, irreversibility, rollback, and confirmation
        requirement. Evidence: impact fixtures.
        <!-- NOTIFICATIONS ARE COUNTED BY RECIPIENT, NEVER BY ITEM. Five hundred
        objects that all notify one person is one interruption; five hundred
        that notify five hundred people is an event, and reporting the object
        count misinforms in whichever direction the fan-out runs.
        A ROLLBACK HAS A SCOPE AND AN EXTERNAL EFFECT IS OUTSIDE EVERY SCOPE —
        restoring the internal state does not unsend the mail — so a preview may
        only be presented as reversible when nothing in the set carries one, and
        irreversibility over a set is decided by the STRONGEST member.
        A SINGLE HEADLINE OVER TWO STARTING STATES IS TRUE OF HALF OF THEM: the
        other half takes a different edge with different guards, so a preview
        that cannot say one thing says several. And the confirmation is S7.6's
        own table over what will ACTUALLY BE ATTEMPTED — confirming over 500
        selected when 100 are eligible over-confirms, and over 100 when the 40
        unknowns will also be tried under-confirms. Measured in the browser: a
        twenty-thousand-object SELECTION does not escalate the band, because
        three objects are eligible either way. -->
  - [x] S7.18.d Execute through durable job and display pending/running/
        completed/failed/skipped/cancelled per-item and aggregate progress.
        Evidence: live execution journey.
        <!-- THE SUMMARY MAY NOT BE PRODUCED WHILE THE RUN IS STILL MOVING.
        S4.12's `summarizeBulk` is arithmetic with no notion of time: handed a
        run whose first object is still pending it counts zero successes and
        reports `failed`, and handed one half done it reports `partial` — both
        settled-sounding verdicts about something unfinished, and the surface
        would print them in large type. `foldRun` returns null instead, which is
        the whole reason to compose S4.12 through a function rather than call it.
        The mapping from the six presentation states back to S4.12's three is
        DATA, because a wider vocabulary is where the narrower one loses a word:
        `skipped` and `cancelled` are both `refused`, which is neither a success
        nor a failure. `skipped` also carries WHEN the decision was taken —
        skipped-at-preview was refused before anything started and the reader
        saw that, while skipped-at-execution stopped qualifying mid-run, and it
        is the only evidence anybody gets that the world moved after the preview
        was approved. Two origins under one sentence have been collapsed on
        screen however carefully the record kept them apart. And the progress
        denominator is the FROZEN count: a denominator discovered as the run
        goes makes the bar go backwards, which an operator reads — correctly —
        as "something is wrong", about the wrong thing. -->
  - [x] S7.18.e Allow cancellation of unstarted work, explain in-flight
        behavior, and preserve completed results. Evidence: cancellation
        journey.
        <!-- CANCEL IS NOT UNDO, AND IT IS THREE ANSWERS RATHER THAN ONE. Stop
        what has not started, ask what is running, leave what finished alone. A
        control whose label or description says "undo", "revert" or "roll back"
        is read by somebody who needs the first two hundred objects back, and
        gets a stopped queue and no reversal — so the surface states that
        finished work stays finished, and the browser asserts those words appear
        nowhere in the panel.
        THE IN-FLIGHT ANSWER IS READ FROM S3.9's CANCELLATION POINT, NEVER
        GUESSED: an object inside a noncancellable region runs to completion
        because that is what the region is for, and one whose region has overrun
        its own bound is escalated and fenced because the cooperative cancel has
        already failed. Measured in the browser over one click: three different
        dispositions, the completed row not moving, and the results still on
        screen — a cancelled run whose results vanish is the worst of the three
        possible outcomes, because now nobody knows what happened. -->
  - [x] S7.18.f Allow retry only for eligible failed items with original
        approved parameters/idempotency and refreshed preconditions. Evidence:
        retry duplication tests.
        <!-- THE TWO CONSTRAINTS PULL IN OPPOSITE DIRECTIONS AND BOTH ARE
        REQUIRED. The PARAMETERS must be the ones the actor approved, because
        re-reading them from the form lets an unapproved edit ride in on an old
        approval. The PRECONDITION must be REFRESHED, because the `if-match`
        captured before the first attempt is about a version that may no longer
        exist — reusing it either fails forever or, worse, succeeds against a
        revision the approval was never about. And the idempotency key is the
        SAME key, or the retry is a second action wearing the first one's name.
        FUTILITY IS PART OF LEGALITY (S7.14's rule): a retry over a
        `handler-defect` is a second identical failure, a second audit record
        and a second bill, so the surface withholds the control and says the
        refusal in its place rather than offering it with a warning. Retryable
        composes S7.16's `CALLER_FIXABLE_FAILURES` plus the transient kinds;
        `unknown-outcome` is decided by the DELIVERY, and only `at-most-once`
        withholds it. Proved at a real server: one request per retryable object,
        each carrying the key the first attempt claimed, and no request for
        anything that succeeded. -->
  - [x] S7.18.g Export result/evidence and provide keyboard/screen-reader
        traversal of mixed status without color-only meaning. Evidence:
        accessibility tests.
        <!-- AN EXPORT THAT LISTS ONLY WHAT HAPPENED IS FILED AS WHAT THE BULK
        DID. A hundred completed rows is true, and the four hundred the run
        declined to touch are exactly the part somebody comes back for —
        S7.17's completeness rule pointed at the artifact that leaves the
        building. It is built FROM THE READING, which is a claim about what the
        page did NOT fetch: measured as zero requests at a real `node:http`
        server and zero at the browser.
        STATUS IS CARRIED BY A BORDER STYLE, NOT A COLOUR, measured under
        `forcedColors: 'active'` with the companion control that makes the proof
        non-vacuous — two states computing the SAME border colour, so the colour
        channel really was flattened, while the styles stay distinct. Every row
        states its word in its accessible name, has a box with a height, and is
        reachable by a real Tab walk; and a MIXED list offers a way to reach the
        failures directly, because three failures among five hundred rows are
        reachable the way a needle is reachable in a haystack. -->
        <!-- S7.18 rollup: 127 kit tests (111th entry point, 6108 kit tests
        total) + 46 jsdom tests + 104 real-Chromium controls, each rule with a
        NEGATIVE control driving the case it exists to catch, built from what
        the browser rendered.
        THE BROWSER FOUND A REAL DEFECT THE SOURCE COULD NOT SHOW: a concealed
        object leaked through the DRIFT list. The eligibility list filtered it
        out correctly and the drift row named it anyway, under the one label
        that makes the leak sound like a courtesy — "no longer yours to read".
        Decisions 4 and 5 look contradictory until you see they are about
        different objects: one was disclosed and then withdrawn, the other was
        never disclosed at all. `disclosedItemIds` is now an argument and the
        surface filters both lists through one predicate. Its COUNT had the same
        problem: a changed-count of 2 beside a list of 1 says "and one more you
        may not see", so the panel renders the disclosed count.
        AND THE TOOL FOUND TWO DEFECTS IN ITSELF, both of which had been
        reporting green. React renders `<button data-harness-after>` as
        `data-harness-after="true"` and never as the empty string, so the tab
        walk compared against '' , walked the whole document, wrapped, and
        reported that the order never leaves the surface. And the 360px
        negative control measured the ROOT while the overflow is taken by the
        panel's own `overflow-x: auto` — restated as three measurements (the
        container took it, the document did not move, and the container SCROLLS
        rather than clipping) it is S7.1's finding as a measurement.
        DIST-PATCH VERIFICATION, THREE WAYS: emptying
        `DRIFT_KINDS_ALSO_COUNTED_ELSEWHERE` failed one control (but only AFTER
        a control was added that drives the membership half — the first attempt
        stayed green because the rendered counts already tripped the arithmetic
        half, which is the S7.10 lesson landing for a sixth time); widening
        `RETRYABLE_FAILURES` with `handler-defect` failed one; emptying
        `TEMPORAL_INELIGIBILITY_REASONS` failed two. Restoring returned all 104
        to green. -->
- [x] S7.19 Build notification center and preference entry points. Evidence:
      unread counts reconcile with server state.
      <!-- THE FIRST DECISION IS WHAT THIS MODULE IS NOT.
      `libs/veritas/notifications` already owns the delivery system — the
      channels, providers, categories, urgencies, quiet hours, digest
      frequencies and delivery statuses. A second copy of any of those lists in
      the kit would compile, would be convenient, and would be wrong within a
      release, so the taxonomy ARRIVES as a `NotificationTaxonomy`, exactly as
      S3.2's role model arrives in `actors.ts`. The kit's contribution is the
      DRIFT, not the list. (It is also what the buildable-package rootDir
      constraint requires: this package cannot import that one.) -->
  - [x] S7.19.a Build permission-aware paginated notification list with event
        type, urgency, domain/workbench, actor-safe summary, target, time,
        read/archived state, and action. Evidence: list tests.
        <!-- THE SUMMARY IS THE LEAK, NOT THE LINK. A notification summary is
        the one string in the product that also lands in a push payload, an
        email preview and a lock screen, so it is a TEMPLATE plus FIELDS with a
        disposition each, and `renderSummary` is the only place substitution
        happens — a surface that builds the sentence itself has already had the
        chance to put the value in.
        AND THE DOOR AND THE NAMEPLATE ARE DECIDED SEPARATELY. Whether the
        reader may OPEN the target is a different question from whether they may
        know what it is CALLED, and conflating them is how a summary that
        carefully withholds a document title sits beside a target line that
        prints it. The jsdom spec found exactly that: every surface that renders
        one fact twice checks one of them. `targetLabelDisposition` is the
        second question and `withheld-value-repeated-outside-the-summary` is the
        cross-check.
        READ STATE IS NOT A SORT KEY. Sorting unread first means marking one
        read makes it jump out of the page and pulls an unseen one in, so the
        reader loses their place by doing the one thing the list is for. -->
  - [x] S7.19.b Group/deduplicate event storms while preserving distinct
        assignments, decisions, blockers, failures, mentions, and
        security/safety alerts. Evidence: grouping fixtures.
        <!-- SEVEN KINDS MAY NEVER BE COLLAPSED, and it is data rather than a
        threshold, because the argument is never about how many — it is about
        which. Each is a notice whose whole value is that one person sees one of
        them, and "and 47 others" is exactly how the one that mattered
        disappears. A group also states its distinct-TARGET count, since fifty
        notices about one object and fifty about fifty objects are different
        situations that one number cannot tell apart.
        DEDUPLICATION IS NOT GROUPING. A second delivery of one event is not a
        second thing that happened, and an event every delivery of which was
        suppressed was never counted at all — the invisible half of the
        at-least-once problem S3.7 exists for, and the half that has no symptom.
        A group's size is therefore over DISTINCT EVENT IDS. -->
  - [x] S7.19.c Implement mark read/unread, mark group/all, archive/unarchive,
        delete/retention, and target navigation with optimistic rollback.
        Evidence: lifecycle tests.
        <!-- BOTH HALVES OF A ROLLBACK ARE EASY TO WRITE AND ONE IS EASY TO
        FORGET. A failed mark-read that restores the row and not the badge
        leaves two numbers on one screen disagreeing forever, which the reader
        resolves by ignoring the badge — and the badge was the only thing that
        would have brought them back. Faults in both directions, plus the
        refusal that was kept, plus a rollback nothing explained (a row that
        silently springs back reads as a bug in the page rather than a refusal).
        A MANDATORY NOTICE MAY BE READ AND NOT LOST: `archive` and `delete` are
        refused, `mark-read` is not. A delete names the retention rule that
        permitted it, or nobody can say whether it should still have been there.
        And a sweep must state its SCOPE: "mark all read" over a filtered list
        that clears the whole inbox destroys, in one press, exactly the notices
        the filter was hiding — the set the reader was not looking at. -->
  - [x] S7.19.d Reconcile unread counts across tabs/devices/reconnect/duplicate
        delivery and never infer read state only from opening the center.
        Evidence: real-time count tests.
        <!-- OPENING THE CENTRE IS NOT READING. It is the most common defect in
        this surface and the most damaging, because the badge is the only thing
        that would have brought the reader back to the one that mattered.
        Measured in a real browser as a real document load: the badge survives
        the mount, every unread row is still unread, and nothing is written to
        the server just for looking.
        THE COUNT IS RECONCILED, NEVER ACCUMULATED. A reconnect resyncs rather
        than replaying a delta — a delta across a gap counts whatever arrived
        twice and misses whatever did not, and the whole point of a
        reconnection is that the client does not know which. The server wins,
        and a disagreement is REPORTED rather than silently overwritten, because
        it means a local action was lost and overwriting it quietly destroys the
        only evidence of that. Two real browser tabs are opened and compared. -->
  - [x] S7.19.e Expose preference entry by event/category, urgency, domain/
        project, role, channel, locale/time zone, quiet hours/digest, and
        nonmutable mandatory notices. Evidence: preference tests.
        <!-- A PREFERENCE FOR A CHANNEL THE DELIVERY SYSTEM DOES NOT HAVE IS A
        SWITCH THAT DOES NOTHING, and this is the drift check the taxonomy
        argument exists for. A switch labelled "SMS" over a deployment with no
        SMS provider reads as a promise, and the reader who turns it ON is now
        expecting messages that will never arrive — which is worse than the
        reader who turns it off.
        A MANDATORY NOTICE IS A WORD, NOT A DISABLED SWITCH. A control that
        silently refuses is worse than none, because the reader has been told
        they turned it off; measured in the browser as not a button, not an
        input, and not in the tab order.
        QUIET HOURS DELAY, THEY DO NOT SUPPRESS — dropping is a data-loss bug
        wearing a preference's clothes, and the person who set it asked for
        later, not never. The mandatory ones pierce them, which is what quiet
        hours are FOR: the things that can wait only get to wait if the things
        that cannot, do not. And the window is read in the READER'S time zone;
        the server's zone is the setting nobody notices is wrong until they are
        woken up. -->
  - [x] S7.19.f Show notification capability/degraded/delivery failure state and
        protect source/content/learner/biometric/private target detail.
        Evidence: state/security tests.
        <!-- A DELIVERY FAILURE IS ABOUT ONE CHANNEL AND MUST NAME IT. The
        banner reading "notifications are degraded" while only SMS is down is
        read by everybody, is true for almost nobody, and is dismissed — and so
        is the next one, on the day the whole thing is down. A global banner is
        honest only when every channel is impaired, which the browser drives
        both ways. Naming a channel that is working is the other half of the
        same failure.
        The protection half composes S5.3 directly: a field in one of the
        PRESENCE-DISCLOSING classes is withheld from a summary even under an
        `allow` disposition, because naming it at all answers the question — and
        a notification is the least controllable place it could be named. -->
  - [x] S7.19.g Meet keyboard, screen-reader, focus, live announcement, narrow
        layout, and long-locale requirements. Evidence: accessibility journey.
        <!-- ANNOUNCING EVERY ARRIVAL IS A DENIAL OF SERVICE ON A SCREEN READER.
        An assertive live region per arrival interrupts once per notification,
        and forty in a storm is forty interruptions in the middle of whatever
        was being read; the page is then turned off, which costs them the urgent
        one as well. Only the urgencies at or above a threshold TAKEN FROM THE
        TAXONOMY interrupt — a threshold over an unordered vocabulary is a
        guess — and everything else moves a polite count. Both regions stay in
        the accessibility tree with nothing to say.
        The announcement is the SAME string the row shows, which is the kit's,
        so a value withheld from the screen cannot be spoken — an announcement
        is read out in a room, which is the one audience nobody models.
        Read state survives `forcedColors: 'active'` because it is a border
        STYLE, with the companion control proving the colour channel really was
        flattened. Real Tab traversal reaches every notice and leaves the
        surface, and the 360px check is load-bearing: forcing the rows to nowrap
        overflows the LIST, the document still does not move, and the list
        SCROLLS rather than clipping. -->
        <!-- S7.19 rollup: 89 kit tests (112th entry point, 6198 kit tests
        total) + 28 jsdom tests + 80 real-Chromium controls, each rule with a
        NEGATIVE control driving the case it exists to catch.
        THE JSDOM SPEC FOUND A REAL LEAK: the summary correctly withheld a
        document title and the target line beside it printed the same string. I
        had conflated "may you open it" with "may you know its name"; they are
        two dispositions, and the cross-check
        `withheld-value-repeated-outside-the-summary` catches the general shape
        — a surface rendering one fact in two places checks one of them.
        THE BROWSER TOOL'S OWN RECONSTRUCTION FAILED TWICE FIRST, and both are
        worth recording. An empty `summaryFields` list makes `renderSummary`
        return the template unsubstituted, so every row was reported as a
        sentence built somewhere else; and reading the target line back as the
        NAME accuses the surface of the leak it had just prevented, because
        under a refusal that line holds the class word.
        DIST-PATCH VERIFICATION, THREE WAYS: dropping `deny` from
        `VALUE_WITHHOLDING_DISPOSITIONS` failed five controls, dropping
        `mention` from `NEVER_COLLAPSED_KINDS` failed two, and making
        `urgencyAtLeast` always true failed two. Restoring returned all 80 to
        green. -->
- [x] S7.20 Build contextual help that describes real capability and failure
      semantics. Evidence: help links are versioned with the surfaced feature.
      <!-- THE ACCEPTANCE IS THE WHOLE SUBJECT. A help link that resolves to
      "latest" is not a stale document; it is a CONFIDENT WRONG ANSWER, because
      after one release it describes a different product to somebody reading a
      screen that has not changed — which is worse than no link at all. -->
  - [x] S7.20.a Register help by capability/route/operation/field/block/gate/
        error/state/contract version and locale. Evidence: help registry.
        <!-- HELP IS KEYED BY THE OWNING REGISTRY'S OWN IDENTIFIER, never a slug
        invented for the docs, which is what turns the drift check into a set
        comparison rather than a judgement. The anchor carries the CONTRACT
        VERSION of the thing and the locale, so a key names one document about
        one version of one feature in one language. -->
  - [x] S7.20.b Write utility guidance for purpose, prerequisites, authorized
        audience, inputs/outputs, lifecycle, limitations, cost, safety/rights,
        failure/recovery, and support. Evidence: content completeness review.
        <!-- AN EMPTY SLOT IS A CLAIM, SO THERE ARE THREE STATES AND NOT TWO.
        "No limitations" and "nobody has written the limitations" are different
        sentences and a `string | null` can only say one of them. And WHICH
        slots are mandatory is DERIVED from the thing's own declared effects
        rather than remembered: an operation carrying a payment must say what it
        costs, one publishing irreversibly must say so under safety and rights.
        The purpose is the one slot never optional and the one held to a single
        sentence, because it is read in a tooltip beside a control somebody is
        already halfway through using — a paragraph there is a paragraph nobody
        reads, and everything below it then goes unread too. -->
  - [x] S7.20.c Link help at labels, states, disabled reasons, errors,
        remediation, inspectors, command palette, and documentation entry points
        without clutter. Evidence: placement review.
        <!-- THE MOST-ASKED QUESTION IS "WHY CAN'T I PRESS THIS", AND THE ANSWER
        ALREADY EXISTS — as the guard's remediation, the capability's state or
        the authorization disposition. Help that restates it in its own words
        creates a SECOND answer, and the paraphrase is the one that goes stale
        without anybody noticing, because both read plausibly. So the panel
        renders the reason verbatim and has no prop that could carry a second
        wording of it.
        AND CLUTTER IS MEASURABLE. A help affordance beside every label is not
        help; it is noise that trains people to ignore the one beside the field
        that needed it. One affordance per element, and a ceiling on the share
        of controls carrying one — counted off the rendered document, with a
        companion load that exceeds the ceiling so the measurement is
        load-bearing rather than true of any fixture. -->
  - [x] S7.20.d Resolve the exact feature/version and show stale/missing/
        incompatible/unauthorized help honestly. Evidence: version matching
        tests.
        <!-- FOUR NON-ANSWERS, FOUR DIFFERENT SENTENCES, MEASURED ON ONE SCREEN.
        Missing reads as "there is nothing to know", unauthorized as "there is
        nothing here", stale as current, and incompatible as either — and each
        sends the reader somewhere different. It is not enough for them to be
        four values in a type; the browser collects the four rendered strings
        from four affordances in one document and asserts they are distinct.
        STALE AND INCOMPATIBLE ARE SEPARATED BY THE READER'S NEXT MOVE. Stale
        help was written for THIS feature and has not caught up, so it is worth
        reading with care and its body is shown; incompatible help is about a
        version this reader does not have. Help written for a LATER contract is
        incompatible and never stale, for the same reason.
        UNAUTHORIZED IS ANSWERED BEFORE THE LOOKUP when the thing is concealed —
        an entry found and refused is an entry whose existence has been
        disclosed. And the link carries the version of the ENTRY that was
        rendered, not of the surface and not "latest", which the browser checks
        by resolving a v2 entry on a v4 surface and reading /v2/ out of the
        href. -->
  - [x] S7.20.e Generate links from the same registry/contracts as capability
        and errors and detect drift/broken anchors. Evidence: drift tests.
        <!-- BOTH DIRECTIONS, BECAUSE THEY ROT DIFFERENTLY AND ONLY ONE OF THEM
        IS EVER NOTICED. Help for something no registry declares is found by the
        first reader who follows it. A declared feature with NO help is found by
        nobody, because there is no broken link to notice — nobody reports the
        absence of a link they never saw.
        A BROKEN ANCHOR IS WORSE THAN NO LINK: it lands the reader at the top of
        a four-thousand-word page, which they read as "the answer is not here",
        and then they stop asking. Anchors are checked against what the document
        declares it contains, and a document nothing indexes is its own fault. A
        retired anchor still serving help and help left behind a contract bump
        are the two remaining directions. -->
  - [x] S7.20.f Validate concise product-language copy, keyboard/focus behavior,
        screen reader, locale, narrow layout, and no secret/protected details.
        Evidence: content accessibility tests.
        <!-- THE LEAK SCAN IS S1.10's OWN, NOT A SECOND COPY. That module already
        decided what a credential, a connection string, a stack frame and a
        cross-tenant identifier look like, and help text is the LAST place
        anybody thinks to check — which is exactly why it is where a support
        engineer pastes a working command with the token still in it.
        THE FOCUS ROUND TRIP IS MEASURED: opening moves focus INTO the panel and
        closing puts it back on the trigger, against `document.activeElement`
        after real clicks with the panel actually laid out — the case a
        `useLayoutEffect` restoration silently loses.
        AND THE BROWSER FOUND A REAL CSS DEFECT: a WIDTH bounded by the viewport
        is not a RIGHT EDGE bounded by the viewport. `min(320px, 100vw - 32px)`
        is the obvious rule and it is wrong, because the panel's ORIGIN is the
        trigger rather than the viewport — a 320px panel hanging off a control
        115px in ends at 435px and pushes a 360px page sideways. CSS cannot
        express "as wide as the space to my right" for an anchored element, so
        below 480px the panel stops being anchored and becomes a sheet held
        against the viewport, which is the only position CSS alone can
        guarantee. A locale whose words are three times as long is loaded as a
        second viewport case. -->
        <!-- S7.20 rollup: 62 kit tests (113th entry point, 6261 kit tests
        total) + 16 jsdom tests + 51 real-Chromium controls. Every one of the 37
        declared fault kinds is both raised in the source and asserted in the
        spec — checked mechanically, because a declared-and-never-fired rule is
        the shape of every unwired gate.
        THE NEGATIVE CONTROL HAD TO BE REWRITTEN ONCE, and the reason is worth
        keeping: after the sheet fix, widening the panel proved nothing, because
        a `position: fixed` element pinned to both edges does not move the
        document however wide it is told to be. The control now RE-ANCHORS the
        panel to its trigger, which reproduces exactly the defect the browser
        found. A negative control has to break the thing that is doing the work,
        not a thing that used to.
        DIST-PATCH VERIFICATION both ways: dropping `incompatible` from
        `NON_ANSWER_RESOLUTIONS` failed the four-sentences control, and dropping
        `cost` from the payment requirement in `EFFECT_REQUIRES_SLOT` failed
        two. Restoring returned all 51 to green. -->
- [x] S7.21 Add design tokens and primitives to Storybook or the repository
      equivalent. Evidence: automated interaction, a11y, theme, and visual
      regression suites.
      <!-- Closed by S7.21.a-h: typed tokens with external constraints rather
      than agreed values, nineteen primitives with no app vocabulary, the state
      matrix split by owner, fourteen contexts and the four families the
      contract cannot name, the interaction suite where a dispatched event is
      not a gesture, the accessibility coverage that leads with the thirty-two
      criteria a checker cannot reach, the pinned baselines with a fourth cause,
      and the ship boundary where three leaks in four are invisible in the
      bytes. Six browser tools, one bundler tool and seven generators, each with
      a `--check` in `verify:inventory:v1-workbenches`. -->

  - [x] S7.21.a Publish typed tokens for typography, spacing, sizing, layout,
        density, color/accent/semantic state, border/radius, elevation, motion,
        z-index, and data visualization. Evidence: token docs.
        <!-- `tokens.ts` already carried six of the twelve — colour, spacing,
        radius, typography, motion, elevation. The other six are in
        `scale-tokens.ts`, kept separate because they share one property the
        first six do not: EVERY ONE OF THEM HAS AN EXTERNAL CONSTRAINT THAT CAN
        BE CHECKED RATHER THAN AGREED. A spacing ramp is a house style and a
        test pinning `md === 16` only records that somebody typed 16. A target
        size is not: WCAG 2.5.5 says 24 CSS pixels and 2.5.8 says 44, and the
        touch density takes the AAA figure because a touch density that does not
        is a density with no reason to exist. So the spec asserts the CONSTRAINT
        — the tokens can be retuned and cannot be retuned past the rule.
        CONTRAST IS THE WRONG METRIC FOR A CATEGORICAL PALETTE, and writing the
        test is what showed it. A contrast ratio is a ratio of LUMINANCE, and a
        palette separated by hue has near-identical luminances on purpose —
        that is what makes it unordered. Requiring 3:1 between every pair would
        have forced it into a ramp and destroyed the property it exists for,
        while two colours at delta-E 60 sit at contrast 1.1. So separability is
        measured in CIELAB with CIE76 delta-E (a documented approximation:
        CIEDE2000 is more accurate and forty lines longer, and at the 25
        threshold they differ by units rather than by verdict), and contrast is
        kept for the question it really answers — whether a mark is readable
        against the surface behind it. There is a test that DEMONSTRATES the
        point: the palette is separable and would fail a contrast rule.
        Both transforms are checked against published values rather than against
        themselves — L* 100 for white, 53.585 for #808080, and 53.24/80.09/67.20
        for sRGB red, which a transposed matrix row still misses while giving a
        plausible-looking number. White lands at 100.0000039 rather than 100
        because the published D65 white point is rounded to five places, and the
        test says so instead of nudging the white point until it passes, which is
        how a colour pipeline ends up half a standard.
        The other constraints: a sequential ramp STRICTLY MONOTONIC in luminance
        (a ramp that is not stops encoding order the moment it is printed, and
        the failure is invisible to whoever picked it); a diverging ramp lightest
        at its midpoint and odd-length, or it has no midpoint to be neutral at; a
        z-index scale whose members are DISTINCT (or the stacking is decided by
        document order) with gaps of 100 so a layer can be inserted without
        renumbering, and a stated narrative the tests hold to; a focus ring as
        its OWN token at the WCAG 2.4.13 2px floor, so retuning the border scale
        cannot thin it by accident; breakpoints strictly ascending from 360,
        which is the width every browser tool in this repository measures; and a
        reading measure inside the 45-75 character range. 52 tests. -->
  - [x] S7.21.b Publish base controls, forms, status, navigation, overlay,
        workspace/inspector, table/list/tree, feedback, and accessibility
        primitives without app-specific vocabulary. Evidence: primitive index.
        <!-- Nineteen primitives across the nine groups, in
        `apps/oshun/web/src/components/workbench/primitives`, with their rules in
        `@oshun/workbench-kit/primitives` and their index generated from the
        source by `generate-workbench-primitive-index.mts`.
        A PRIMITIVE IS EXACTLY THE DECISIONS THE APP MAY NO LONGER MAKE, and it
        fails in two opposite directions that are each invisible from inside the
        other. Too specific is a domain word in a shared file. Too generic is a
        div with props — it moved nothing, so every consumer still decides the
        focus ring, the target size and the disabled semantics, and the "system"
        is a naming convention. `primitive-owns-no-invariant` is the direction
        nobody reports, because a component that decides nothing never breaks.
        THE CONTRACT ALREADY EXISTED AND HAD NO CONSUMER. ADR-S4 declared nine
        categories of `--wk-*` role names scoped to `[data-workbench-theme]` at
        S0.9, and before this item the string `--wk-` appeared in four places —
        the ADR, the generator that emits its prototype stylesheet, that
        stylesheet, and its README — and in no component. What shipped instead
        was a second undeclared palette: twenty `--wb-*` properties defined
        INSIDE one component's CSS module, plus raw hexes that near-miss those
        values (`#16181d` beside `rgb(16, 24, 40)`, which is `#101828`). The
        primitive layer is that contract's first reader.
        AND THE CONTRACT'S OWN GATE RAN IN ONE DIRECTION. It checked that every
        declared role appears in `tokens.css` and never the reverse, so four
        `-fg` roles the stylesheet has emitted since S0.9 were declared by no
        category. A missing role is found by the first host that sets it; an
        undeclared one is found by nobody, because there is no broken reference
        to notice. Both roles and the reverse check are now in the generator.
        APP VOCABULARY LEAKS AT FOUR LAYERS AND ONLY THE FIRST IS OBVIOUS: the
        NAME, the PROP NAMES, the ENUMERATED MEMBERS, and the DEFAULT TEXT. The
        last is the one that hides — a component can pass the other three and
        still carry a domain sentence in a default argument, which is the string
        nobody reads at the call site. So a primitive renders no text it was not
        given, and the tone glyphs are allowed because a SYMBOL IS A CHANNEL AND
        A WORD IS VOCABULARY: `✓` says the same thing in every language on the
        roadmap and "Passed" says it in one.
        THE FILTER THAT MAKES A LEXICON A LEXICON IS A CITATION, NOT A
        STATISTIC, and measuring is what showed it. A lexicon derived from 104
        kit vocabularies is a dictionary — it convicts `Table`, `TextInput` and
        every honest primitive. The obvious repair is frequency, and it runs
        EXACTLY BACKWARDS: `stale` appears in 23 modules here, `concealed` in 11
        and `quarantined` in 8, while `modal`, `toggle`, `orientation` and
        `density` appear in ONE each, because a domain reuses its own words
        hardest and reaches for a platform word about once. So the filter is
        `PLATFORM_VOCABULARY` — the words of HTML, ARIA, CSS and the DOM, which
        are the words a primitive is written in — and running it renamed
        `TreeView` to `Tree` and `InspectorPane` to `DetailPane` rather than
        being tuned around them.
        A DESIGN SYSTEM ALREADY EXISTED AND MOST OF IT IS THE APP.
        `src/design-system/components` holds 63 components, and beside `Button`
        and `Divider` sit `GroundingStateBadge`, `ProvenanceBadge` and
        `HabitCheckbox`. `classifyComponents` publishes the separation rather
        than fixing it — one product's system carrying that product's words is
        CORRECT, and the fault would be shipping them in the layer seven
        workbenches share. Calibrating it was the work: the first rule was "a
        vocabulary that is not presentational", which needed nothing external
        and convicted `Card` of `elevated|glass`, `Avatar` of `xs|sm|md` and a
        polymorphic row of `div|span|article`. It had discovered that the
        exemption list is not "the presentational vocabulary" but "the words
        these nineteen primitives happen to use" — A CLOSED VOCABULARY IS A
        PROMISE THE PRIMITIVE LAYER MAKES ABOUT ITSELF, not a test that can be
        applied to somebody else's component. Recalibrated it finds 55 of 63,
        including `Spinner`, whose name is as generic as exists and which ships
        the word "Loading".
        THE BROWSER FOUND FOUR THINGS SOURCE-READING COULD NOT. (1) THE ROVING
        TABINDEX WAS HALF-IMPLEMENTED: an arrow press moved the selection and
        left focus on a tab whose tabindex had just gone to -1, so the next press
        came from nowhere. Focus moves in an EFFECT keyed on the selection, not
        in the handler, where the new tab has not rendered yet. (2) NO
        `box-sizing: border-box`, so the detail pane's own border put it at 102%
        and overflowed a 360px container by two pixels. (3) THE WCAG FLOOR NEVER
        BOUND: with fixed padding a control's intrinsic text height already
        clears 24px at every density, so the `max()` was guarding nothing and a
        control asserted at the shipped densities passes with or without it.
        Padding now scales with the density too, and the tool drives the density
        to 0.1 to put the floor in the regime it exists for. (4) THE FIRST
        VERSION OF THAT CONTROL PASSED AT A DENSITY THAT WAS NEVER APPLIED,
        because the override went on the theme scope and the Region's own
        `data-density` rule is more specific.
        AN UNRESOLVED CUSTOM PROPERTY IS INVISIBLE, which is why the token proof
        is a COMPUTED value taken twice — inside the scope and outside it. A
        `var()` naming an undefined property is invalid at computed-value time:
        an inherited property takes its parent's value and a non-inherited one
        takes its initial value, nothing throws, and `color` in particular lands
        on something that looks deliberate. The outside copy is what catches a
        hardcoded value or a `var(--x, #fallback)`, and the two halves are proved
        SEPARATELY — one control drives a hardcode (both fire) and one drives a
        fallback that survives with no theme (only the second fires).
        Also repaired here because it guards this registration: the S1.2 export
        map's test had pinned 46 subpaths by hand and the kit passed 100, so the
        gate had been red since 2026-08-03 and every S7 item since had widened
        the gap unnoticed — the exact failure the generator's own "the approved
        set is DERIVED, never authored" comment exists to prevent. Five
        assertions now derive what they used to restate, and a bogus entry point
        injected into the generator still fails two of them.
        44 browser controls, 63 kit tests, 43 jsdom tests, 13 generator tests.
        DIST-PATCH VERIFICATION three ways: silencing `token-not-load-bearing`
        failed two controls, widening `PRIMITIVE_CONSUMED_CATEGORIES` with
        `data-visualization` failed two, and silencing
        `primitive-owns-no-invariant` failed one. Restoring returned 44/44. -->

  - [x] S7.21.c Add default, hover, active, focus, selected, disabled,
        read-only, loading, empty, error, stale, fixture, degraded, conflict,
        and destructive stories. Evidence: state coverage.
        <!-- 57 stories in 20 CSF files beside the primitives, the matrix in
        `@oshun/workbench-kit/primitive-states`, and
        `evidence/v1-workbenches/workbench-primitive-state-coverage.json`.
        A STATE MATRIX WITH ONE AXIS HAS ALREADY LOST, BECAUSE THE FIFTEEN
        STATES ARE OWNED BY THREE DIFFERENT PARTIES. The BROWSER owns hover,
        active and focus: nobody sets them, no argument produces them, and a
        story that RENDERS one is a picture of a state the product does not
        have — it reviews well, it passes a visual baseline, and the real hover
        can be doing nothing at all. The COMPONENT owns whichever of the rest it
        has an argument for, which is a FACT read out of S7.21.b's parsed index
        rather than a decision. The COMPOSITION owns the five no primitive has an
        argument for anywhere — stale, fixture, degraded, conflict, destructive —
        which are exactly the words S7.21.b forbids a primitive to know.
        AND A COMPOSITION STATE IS A CLAIM ABOUT THE LAYER, NOT ABOUT EACH
        COMPONENT. The first version of the module asked it per primitive: 5
        states × 19 components = 95 cells, of which "a stale VisuallyHidden" is
        representative — not a gap in the coverage, a sentence with no meaning.
        Worse, it would have been answered by somebody adding `tone="stale"`,
        which is how the vocabulary rule gets undone: not by anybody arguing with
        it, but by a story that was awkward to write. `NEVER_A_COMPONENT_STATE`
        refuses to call one of those a component state however the API changes.
        INTERACTIVITY IS MEASURED, NOT DERIVED. Reading it off the API looked
        possible and gets `Field` wrong (its `children` is a render prop, so it
        parses as a handler) and `NavList` wrong (it takes no handler and is a
        list of links). Whether something can be hovered is a fact about what it
        renders, so the browser reports it and the kit takes the report — which
        also means the coverage generator REFUSES to run without the measurement
        rather than quietly marking those cells inapplicable, because a coverage
        report that improves when the browser tool stops being run is worse than
        none.
        THE BROWSER FOUND THREE THINGS. (1) THE LAYER HAD NO POINTER STATES AT
        ALL — hover, active and rest computed the same canonical string on every
        control, so nothing in it answered a pointer. Two rules fixed it, drawn
        from contract roles, with `opacity` for the press because it survives
        forced colours where every colour collapses. (2) A TREE ITEM TOOK A CLICK
        AND SAID SO NOWHERE: no `cursor: pointer`, so `hover` measured as
        INAPPLICABLE to the one collection primitive that is operated. (3) THE
        RULE ABOUT FOCUS RINGS WAS WRONG FOR TEXT FIELDS, and the browser is the
        authority: `:focus-visible` MATCHES a clicked input, because somebody who
        clicked into a field is about to type and needs to see where. A text
        field also has no press state at all — a pointer down places a caret
        rather than taking an action — so one measured `acceptsText` flag exempts
        it from both the ring rule and the press comparison.
        The measurement also settled two story sets against me and was right
        both times: a CLOSED `ModalDialog` and a `Popover` panel whose own box is
        `tabindex="-1"` have nothing to hover, so their interaction stories were
        removed rather than the check being softened.
        16 browser controls, 34 kit tests, 57 of 57 applicable cells told (21
        browser-owned, 31 component-owned, 5 layer compositions). -->

  - [x] S7.21.d Add comfortable/compact/touch, theme/forced-colors, narrow/wide,
        zoom, reduced-motion, RTL/bidi, long-string, and font-fallback stories.
        Evidence: cross-context coverage.
        <!-- Fourteen contexts across the parent's eight families, in
        `@oshun/workbench-kit/primitive-contexts`, with nineteen stories beside
        the primitives and `tools/primitive-contexts-browser.mjs` driving every
        one of them in a real browser.
        THE CONTRACT'S CONDITIONS ARE THE ONES A VALUE CAN ANSWER, AND THE ONES
        THAT BREAK A LAYOUT ARE THE OTHER FOUR. ADR-S4's a11y matrix names five —
        theme, density, zoom, reduced motion, forced colours — and S7.21.b made
        every primitive declare which of them it answers. Every one of the five
        is answered by SUBSTITUTING A VALUE: a colour role changes, the density
        multiplier changes, a duration goes to zero. The four families the
        contract does not name are exactly the four no substitution can answer:
        no token makes a control fit in less room, no token mirrors an inset
        box-shadow because `box-shadow` has no logical form at all, no token
        makes a sixty-four character digest break, and no token gives a
        substituted face the metrics of the one that was asked for. So a
        primitive can declare all five, pass every check the index makes, and be
        broken in half the families — measured here at four to seven unclaimed
        families for every visible component, and the contract's own matrix makes
        that invisible because it looks complete.
        A CONTEXT IS A PROPERTY OF THE DOCUMENT, NOT AN ARGUMENT TO A COMPONENT.
        You cannot put one button into forced colours while the button beside it
        stays as it was, so the unit is ONE story per context whose subject is
        every primitive that context reaches. A per-(context, primitive) grid is
        266 pictures of something that cannot happen, and — S7.21.c's lesson,
        learned the same way — it would have been answered by somebody adding a
        `forcedColors` prop. The gallery is what makes the subject derivable
        rather than remembered: a twentieth primitive fails every context story
        until it is in there.
        EVERY CONTEXT NEEDS TWO MEASUREMENTS AND THE FIRST IS THE ONE EVERYBODY
        FORGETS — was it APPLIED, and did the layer SURVIVE it. Three prior
        sightings in this repository of the first going missing (S7.14's
        forced-colours companion, S7.21.b's density applied to a scope a more
        specific rule defeated, S7.10's gate answering an empty list), so each
        context declares a proof of application and the invariants are SKIPPED
        when it fails. And application is a property of the DOCUMENT while
        answering is a property of the PRIMITIVE: a density that moved twelve
        primitives and not the thirteenth arrived perfectly well, and the
        thirteenth is S7.5's checkbox. Collapsing the two files a real defect as
        a broken harness.
        WHICH COMBINATIONS MATTER IS DERIVED, NOT CHOSEN. Fourteen contexts is
        16,384 combinations. Each declares the BUDGET it spends and the SIGN of
        its effect, and two must be told together exactly when one raises demand
        on a budget the other lowers the supply of, or when both substitute the
        same channel — fourteen pairs, of which five need a story because both
        halves are the story's to render. `reduced-motion` pairs with NOTHING,
        because nothing else spends the motion budget, and that absence is a
        result rather than an omission.
        TWO SUBSTITUTIONS ON ONE CHANNEL DO NOT BOTH APPLY, AND THE OBVIOUS WAY
        TO MEASURE THE PRECEDENCE IS WRONG. "The reader's palette beats the
        host's" is not "the rendering is identical to forced colours alone": the
        user agent forces the USED colour and leaves the declaration alone, so a
        host that named `LinkText` survives forced colours legitimately while a
        host that named its own blue is flattened to `CanvasText` — two different
        renderings, both correct. Asserting equality reported five primitives as
        leaking. What a host may never do is get a colour of its own onto the
        screen, so the rule is that every used colour is one the reader's palette
        supplied, with the palette RESOLVED from the browser rather than listed.
        ZOOM IS TWO MECHANISMS AND THEY FAIL IN OPPOSITE DIRECTIONS. The user
        agent's text zoom scales the root font size, so every `rem` grows —
        while a media query's `rem` does not, because font-relative units there
        resolve against the INITIAL font size, and a breakpoint therefore slides
        away from the panel it guards. The contract's own `--wk-text-scale` is
        the reverse: only the declarations that read it grow, so type gets bigger
        inside boxes that did not. One name for both would have tested whichever
        one somebody implemented.
        THE BROWSER FOUND SEVEN REAL DEFECTS, none of them visible in source.
        (1) `NavList` and `Tree` marked the current and selected rows with an
        INSET BOX-SHADOW, which has no logical form — so the marker drew on the
        physical left in a right-to-left layout, on the trailing edge of every
        row — and which forced colours forces to `none`, so the one channel the
        marker had disappeared for exactly the reader who set the palette. One
        `border-inline-start` fixes both. (2) `NavList`, `Tabs` and `Tree` are
        interactive targets that answered NO DENSITY at all: their padding read
        `--wk-space-*` directly, so a host setting `[data-density]` got bigger
        buttons beside navigation that did not move. (3) `--wk-font-sans` and
        `--wk-text-scale` had NO BASE READER — ten of the seventeen visible
        primitives did not move when either was retuned, and every primitive that
        declared no `font-size` rendered in the surrounding application's
        default. (4) `InlineMessage` distinguished its six tones with a border
        COLOUR alone, so two tones computed the same appearance under forced
        colours; it now carries `StatusMark`'s glyph table rather than a second
        one. (5) At 1600px the region and the detail pane set prose at 129 and
        177 characters: S7.21.a published the 45–75 measure as a constraint and
        nothing read it, so the contract gains a `--wk-measure` role and the text
        — not the box — is capped. (6) A sixty-four character digest at 360px
        pushed the DOCUMENT sideways from a table cell, a status mark and a
        button. (7) An `<input>` sizes itself from `size`, which is a width in
        ems, so a reader who doubles the text size at 360px gets a field 31px
        wider than the document — and NEITHER context alone shows it.
        Harness lessons worth keeping: THE REFERENCE FOR ONE HALF OF A PAIR IS
        THE OTHER HALF ALONE, because measured against the plain baseline "did
        the layout mirror" is asked of a document that also doubled its type and
        every mirrored surface comes back broken; the pair's scope is the
        INTERSECTION, or a detail pane gets asked about a touch density; the
        mirror anchor is the first LINE BOX rather than the first element child,
        which changes with the content; a form control's hidden overflow is
        reachable with the caret and is not clipped content; and the reading
        measure is the longest rendered LINE, because a seventeen-character label
        in a 1600px box is not a 180-character line.
        42 browser controls, 56 kit tests, 32 jsdom tests, 181 of 181 in-scope
        cells measured. DIST-PATCH VERIFICATION three ways: emptying
        `SUBSTITUTION_BUDGETS` failed the pair control, silencing the target-floor
        comparison failed its negative control, and NARROWING `viewport-narrow`'s
        required feature to `motion` changed nothing at all until a control was
        added that the viewport family reaches every primitive the browser saw on
        the screen — S7.10's asymmetry again, and the fourth sighting of a scope
        that can only be narrowed. Restoring returned 42/42. -->
        <!-- S7.21.f/g/h remain: automated WCAG checks, visual baselines, and
        the bundle scan. -->

  - [x] S7.21.e Add play-function interaction/keyboard/focus/announcement/
        pointer-alternative tests. Evidence: passing interaction suite.
        <!-- Fourteen play functions in `primitives/interactions.stories.tsx`,
        the model in `@oshun/workbench-kit/primitive-interactions`, the
        measurement in `tools/primitive-interactions-browser.mjs` (75 controls),
        and the coverage in
        `evidence/v1-workbenches/workbench-primitive-interaction-coverage.json`
        at 38 of 38 cells.
        THE PARENT'S FIVE WORDS ARE NOT FIVE WAYS OF SAYING THE SAME THING, and
        only the first is about the person who performed the gesture. Interaction
        asks whether it produced the result it names; keyboard asks the same
        question of somebody who cannot point, focus of somebody who cannot see
        where the page went, announcement of somebody who cannot see the page at
        all, and pointer-alternative of somebody who cannot execute a path. The
        four that follow are ONE QUESTION ASKED OF FOUR OTHER READERS, and a
        component can answer the first perfectly while failing all four — which
        is what this layer was doing in four separate places, having passed every
        check S7.21.b, S7.21.c and S7.21.d make.
        EVERY OPERATION NEEDS BOTH PATHS AND THE TWO GAPS ARE INVISIBLE TO
        OPPOSITE PEOPLE. A pointer-only operation is invisible to whoever built
        it with a keyboard and a keyboard-only one to whoever built it with a
        mouse; nobody drives both, so the check is a MATRIX of operation by path.
        And the halves are not equally defended: WCAG 2.1.1 requires the keyboard
        path and NO success criterion anywhere requires the pointer one. That is
        why `Tree` shipped with a disclosure a mouse could not reach — clicking
        selected, there was no affordance that toggled, and ArrowRight was the
        only way into a branch — and why `Popover` shipped able to be dismissed
        with Escape and by nothing a pointer could do. Both passed every
        automated accessibility checker ever written, because none of them looks.
        THE OTHER TWO DEFECTS. `Tree` had exactly ONE tab stop and no arrow
        navigation at all, so a keyboard reader arrived at the first node and
        that was the entire tree — while the component's own invariant said it
        was "walked with the arrow keys as WAI-ARIA's pattern requires".
        `ModalDialog` called its caller TWICE for one Escape, because the press
        fires `cancel` and then `close` and both were wired; `setOpen(false)`
        twice survives that and a handler that pops a stack does not, and both
        halves read correctly in source.
        A DISPATCHED EVENT IS NOT A GESTURE, WHICH IS THE TRAP IN PLAY FUNCTIONS
        SPECIFICALLY. `userEvent.click` constructs an event sequence and hands it
        to a node; a real pointer is hit-tested first. Measured here: with a
        modal `<dialog>` open, a dispatched click on the button behind it ran the
        button's handler and a real click at the same coordinates did not. A play
        function is therefore capable of asserting a press no user can make, in
        green. The harness then produced the same finding again by accident — the
        obvious way to write "dismiss the popover by pressing outside" pressed a
        control the 320px panel was drawn straight over, and only the real
        pointer noticed. So the play functions are written against an input
        INTERFACE, the runner decides which implementation they get, every
        observation records which one it was, and the kit refuses to settle
        `keyboard`, `focus` or `pointer-alternative` from a constructed event.
        Two of the fourteen plays pass under real input and fail under
        `userEvent`, which is the evidence that the distinction is observable.
        BEING ASSOCIATED IS NOT BEING ANNOUNCED. There are two ways a change
        reaches somebody who is not looking — focus lands on the thing that
        changed, or a live region says so — and `aria-describedby` is neither,
        because a description is read when focus ARRIVES and a validation error
        exists precisely because focus is already there. `Field` rendered its
        error, associated it correctly, satisfied every check of its own wiring,
        and said it to nobody. The fix is a live region that is OLDER than its
        message, deliberately not hidden while empty, since `display: none` takes
        an element out of the accessibility tree and a region that hides itself
        re-enters it together with its first message.
        RUNNING THE RULE CORRECTED IT THREE TIMES, each worth keeping. `open` on
        a `<dialog>` is the platform's visibility mechanism rather than a state
        anybody is told, so leaving it among the announced attributes convicted a
        dialog of closing correctly. A change inside an `aria-hidden` subtree is
        not an announcement question at all, which is what the disclosure glyph
        is. And a set that permits exactly ONE holder says everything about the
        losers by naming the winner — announcing the tab that just went
        unselected is "Overview, not selected" on every arrow press, so
        `implied-by-the-single-selection` is a third announcing route, narrowed
        to `aria-selected`/`aria-current`/`aria-checked` because two branches can
        both be open and two filters can both be on.
        `reached` IS DERIVED FROM THE CANONICAL STATE AND NEVER REPORTED BY THE
        HARNESS: a tool that hands over its own yes-that-worked boolean can hand
        it over for a gesture that did nothing, which is the shape of every
        unwired gate this phase has found. Which also caught a story of its own —
        a popover whose canonical state was a boolean is back where it started by
        the time the tool reads it, because the play has to open the panel before
        it can close it.
        A FEW OPERATIONS GENUINELY HAVE ONE PATH AND THE EXEMPTION IS CHECKED
        RATHER THAN BELIEVED. Typing has no pointer gesture — the pointer path
        for text entry is an on-screen keyboard, which is the platform's — and a
        modal's dismissal control is the caller's content, because a shared layer
        drawing its own "Close" would be putting this file's wording on every
        product's screen. Both are sentences rather than flags, both leave no
        cell in the denominator, and an exemption for a path the measurement
        finds anyway is reported as `path-exempted-and-then-found`.
        S7.21.C'S INTERACTIVITY MEASUREMENT IS THE WRONG EVIDENCE HERE and the
        first full run proved it: "an operation on a primitive with nothing
        operable in it" convicted `Popover` and `ModalDialog`, because that
        measurement is taken over S7.21.c's RESTING stories, where an overlay is
        closed and has no box. The parsed index answers the useful question
        instead, in the direction nobody reports — WHICH HANDLER PROPS NOTHING
        DRIVES. And a focus outcome had to become PER PATH, because Escape says
        only "take this away" while a press outside also says "I am over here
        now"; one answer for both convicts whichever half it was not written for.
        A CONTAINER'S CENTRE IS NOT A POINT ON THE CONTAINER, which the parent's
        own S7.21.c tool found out the moment an open branch drew its children
        under its row rather than beside it: the geometric centre of the branch
        landed on a child, the press focused the child, and three controls
        reported a working component as having no pointer states. Every pointer
        gesture there now aims at the element's own first row.
        75 browser controls, 59 kit tests, 18 jsdom tests, 38 of 38 cells.
        DIST-PATCH VERIFICATION three ways: emptying `SINGLE_HOLDER_ATTRIBUTES`
        failed one control, making `reached` always false failed twelve, and
        silencing `focus-left-outside-the-tab-order` failed its negative control.
        Restoring returned 75/75. -->
        <!-- S7.21.f/g/h remain. -->
  - [x] S7.21.f Run automated WCAG checks and real semantic assertions for
        complex widgets. Evidence: passing accessibility suite.
        <!-- `primitive-accessibility.ts` (the standard, the dispositions and the
        widget contracts), `tools/primitive-accessibility-browser.mjs` (axe-core
        over every subject plus the computed accessibility tree read through
        CDP), `__fixtures__/primitiveAccessibilityHost.tsx` and
        `primitiveStoryKinds.ts`. 44 browser controls, 68 kit tests, 39 fault
        kinds, 55 criteria each with exactly one derived disposition.
        A PASSING AUTOMATED SUITE IS QUOTED AS CONFORMANCE, AND THE COVERAGE IS
        THEREFORE THE DELIVERABLE. Measured against the installed axe-core rather
        than argued from a blog post: 104 rules, 74 carrying a WCAG
        success-criterion tag, and between them they touch 23 of the 55 Level A
        and AA criteria in WCAG 2.2. So the very best an automated pass can mean
        is that a fifth of the standard was checked and nothing whatever was said
        about the other thirty-two, while the artifact everybody forwards reads
        "0 violations". `suiteHeadline` is BUILT from the counts (S7.14's rule)
        and the run reports 30 of 55 answered by a machine — 8 by the checker, 22
        by controls here — with 11 the host's, 14 not exercised, and nothing left
        unanswered.
        `inapplicable` AND `incomplete` BOTH READ AS GREEN AND NEITHER IS AN
        ANSWER. A rule that matched no node is the empty-argument-answering-no-
        problems shape this repository has now found in six modules; `incomplete`
        is the checker saying it could not tell, dropped by almost every
        integration because the line everybody prints is `violations.length`.
        Only `passed` and `violated` are evidence. Measured here, `color-contrast`
        comes back INCOMPLETE over the tone glyphs — "element content contains
        only non-text characters", a correct refusal about a real question, since
        the glyph is the second channel S7.21.d added so a tone survives forced
        colours. THE CHECKER'S THIRD ANSWER IS WHERE THE WORK IS, so a resolution
        names a control here, the control measures the glyph at the 3:1 non-text
        floor, and `resolution-for-a-rule-that-decided-on-its-own` catches the
        other direction.
        THE ATTRIBUTE IS THE INPUT AND THE ACCESSIBLE NAME IS THE OUTPUT.
        Everything S7.21.b through S7.21.e asserts is markup, and a screen reader
        reads none of it. Reading the COMPUTED tree found two live defects in the
        first pass: `Meter`'s `aria-labelledby` named an element no story
        rendered, so a progress bar was called nothing while every check of its
        markup passed; and every `Tabs` in the layer declared `aria-controls`
        pointing at a panel that was not in the document — from a component whose
        own invariant said the panel was "reachable from the tab rather than only
        by sight". The strip also wrote the CALLER'S tab id straight into the
        document's global id namespace, so two strips sharing a vocabulary
        collided. Both are the arrangement `Field` exists to prevent — three
        identifiers that have to agree, written by hand in two files — so the
        panels are the component's now, `aria-controls` appears only when there
        is a panel to point at, and the ids are namespaced by `useId`. The
        interactions file had the same defect independently: it rendered only the
        SELECTED panel, so the other tab pointed at nothing.
        THE INSTRUMENT HAS A RANGE AND IT IS PART OF THE COVERAGE. The protocol
        that reports the tree carries no `aria-rowcount`, `aria-rowindex`,
        `aria-posinset` or `aria-setsize` — exactly the properties a windowed
        table's first invariant is about. Asserting the DOM attribute quietly
        instead would claim tree-level evidence the suite does not have, and
        dropping the check would report a green table. So the observable set is
        MEASURED from a reference document, a contract property outside it is
        answered at the DOM with the caveat recorded, and
        `settled-from-the-dom-with-no-caveat` catches the arrangement where
        somebody forgets. A gap in the reference document showed up as a caveat
        rather than a silent pass the first time it ran, which is the arrangement
        working — the gap was the reference's.
        WRITING THAT CONTRACT DOWN FOUND A REAL DEFECT IN THE TABLE. WAI-ARIA
        counts every row a table draws, header included; its own example is a
        thousand records at `aria-rowcount="1001"` with the header at index 1.
        This component draws a header the caller never mentions and was neither
        numbering nor counting it, so a caller passing the count of its records
        and the absolute position of each described a table one row shorter than
        the one on the screen with every position one out. The fixture that
        showed it had a header at row 1 and a first record also at row 1. The
        offset is the component's, because a caller counts the rows it HAS and
        this counts the rows it DRAWS.
        AND THE ONE WORTH THE ITEM: A LAYER CAN MAKE A CRITERION IMPOSSIBLE FOR
        EVERY HOST BUILT ON IT AND NOTHING ANYWHERE WILL SAY SO. WCAG 1.3.5 asks
        a field collecting information about the person using it to declare that
        purpose. `TextInput` owns the `<input>` and had no `autocomplete`
        argument, so no product on the layer could ever comply — and the checker
        reports the rule INAPPLICABLE, because the attribute that would have been
        wrong is absent. Silence from every direction about a criterion the layer
        had removed from the table. So a criterion may be delegated to a host only
        by naming the HANDLE the host reaches for, the handle is checked against
        S7.21.b's parsed index, and `delegation-whose-handle-the-layer-does-not-
        offer` is the fault. Its negative control removes `autocomplete` from the
        index and watches it fire.
        A FRAGMENT IS NOT A SMALLER USAGE, AND A CHECKER RUN OVER ONE DAMAGES THE
        LAYER. `TextInput` takes its name from the `Field` around it by design, so
        a `TextInput` fragment has no name and cannot be given one; run the naming
        rules over it and the only way to green is an `aria-label` on a component
        that deliberately has none, which is how a design system ends up with
        `aria-label="Text input"` everywhere and a label rule true nowhere. The
        default is `usage` so a new story is held to the full rule until somebody
        writes down why not, the obligation MOVES rather than vanishing (every
        bound control needs a complete usage somewhere), and a fragment may omit a
        name while never pointing at one that is missing.
        A HOST RULE IS A REDIRECTION, NOT AN EXCUSE. A gallery of nineteen
        primitives has five `<nav>`s with one name between them and no `<main>`,
        and a checker is right about all of it and none of it is a component's.
        Twenty-eight rules are classified as the document's WITH A REASON EACH,
        a host classification naming a rule the checker does not have is a fault,
        and a criterion whose only automated evidence is a host rule is reported
        as the host's rather than as passing. The off-by-default set is READ OUT
        OF THE CHECKER and it is two mechanisms: an `enabled` flag on eight rules,
        and a separate run-time exclusion of the seven tagged experimental —
        reading only the first left seven rules looking unclassified.
        EVERY EXCLUSION CARRIES WHAT WOULD REFUTE IT, over four channels: an
        element the browser would have to find, a rule that would have to fire, a
        stylesheet declaration, and the layer's own source — the last because
        WCAG 2.5.4 is about an API that leaves no element and no declaration
        behind it, and an exclusion nothing can contradict is a shrug.
        RUNNING THE RULES CORRECTED TWO OF THEM. `aria-controls` on the unselected
        tab of a WAI-ARIA tab strip names a panel that is `hidden`, exactly as the
        pattern prescribes, so the user agent drops the relation from the tree;
        treating that as a dropped property convicted the pattern of following
        itself, and relations moved out of the state table because a relation's
        presence is a fact about its TARGET. And the layer was reported as
        reordering its own content on the strength of a border radius, because
        `order:` is a substring of `border:` and `direction:` of
        `flex-direction:` — a property name is matched as a property.
        THREE HARNESS DEFECTS WORTH KEEPING. The focus-ring control resolved two
        custom properties through a probe span and reported 1.00:1, because
        `--wk-focus-ring` is a whole `outline` shorthand rather than a colour and
        the surface role it was compared against is a name the contract does not
        have — two unresolvable properties compute the same inherited colour, so
        the control was comparing a value with itself and calling the answer a
        failure. `getComputedStyle` returns `color(srgb 0.92 0.92 0.92)` for
        anything that came through a `color-mix`, which a parser that only knew
        `rgb()` read as null. And the three WCAG 1.4.13 questions cannot be asked
        of a story whose `onClose` is `() => undefined`: Escape "failed" to close
        a panel that had no close handler, which is a harness reporting its own
        fixture as a defect.
        `SPECIFIED_VOCABULARIES` IS A THIRD ANSWER TO "WHOSE WORD IS THIS".
        S7.21.b had two — presentational, or a domain word one workbench chose —
        and `email`, `postal-code` and `given-name` are neither: they are the HTML
        autofill tokens WCAG 1.3.5 names, implemented by the browser, and a
        vocabulary this layer may not extend or cut down. Parking thirty-nine of
        them in PRESENTATIONAL_VOCABULARY would claim `bday-month` is a way of
        drawing something and would exempt the word everywhere. The exemption
        names one prop on one primitive and substitutes a STRICTER test, checked
        both ways: a member the standard lacks is a fault, and a token the prop
        refuses is a purpose no host can state.
        DIST-PATCH VERIFICATION three ways: silencing `computed-name-is-empty`
        failed its negative control, emptying `INCOMPLETE_RESOLUTIONS` failed four
        controls and dropped 1.4.3 from automated to unanswered, and dropping
        `expanded` from `STATE_PROPERTY_SOURCES` failed the dropped-property
        control. Restoring returned 44/44. -->
        <!-- S7.21.g/h remain: visual baselines, and the bundle scan. -->
  - [x] S7.21.g Pin visual baselines for intended compositions and fail on
        accidental token/layout/state changes across themes/viewports. Evidence:
        visual regression report.
        <!-- `primitive-baselines.ts` + `baselines.stories.tsx` (four intended
        compositions) + `baselineCompositions.ts` (the declaration module both
        sides read) + `primitiveBaselinesHost.tsx` + `primitive-baselines-browser
        .mjs` (37 controls) + `generate-workbench-primitive-baseline-coverage.mts`
        (the report, with `--check` in `verify:inventory:v1-workbenches`), over a
        committed recording in `workbench-primitive-baseline-pin.json` and a
        hand-edited approval ledger beside it.
        THE PARENT'S THREE CAUSES ARE THE THREE THINGS THAT CHANGE AROUND THE
        LAYER AND NOT THE ONE THAT CHANGES INSIDE IT, and that is the sentence
        the whole item turns on. Ask where each of the three comes from: a TOKEN
        change is the host handing the layer different values, a LAYOUT change is
        the window handing it a different amount of room, a STATE change is the
        data handing it a different condition. All three are INPUTS. The change a
        visual regression suite is actually installed to catch is the stylesheet
        edit somebody made to the layer itself — a border that became a shadow,
        an element that stopped reading `--wk-color-muted` and started reading
        `--wk-color-ink` — and it is in none of them. A vocabulary of only the
        parent's three files every such edit under one of them, and the report
        then says "token change" for a change no token made, which is worse than
        silence because it sends the reader to the theme file. So there are FOUR
        causes, `CAUSES_THE_PARENT_NAMES` keeps the addition visible, and
        `declaration` is the fourth.
        A SCREENSHOT IS A BASELINE WITH NO VOCABULARY. A pixel comparison answers
        "1,240 pixels differ", which names none of the parent's causes and
        therefore cannot tell an intended change from an accident; it is a
        function of the machine that recorded it; and it is BLIND to a third of
        what was asked for, because `aria-busy` arriving on a table is a state
        change with no pixels whatever. So the PIN is a structured signature with
        one channel per cause and the raster is measured INSIDE a run, where the
        machine is constant. Measured: of four deliberate changes the recording
        saw all four, ONE of them leaving no pixels at all, and none visible only
        as pixels.
        WHAT THE RECORDING CANNOT SEE IS MEASURED, NOT ASSERTED — the S7.21.f
        lesson applied to an instrument rather than to a standard. A catalogue of
        real changes, one per cause plus a null change, is applied to a live
        document and each is recorded against BOTH instruments, so a blind spot
        is a number in the report rather than a silence. It also calibrates the
        one tolerance in the design from both sides at once: the geometry is
        quantised into units of the cell's own inline size (a pinned pixel is a
        fact about the recorder's font build), and the quantum is proved fine
        enough by a layout mutation that must move it and coarse enough by a
        two-load comparison that must not.
        ACCIDENTAL IS THE HARD WORD, AND AN APPROVAL IS WHERE EVERY VISUAL SUITE
        DIES — never on the first red run, but on the wholesale re-record three
        weeks later where forty images are accepted because somebody made one of
        them. So a difference is permitted only by a declared intent whose CAUSE
        and SCOPE must both match, which is what stops a token approval from
        laundering a layout regression sitting beside it (driven as a control:
        two differences, one intent, exactly one still accidental and it is the
        geometry one), and which must name the PIN REVISION it was written
        against — S7.12's golden rule applied to the approval, so an approval
        cannot outlive the recording it was written for.
        AND COVERAGE CAN SHRINK TO ZERO WITHOUT A SINGLE FAILURE: delete the
        stories, delete the baselines, and every visual suite ever written
        reports success. A pinned cell that no longer renders is therefore a
        FAULT, and a primitive in no intended composition is named — checked
        against S7.21.b's PARSED INDEX rather than against the composition list,
        because a list of subjects somebody remembered makes the coverage claim
        circular. All 19 reached by 4 compositions, none a subset of another.
        THE BROWSER FOUND THREE DEFECTS, TWO OF THEM IN THIS ITEM'S OWN
        INSTRUMENT, AND THE THIRD WAS THE INSTRUMENT MEASURING ITSELF:
        (1) The reference channel first asked "which role has this value", and a
        value-to-role lookup OVER-ATTRIBUTES: in the neutral theme
        `--wk-color-ink` is `CanvasText`, the same black an element that sets no
        colour at all inherits from the user agent, so every such element was
        recorded as reading the ink role — and retuning ink then presented as a
        DECLARATION change at a dozen places. The catalogue's retheme caught it on
        the first run. Provenance is now MEASURED: every role is substituted with
        a value unique to it, all at once, and what the engine computes is a
        fingerprint of the DECLARATION with the host's palette factored out by
        construction. Inheritance is handled exactly, composed values come back as
        compositions of sentinels, one style recalculation for the whole cell.
        (2) A PROPERTY WITH A TRANSITION DOES NOT JUMP TO ITS NEW VALUE — it
        animates, so `getComputedStyle` hands back the OLD one for as long as the
        transition runs. Read straight after the substitution, `Button`'s
        background and `ToggleButton`'s border came back as the host's real accent
        and rule colours in BOTH themes, which is exactly the theme-dependence the
        channel exists to remove, and each cell's answer looked perfectly
        plausible on its own — only a control comparing two palettes could see it.
        Worse, the motion roles are substituted too, so the sentinel durations
        made the wrong answer last a second. `transition-property: none` stops the
        animating without touching `transition-duration`, which this recording
        reads: turning the durations off would have silently emptied one of the
        properties in the channel.
        (3) A HARNESS THAT MEASURES ITS OWN SIDE EFFECTS. Three loads of the modal
        composition compared in isolation were pixel-identical and the same three
        inside the tool were not — because signing a cell puts the roles back
        afterwards, and putting them back starts a transition on every property
        that declares one, so the screenshot caught a button mid-fade at whatever
        moment the call returned. The fix is ordering, and it is the rule: the
        raster is of the document as RENDERED, never of the document as PROBED.
        Also corrected during the build: the report first rebuilt its repeat pairs
        as the same signature twice, which satisfies the stability rule with data
        incapable of failing it — the empty-argument-answering-no-problems shape
        arriving through an ARTIFACT rather than through a function, eighth
        sighting. The measurement now records the second signature exactly when it
        disagreed.
        DIST-PATCH VERIFICATION four ways: mapping the `reference` channel onto
        the `token` cause failed the catalogue (a declaration change filed under a
        cause an approval would authorise), dropping the CAUSE half of
        `intentExplains` failed the laundering control, widening `scopeCovers` to
        always true failed the unused-approval control, and narrowing
        `PINNED_APPLICANTS` to drop the viewport contexts failed both the derived
        context list and `pinned-cell-no-longer-rendered` over twelve real cells.
        Restoring returned 37/37, and a second full run reproduced the recording
        byte for byte. -->
  - [x] S7.21.h Prevent stories/fixtures/test providers from production exports
        and bundles. Evidence: bundle scan.
        <!-- `ship-boundary.ts` (72 tests, 25 fault kinds) +
        `apps/oshun/web/tools/ship-boundary-scan.mjs` (24 controls, real esbuild
        at production settings) + `generate-workbench-ship-boundary.mts` (the
        report, with `--check` in `verify:inventory:v1-workbenches`) + a
        `no-restricted-imports` ban in the app's eslint config, over a committed
        measurement in `evidence/v1-workbenches/workbench-ship-boundary.json`.
        A PRODUCTION BUNDLER IS AN OPTIMISER, AND AN OPTIMISER ERASES THE
        EVIDENCE WHILE LEAVING THE LIABILITY. The parent names a BUNDLE SCAN,
        which read plainly is a search of the emitted output, and that is the one
        instrument incapable of settling it. Four leaks were planted into this
        layer's own barrel, one per class, and each was built by a real esbuild
        at production settings. Against a 1,151-byte reference: a re-exported
        story emitted 1,151 bytes with its title string nowhere in the output, a
        fixture emitted 1,151, and a test provider reaching a development-only
        package emitted 1,654 with its own code shaken away while the bundler
        still resolved fifteen files out of that package. Only the harness
        survived, at 17,358, and it survived because it MOUNTS A ROOT AT MODULE
        SCOPE and a side effect is the one thing tree shaking may not remove. Of
        four real leaks the served bytes showed ONE; the import was still in the
        source in all four cases. So the load-bearing channel is REACHABILITY,
        taken from the bundler's own metafile before any optimisation, and the
        emitted bytes are corroboration rather than the finding. Measured over
        the finished instrument: reachability 4/4, emitted bytes 1/4, resolved
        dependencies 1/4.
        AND THE BYTE ANSWER IS A PROPERTY OF SOMEBODY ELSE'S CONFIGURATION,
        WHICH IS DRIVEN TWICE RATHER THAN ARGUED. The same planted story emitted
        1,841 bytes with tree shaking and 11,442 without, over an identical
        source tree. The same four leaks were seen by the bytes 1/4 through a
        consumer that imports one export and 4/4 through one that re-exports the
        barrel — so the scan blinds itself exactly as the consumer becomes more
        selective, which is the direction real pages go, and neither knob is in
        this layer.
        THE PARENT NAMES THREE INERT THINGS AND THE FOURTH ONE RUNS. A story is
        a catalogue, a fixture is data and a test provider is an implementation
        somebody asked for; all three sit there until something renders them. The
        harness that mounts the layer into a document is in none of the parent's
        words, and it is the only one of the four that executes on import, the
        only one an optimiser cannot remove and by fifteen times the largest.
        `TEST_ONLY_CLASSES` has four members, `CLASSES_THE_PARENT_NAMES` keeps
        the addition visible, and `harness` is the fourth. A CLASS IS ALSO NOT A
        CONSEQUENCE: what a file is belongs to the test apparatus and what it
        costs in production is derived, one file carries several at once, and
        `fabricated-content` is in that list because a fixture a production
        module reads is not weight in a bundle — it is a surface showing data
        nobody has.
        ONE OF THE FOUR CANNOT BE CONFIRMED FROM ITS CONTENTS AND SAYING SO IS
        THE POINT. A harness works at import, a story carries a story-format
        default export and a test provider imports something the install does not
        have: each announces itself in its own syntax and a declaration that
        disagrees is a finding. A fixture is a table of ids indistinguishable
        from a table of ids somebody ships, so any rule invented to tell them
        apart gets tuned until it agrees. `CLASS_WITH_NO_SYNTACTIC_EVIDENCE`
        records that the residual class's only evidence is where somebody put it,
        which means a fixture leaving the fixtures directory stops being
        classifiable and the reachability rule is all that is left.
        THE ENTRY SET IS THE ARGUMENT. This layer has no route consumer at all,
        so an audit rooted at the product's pages would traverse everything,
        never arrive, and report zero leaks in the layer it was written for —
        the empty-argument-answering-no-problems shape, NINTH sighting, arriving
        through the ROOT SET rather than through a function argument or an
        artifact. `no-production-entry-point` is a fault and the headline says
        out loud that no application file imports the layer yet.
        THE SCAN FOUND A REAL DEFECT ON ITS FIRST RUN THAT HAD NOTHING TO DO
        WITH STORIES: `@oshun/workbench-kit` is imported by fifty shipped files
        in `apps/oshun/web/src` and appears in neither `dependencies` nor
        `devDependencies`. It builds because Next resolves it through
        `tsconfig.base.json`; any resolution that goes through the manifest does
        not have it. Fixed by declaring it. It also produced
        `shipped-file-no-entry-reaches` for fifteen components, because the
        barrel named five of twenty — a gate whose coverage is a quarter of the
        directory reports a clean layer either way — so the barrel now names
        every shipped component.
        THREE DEFECTS IN THIS ITEM'S OWN INSTRUMENT, EACH FOUND BY RUNNING IT:
        (1) the module-scope-effect walk exempted its own root, so a top-level
        `const readQueue = () => { window.localStorage... }` read as work
        performed at import and convicted two shipped components — a function
        initializer performs no work at import whatever is inside it. (2) It
        counted every occurrence of the text `document`, including parameter
        names and property accesses, which is ordinary vocabulary in an editor
        component. (3) The token corpus for the byte channel was assembled from
        parsed string LITERALS while the search over a minified output can only
        be a SUBSTRING test — asymmetric, and not theoretically: `Hierarchy`
        survived as a supposed discriminator for a story and then matched
        `HierarchyTree` in the bytes. Both sides are now `includes` over raw
        text, and the corpus is derived from the bundler's own shipped inputs
        rather than from a remembered list of directories, which had left
        `verified`, `completed`, `sequence` and `onSelect` looking distinctive.
        Excluding tokens that appear in the CLEAN BUNDLE instead would be
        circular: a token is in that bundle either because something that ships
        says it or because the file leaked, and the second case is the finding.
        THE BAN IS SCOPED, NOT THE IGNORE LIST. The lint rule fired on
        twenty-three harnesses doing the correct thing until `__fixtures__` was
        exempted from THAT RULE rather than added to the config's global ignore
        list, because these files mount real components into real documents and
        adding a directory there switches off forty rules instead of one.
        DIST-PATCH VERIFICATION three ways: silencing the module-scope arm of
        `evidencedDisposition` failed four controls, pointing `REQUIRED_CHANNEL`
        at the emitted bytes failed five, and dropping `harness` from
        `TEST_ONLY_CLASSES` failed two. Restoring returned 24/24 each time. -->
- [ ] S7.22 Enforce no unnecessary card nesting, no app heroes, no decorative
      gradient chrome, and no competing accent colors. Evidence: design
      conformance review.
  - [x] S7.22.a Inventory every container/card/panel and require that cards
        represent a selectable, draggable, comparable, previewable, or otherwise
        meaningful object interaction. Evidence: card-use review.
        <!-- `libs/oshun/workbench-kit/src/card-use.ts` (67 tests, 18 fault
        kinds) + `apps/oshun/web/tools/card-use-browser.mjs` (77 controls, 21
        fixture hosts, real Chromium). Evidence:
        `evidence/v1-workbenches/workbench-card-use.json` — 998 containers, 22
        cards, every card names an interaction and every one of them was DRIVEN,
        every component the directory ships reached.
        S7.1.g's RULE WAS CORRECT AND HAD NEVER BEEN SHOWN THE SET IT GOVERNS.
        `workbench-shell-browser.mjs` collects a container only where the element
        is a BUTTON that draws a border and reads the interaction off an
        attribute the component author wrote, so both halves of the argument were
        declarations and the decorative wrapper the rule exists to catch — a div
        with a border, a radius and sixteen pixels of padding — was never in the
        list. Empty-argument shape, tenth sighting, arriving through the
        INVENTORY. Measured: of the sixteen `data-card-interaction` attributes in
        the layer, NOT ONE was on an element this review derives as a card, and
        twelve of them said `movable`, a word nobody had defined — the shipped
        rule is `interaction !== 'none'`, which every undefined word satisfies.
        So the inventory is DERIVED FROM WHAT THE ENGINE PAINTED and the
        declaration is the second opinion. RUNNING IT CORRECTED THE MODEL FIVE
        TIMES, each correction a class of finding against surfaces doing the
        right thing, and the first run filed four hundred of them. (1) A LINE IS
        NOT A BOX HOWEVER MUCH SPACE IS AROUND IT: reading inner space as
        box-hood called forty list items with a four-pixel left rule cards, and a
        list with rules between its items is precisely what S7.22.b recommends
        flattening a card mosaic INTO, so the rule asked people to flatten what
        they had already flattened. Three strokes close a shape. (2) A GRID IS
        ONE ENCLOSURE DRAWN N BY M TIMES, NOT N BY M CONTAINERS — the line
        between two cells belongs to neither, and 578 table cells came back as
        decorative cards that ought to be draggable. (3) A LANDMARK CANNOT BE A
        COLLECTION MEMBER however many identically-drawn siblings it has: a
        header and a footer sharing a border style with a third box repeats
        perfectly well and is not three of anything. (4) FOR A CONTROL THE REPEAT
        IS NOT ENOUGH — a row of filter buttons repeats, is separately named and
        encloses its content exactly as a list of objects does, and only an
        announcement that it stands for an object tells them apart. (5) AN INERT
        BOX OF ONE TEXT RUN IS A MARK: eighteen integrity chips carrying a real
        non-colour signal in their border width and style read as eighteen
        decorative cards, and a box of nothing but controls is a control group,
        not a card. THE MARKUP'S OWN ANNOUNCEMENT COUNTS AND ITS ABSENCE IS A
        FAULT: `aria-selected` says a thing can be chosen whether or not it is
        chosen now, and S7.1.g reached for a private attribute because inferring
        from `aria-current` read a STATE as a capability — but it is the
        attribute's PRESENCE that is the capability and its VALUE that is the
        state. Three of the seven words have a standard announcement and four
        have none, and saying which is which is the point. The shell fixture then
        failed its own new rule: it wrote `aria-current={selected ? true :
        undefined}`, and React's `undefined` idiom is exactly what removes the
        presence. THE VOCABULARY REFUSES FOUR WORDS BY NAME with a reason each —
        `actionable` is a border drawn around a button, `hoverable` is no
        interaction for a keyboard or a finger, `clickable` names the input
        device rather than what happens to the object, `readable` justifies every
        border anybody draws — and ADDS THREE the parent does not name
        (`navigable`, `expandable`, `dismissable`: the ones that do not leave the
        object where it was, and the most ordinary real card there is opens the
        thing it stands for). A CLICK IS NOT A DRAG: driving every word with
        `click()` reported twelve shipped stage-board cards as inert, `<details>`
        discloses through its `open` property and never writes `aria-expanded`,
        and a card's identity has to survive the move it is asked to perform —
        the board prints its column's label inside the card, so identifying it by
        its text made a successful move look like no move at all. EVERY OTHER
        GROUP IS TRIED, NOT THE FIRST: `draggable` claims the card can be moved,
        not that it can be moved anywhere, and where it may go is the graph's
        business. REMEDIATION: `movable` -> `draggable` (12); the job console's
        `<tr>` dropped its attribute, because a table row is drawn with a rule and
        `aria-selected` already announces it; and 31 boxes across seven
        components — audit entries, drift rows, notice rows, gate rows, decision
        sections, the bulk panel's seven stacked tiles, a refusal paragraph —
        became lists and sections, each keeping the non-colour signal on the
        channel its own browser proof measures. All nine affected browser tools
        and 256 jsdom tests re-run green. A PRE-EXISTING RED GATE FOUND AND
        FIXED: `workbench-shell-browser.mjs` had a hand-written esbuild alias
        whose bare entry SHADOWED every subpath, so it had crashed on startup
        since `browserHost.tsx` began importing `@oshun/workbench-kit/identity`.
        DIST-PATCH VERIFICATION four ways. Raising `MIN_CLOSED_EDGES` failed 17
        controls, silencing the descendant-seat arm failed 1, dropping the mark
        branch failed the layer verdict — and EMPTYING `REFUSED_CARD_INTERACTIONS`
        CHANGED NOTHING, because the four refusal controls are generated by
        iterating that very list: no words, no controls, no failures, a green run
        over a rule that had been deleted. The empty-argument shape inside the
        harness itself, eleventh sighting. A control loop fed by the thing it
        verifies now states how many controls it expects to produce, and the same
        patch then failed. Restoring returned 77/77. -->
        
  - [x] S7.22.b Flatten nested cards/panels into workspace regions, sections,
        columns, dividers, lists, or semantic groups where borders/elevation add
        no meaning. Evidence: before/after composition review.
        <!-- `libs/oshun/workbench-kit/src/composition.ts` (102 tests, 20 fault
        kinds) + `apps/oshun/web/tools/composition-browser.mjs` (60 controls, 21
        fixture hosts, real Chromium). Evidence:
        `evidence/v1-workbenches/workbench-composition.json` — 998 containers,
        520 nested boundaries, every one of them warranted, no box drawing a
        channel it cannot justify, deepest run of parallel edges 3 of 3.
        A NESTED BOUNDARY IS NEVER JUDGED ON ITS OWN, which is what makes this a
        different question from S7.22.a rather than the same one again: a border
        that would be the whole point of a box standing on the page says nothing
        at all when it is drawn four pixels inside another border that has
        already gathered the same content. So this module composes card-use.ts
        outright — `derivedContainerRole` says what a box is, `drawsAsABox` says
        whether a stroke closed a shape — because two measurements of what a
        container is would drift within a release and the second one would be the
        one nobody notices is wrong. THE CHANNEL MAKES THE CLAIM, NOT THE BOX: a
        border says an edge is here, an elevation says this is above the surface,
        a fill says this is a different surface, an inset rule says a line marks a
        state, so a box drawing two of them can be right about one and the remedy
        for a warranted border under an unwarranted fill is to drop the fill
        rather than the box. A WARRANT IS MEASURED AND SEVEN OF THE TEN ARE THE
        ROLE — and THREE ROLES BRING NO WARRANT AT ALL. A section, a panel and a
        well are exactly what the parent's word "container" hides, and they are
        this item's whole subject, which the table says rather than a predicate.
        "IT SEPARATES IT FROM ITS SIBLINGS" IS DELIBERATELY NOT A WARRANT, because
        it is the argument every card mosaic makes for itself and it is true: a
        rule separates them too, with one stroke instead of four, which is
        precisely why dividers and lists are on the parent's list of
        destinations. Separation is not a warrant AND it is something the boundary
        carries, and that pair is the item — a destination that cannot separate
        may not be prescribed for a box that has a neighbour, or the cheapest
        answer is always to run every stack of panels together into one column of
        text. THE DESTINATION IS CONSTRAINED BY THE ARRANGEMENT, WHICH IS
        GEOMETRY: a horizontal rule between two boxes side by side separates
        nothing and a wrapped set has no line through it at all, so the sibling
        rectangles decide which of the parent's six replacements are available,
        and `unwrap` is the seventh the parent does not name. A DESTINATION IS
        LEGAL ONLY IF IT CAN CARRY WHAT THE BOUNDARY WAS CARRYING — the rule
        S7.22.a's own remediation was already obeying by hand — so a divider
        carries a state on its own stroke and no name, a list carries membership,
        a group carries a name and separates nothing, and removing a border that
        was the only thing announcing a group is a deletion with a tidier diff.
        THE BEFORE/AFTER IS DRIVEN, NOT RECORDED: a pair of snapshots from two
        commits is a golden that pins nothing once somebody re-records it, so
        every prescription is PERFORMED in the live document and the built kit
        compares what the two documents state in order, the named groups in the
        accessibility tree, the reading order and the state signals — additions
        allowed anywhere, nothing dropped, shared statements never shuffled.
        RUNNING IT CORRECTED THE MODEL SIX TIMES and the first run filed sixty-
        nine findings against surfaces doing the right thing. (1) A ROLE THAT
        EXPLAINS THE BOX EXPLAINS EVERY CHANNEL IT DRAWS: the warrants were
        written per channel, so a table cell's border was justified by its
        cell-hood and its wash was not, and a hundred and forty-four filled cells
        came back as findings. (2) The colour-only rule fired on forty-seven
        buttons whose border is drawn because they are the thing you press; it is
        now asked only of a channel the colour is the ONLY candidate justification
        for. (3) A LIST IS A RUN, and membership is a claim about one: a gate
        row's result and remediation blocks are drawn alike and separated by the
        row's own field table, so gathering them into one list moved the fields
        out from between them — the before/after reported the reordering and the
        prescription was wrong, which is the transition doing the job it exists
        for. (4) A BOX WITH NOTHING IN IT IS NOT A CONTAINER, IT IS A DRAWN
        INDICATION: the timeline's range band is a translucent wash with no
        children and no text whose whole job is to say WHERE the range is, and
        there is nothing inside it for a boundary to be about. (5) The channel
        rules were escaping the item's scope onto boxes standing on the page,
        whose channels are S7.22.a's question. (6) A probe holder that is itself a
        box contains every probe's text, so selecting probes by text picked the
        HOLDER and eight controls ran against a box none of their rules were
        about. REMEDIATION: the gate panel's result, remediation and waiver blocks
        became sections — a rule above the block plus a heading that names it —
        and the waiver kept its own signal on a NON-COLOUR channel (a double rule
        rather than an amber wash), because colour alone is a state nobody reading
        in one colour can see. The gate panel's 61 browser controls, card-use's
        77 and its 28 jsdom tests all re-run green. NO RULE GOES UNEXERCISED: the
        layer is now clean, so `compositionFaults` and `flatteningFaults` over the
        real observations both answer an argument nothing in them can fail — every
        one of the twenty faults therefore has to have been produced by a control
        in the same run, over a document the engine painted, and the four ways a
        flattening actually goes wrong (removing the box with its contents,
        gathering non-adjacent boxes, deepening the composition, doing nothing at
        all) are driven rather than described. DIST-PATCH VERIFICATION three ways:
        dropping the divider's warrant failed 5 controls, lowering the parallel-
        edge ceiling to 2 failed 2, and silencing the named-group arm failed 2 —
        including the coverage check, which is what catches a rule that has been
        deleted rather than merely never reached. Restoring returned 60/60. -->
  - [x] S7.22.c Remove marketing hero, campaign headline, stat strip, logo
        cloud, and aspirational banner patterns from operator/authoring routes.
        Evidence: route screenshot audit.
        <!-- `libs/oshun/workbench-kit/src/first-screen.ts` (62 tests, 13 fault
        kinds) + `apps/oshun/web/tools/first-screen-browser.mjs` (44 controls,
        real Chromium, real PNGs). Evidence:
        `evidence/v1-workbenches/workbench-first-screen.json` and twenty captures
        under `evidence/v1-workbenches/first-screen/` — 20 surfaces (5 routes x 2
        archetypes x 2 viewports), every audience derived, none of the five
        patterns found, worst first screen 1% empty of a 17% budget, every
        surface captured.
        A HERO IS AN ALLOCATION, NOT A STYLE. Two blocks can be pixel-identical —
        same height, same type scale, same full-bleed width — and only one of
        them is a hero, because the difference is what the block OFFERS the
        person reading it: a release console's masthead carrying the release
        name, its gate verdict and a publish button occupies exactly the space a
        marketing hero occupies and is the most useful thing on the page. So
        nothing here measures a gradient or a font size on its own; every block
        on the first screen is asked what a reader can DO with it, and the five
        named patterns are five shapes of the answer "nothing".
        THE AUDIENCE IS THE ARGUMENT AND GETTING IT WRONG FAILS BOTH WAYS: the
        rule is addressed to operator and authoring routes, so auditing a landing
        page as an operator surface files every finding against a page doing its
        job, and classifying an operator surface as public lands the exemption on
        exactly the page the rule exists for. It is derived from S1.8's
        descriptor, which already knew and had never been asked — `scope`,
        `roles` and `deepLink.shareSafety`, the last of which has said public /
        tenant-only / never-share since S1.8 and is almost literally the question
        — and a route none of the three settles is reported NOT DERIVABLE rather
        than assumed. THE FIRST RUN FOUND EXACTLY THAT: four of the layer's five
        routes declared `platform` scope and no deep link at all, so sixteen of
        the twenty surfaces could not be placed. That is inaccurate as well as
        unauditable — a brief is a workspace object and its link is not for a
        stranger — and correcting the descriptors made S1.8's own parser refuse
        a tenant-only link with no expiry, which is a URL that keeps working
        after the person it was sent to has left.
        PRIME SPACE IS VERTICAL SPACE ABOVE THE FOLD, NOT THE TOP OF THE
        DOCUMENT, so the unit is the INTERSECTION of a block with the first
        viewport in pixels, and the audit runs at 1440x900 and at 390x844 because
        everything a hero displaces at handset width goes further down rather
        than to one side. The blocks are FOUND rather than named: descend through
        wrappers that have one child covering their own area, stop at the first
        level that partitions into more than one part, and KEEP DESCENDING WHILE
        A PART STILL HOLDS THE WORK to a bound of three. One level was not enough
        and the first run showed why — the shell's outer band held the work, its
        region grid held the work, and the audit stopped with a single block
        covering the entire screen and offering `the-work`, which is exactly
        where a hero inside a workspace would have hidden.
        AN OFFER IS ONE OF A CLOSED LIST AND `what-you-may-not-do` IS ON IT
        DELIBERATELY: a refusal at the top of a surface is the most useful thing
        prime space can carry when it is true, and a rule that scored it as
        decoration would ask every surface to bury the one thing its reader needs
        first. A HEADING IS NOT A HERO — an operator surface names itself and
        that is orientation — so a campaign headline is measured as a heading
        ABOVE the surface's own name or drawn LARGER than it. THE FIRST RUN
        REPORTED A SHIPPED SHELL AS CARRYING ONE, because the surface named
        itself with an `aria-label` and the rule compared a 13px section title
        against a name that has no size, no position and nothing a sighted reader
        can see at all. Both arms now require the name to be a HEADING, and a
        working surface that names itself only in the accessibility tree is a
        finding of its own. AND A NUMBER YOU CANNOT ACT ON IS A STAT STRIP: not a
        number, since a job console's counts are the work when pressing one
        filters the list, and every clause of "three or more identically drawn
        tiles, each a numeral and a caption, none focusable, selectable or
        linking anywhere" is load-bearing.
        THE SCREENSHOT IS THE ARTIFACT AND THE MEASUREMENT IS THE EVIDENCE. A
        picture is the one form of evidence nothing can re-check — it cannot
        fail, it cannot be compared, and a year later nobody can tell what
        viewport it was taken at — so a capture and a measurement are taken from
        the same page at the same viewport and a pair whose two halves disagree
        about that viewport is refused rather than filed.
        RANGE, MEASURED RATHER THAN ASSERTED: the product ships 720 page files
        under `apps/oshun/web/src/app` and not one resolves to an S1.8 route
        descriptor, so this audit can derive an audience for none of them and
        does not claim to have reviewed them. The count is in the evidence. The
        model and the tool generalise to those routes the moment they carry a
        descriptor; what is missing is the argument, not the rule. A production
        `next build` of the app was run to establish that this is a descriptor
        gap rather than a rendering one — it succeeds, with a 10GB heap.
        NO RULE GOES UNEXERCISED: the layer is clean, so `firstScreenFaults` over
        the twenty real surfaces answers a question nothing in them can fail, and
        every one of the thirteen faults has to have been produced by a control
        in the same run over a document the engine painted. Harness trap: a probe
        planted inside a holder div is measured at the holder's full width, so a
        60vw block planted to drive the rule that catches what the five patterns
        MISS came back as a marketing hero — the harness must not be the thing
        under test (S7.2). DIST-PATCH VERIFICATION three ways: tightening the
        empty-space budget past what the layer draws failed 1 control, narrowing
        `AT_WORK_SCOPES` to drop `workspace` failed 2, and silencing
        `isStatStrip` failed 2 including the coverage check. Restoring returned
        44/44. -->
  - [x] S7.22.d Remove decorative gradients, glows, glass effects, ornamental
        icons, thick repeated borders, and shadows that do not convey state/
        hierarchy. Evidence: chrome audit.
        <!-- `libs/oshun/workbench-kit/src/chrome.ts` (40 tests, 8 fault kinds) +
        `apps/oshun/web/tools/chrome-browser.mjs` (35 controls, 21 fixture hosts,
        real Chromium). Evidence:
        `evidence/v1-workbenches/workbench-chrome.json` — 54 effects, every one
        of them conveying something, 50 by state and 19 by hierarchy, 3 of the 6
        effect kinds present in the layer at all.
        THE QUALIFIER IS THE RULE. The item lists six CSS effects and then
        qualifies the whole list with five words — "that do not convey
        state/hierarchy" — and read without them it is a ban on six properties: a
        layer obeying that would delete the inset rule marking a blocked gate and
        the elevation lifting a dialog off the page. Read with them, the
        qualifier names TWO justifications and they are TWO COMPARISONS AGAINST
        TWO DIFFERENT SETS. An effect conveys a STATE when it distinguishes this
        thing from the things BESIDE it; it conveys HIERARCHY when it
        distinguishes this thing from the thing AROUND it. Sideways and outwards.
        An effect the same as its neighbours' and the same as its container's
        orders nothing and marks nothing — it is a texture, and that is the
        finding.
        THE SIBLINGS ARE THE COMPARISON SET FOR A STATE, NOT THE CLASS LIST.
        S7.22.b keyed the instances of a part on the class list, which is right
        for asking whether a component part varies and wrong here: a modifier
        class makes `row unread` and `row read` two parts and each of them is
        perfectly constant, while the reader is comparing a row to the row under
        it.
        AN ELEVATION WITH NO OFFSET IS A GLOW, and the difference is where the
        light is. A shadow offset downward claims a surface above another under
        light from above, which a reader can check against everything else on the
        screen; a zero-offset shadow with a wide blur claims a thing lit from
        nowhere. And a zero-offset shadow with almost NO blur is a focus RING,
        which is the most important mark on a keyboard surface — so the blur is
        the discriminator rather than the offset alone, and both ends of that
        threshold are defended.
        GLASS IS A BACKDROP FILTER AND NOTHING ELSE: a translucent background is
        a colour with an alpha, this layer has several that are ordinary control
        fills, and treating alpha as glass would file findings against every
        `rgba()` in it. AN ICON THAT NAMES ITSELF IS NOT ORNAMENTAL, and state
        and hierarchy are deliberately NOT on its warrant list — a mark does not
        become meaningful by differing from the mark beside it, so it either says
        what it means, sits inside a control whose target it is part of, or draws
        a value. ONE THICK BORDER IS EMPHASIS AND THREE IDENTICAL ONES ARE A
        TEXTURE: the item says thick REPEATED borders and the repetition is doing
        the work, since a four-pixel leading rule on the one blocked gate in a
        list is the most useful mark on the surface.
        THE ONE REAL FINDING WAS A DEFECT IN THE MEASUREMENT, AND IT IS S7.22.a's
        SOLE-ELEMENT-CHILD FINDING IN ANOTHER GUISE. The run reported the
        navigation's current-item marker — a three-pixel leading rule whose whole
        purpose is to mark one entry among several — as a border conveying
        nothing, because the marker is drawn on the anchor and the anchor is the
        only element inside its list item, so it had no sibling at all. The
        reader is comparing list ITEMS. The comparison set now walks up through
        sole-child steps first and reads the effect off anywhere inside each
        sibling, and the marker earns itself by state as it always did.
        AN EFFECT WITH NEITHER A SIBLING NOR A CONTAINER IS UNTAKEN, NOT FAILED:
        both comparisons come back false over an empty argument, and that reads
        exactly like an effect that conveyed nothing.
        NO RULE GOES UNEXERCISED, and here it matters more than anywhere: the
        layer paints NO gradient, NO glass and NO glow at all, so three of the six
        rules answer an empty argument over the real surfaces and come back clean
        — indistinguishable from three rules that have been deleted. Each of the
        six is therefore planted into a live document TWICE, once in the shape the
        rule exists to catch and once in the shape that earns its place, and every
        fault has to have been produced by a control in the same run.
        DIST-PATCH VERIFICATION three ways: lowering `THICK_BORDER_PX` to 1 made
        every hairline a thick border and failed the layer verdict, silencing the
        sideways comparison failed it again over the 50 effects it justifies, and
        raising `GLOW_MIN_BLUR_PX` past every real shadow failed 3 controls
        including both coverage checks. Restoring returned 35/35. -->
  - [x] S7.22.e Enforce one primary accent and separate accessible semantic
        statuses with noncolor cues. Evidence: token/contrast audit.
        <!-- `libs/oshun/workbench-kit/src/signals.ts` (82 tests, 11 fault
        kinds) + `apps/oshun/web/tools/signals-browser.mjs` (33 controls, 21
        hosts, real Chromium). Evidence:
        `evidence/v1-workbenches/workbench-signals.json` — ONE accent hue
        family, 190 of 190 paints governed by a role, 10 statuses across 2
        vocabularies with NONE carried by colour alone, and 154 of 154 measured
        pairs clearing WCAG 2.1 AA.
        AN ACCENT IS A HUE FAMILY, NOT A HEX. A surface painting one blue at
        three lightnesses has ONE accent and a rule counting distinct values
        reports three; a surface painting a blue primary button beside a teal go
        link has two and that same rule reports both cases identically. The unit
        is therefore the hue family in OKLCh, which was fitted to perceptual data
        so one angular tolerance means the same thing everywhere on the wheel —
        HSL's would not, since it puts pure blue and pure yellow at the same
        lightness.
        HUE CANNOT SEPARATE A COLOUR FROM A GREY AND ONLY CHROMA CAN, which the
        measurement found rather than an argument: every neutral in this layer is
        a COOL grey sitting within a couple of degrees of the accent's own hue, so
        grouping by hue alone reports every divider on every surface as a second
        accent painted in the accent's colour. `CHROMATIC_CHROMA_FLOOR` is
        therefore the whole of the measurement, placed at a twenty-percent tint of
        a saturated accent over white — the strongest wash that still reads as a
        tinted surface rather than as the colour itself.
        ONE ACCENT MEASURED IS NOT ONE ACCENT ENFORCED, AND THE ITEM'S VERB IS
        ENFORCE. The first run counted one hue family and found the count was a
        COINCIDENCE: ADR-S4's token contract resolved in ONE of twenty-one
        documents and governed 17 of 184 paints, because the only element in the
        product carrying `data-workbench-theme` was a fixture inside the primitive
        catalogue. The shell — the thing every workbench surface renders inside —
        never turned the contract on, so "one accent" held because sixteen authors
        had reached for the same blue that week. `WorkbenchShell` now carries the
        attribute and publishes the nine roles from the `--wb-*` values it already
        declared (not one painted colour moves), the fixture hosts mount through
        `mountThemed` so the harness matches production, and forty-five literals
        across eight stylesheets resolve to roles: 21 of 21 documents, 190 of 190
        paints.
        SEPARATE IS ABOUT THE ACCENT AND NOT ABOUT THE OTHER STATUSES, AND THE
        ITEM'S TWO CLAUSES ARE WHAT SETTLE IT. The first run also convicted the
        gate panel for drawing three blocked gates in one red and separating them
        with three different words — which is correct, and the rule that convicts
        it demands a distinct hue per member of every vocabulary, which is the
        FIRST clause of this same item inverted: manufacturing a colour per status
        is precisely how a surface acquires the competing accents "one primary
        accent" exists to prevent. A shared colour is a channel DECLINING TO
        CARRY, so it is reported as a fact and the fault is reserved for the case
        where nothing separates two members at all.
        A NON-COLOUR CUE MUST DISTINGUISH RATHER THAN MERELY EXIST, so the
        comparison set for a cue is the other members of its own vocabulary —
        S7.22.d's sideways comparison moved off the document and onto the
        vocabulary, because the reader is not comparing this row to the row under
        it, they are comparing this status to the one it could be confused with.
        AND THERE ARE TWO READERS REACHED BY DIFFERENT CHANNELS. An `aria-label`
        is a complete answer for somebody using a screen reader and nothing at all
        for the reader 1.4.1 was written for, who can see the screen and cannot
        separate its hues; a dashed border is the exact mirror; and the glyph this
        layer's status primitive draws is `aria-hidden` ON PURPOSE, which makes it
        a sighted channel and not a screen-reader one. A single has-a-cue answer
        passes a surface unreadable to precisely the person the criterion protects.
        The first run's six colour-alone findings were all one missing channel:
        THE ACCESSIBILITY TREE CARRIES A CHANNEL OF ITS OWN, and `announced-role`
        is the counterpart of `border-style` — the inline message renders
        `role="alert"` for one tone and `role="status"` for the other, stating the
        distinction in the one place that reader is listening.
        THE PAIR'S KIND DECIDES THE CONTRAST FLOOR AND THE KIND IS MEASURED. WCAG
        2.1 has several numbers and which applies is a property of what the pair
        IS; the floors are read from `docs/releases/p2/accessibility-baseline.md`
        rather than chosen here, because §STD.1 has not yet recorded the 2.2
        target and 2.2 changed neither 1.4.3 nor 1.4.11. Measured on this layer's
        own pixels a disabled toolbar button sits at 3.06:1 — its criterion met
        and the wrong criterion failed from one pair of colours.
        THIRTY REAL CONTRAST DEFECTS, ALL FIXED. A control's boundary is what
        identifies it as a control, so 1.4.11 asks 3:1 of it against the
        surrounding fill, and the layer drew fourteen of them in the DIVIDER
        colour at 1.24:1 — a boundary a reader can find only because they already
        knew the control was there. `--wb-control-line` is that budget written
        down. The block editor's toolbar left its buttons entirely to the user
        agent, which supplied a `#d6d6d6` boundary at 1.45:1 and a `#b4b4b4`
        disabled label at 1.99:1, invisible to review because nothing in the file
        put them there. And three of the contract's own four state roles were CSS
        named colours that clear the 3:1 a badge is judged at and fail the 4.5:1
        the words on it need: SeaGreen 4.25, DarkGoldenrod 3.25, SteelBlue 4.11,
        each replaced by the same hue one step darker.
        FOUR DEFECTS IN THE MEASUREMENT ITSELF, each of which passed a plausible
        run first. (1) AN IDENTITY IS NOT A VOCABULARY AND EVERY PAINT IS A
        FUNCTION OF AN IDENTITY: the derivation reported the gate panel's rows as
        a status vocabulary whose members were the gate IDS, because a map from
        distinct keys is a function whatever it returns, so a status must
        PARTITION its group where an identity ENUMERATES it. (2) A GLYPH IS A
        MARK, NOT A CHARACTER IN A SENTENCE — scanning every non-letter in an
        element's text reported the gate rows as carrying an identical glyph,
        which was the middle dot in "BLOCK · fresh". (3) WHICH SIDE OF AN ELEMENT
        THE BACKGROUND IS ON DEPENDS ON WHAT IS PAINTED, and it was exactly
        backwards: text sits ON its own fill, a border sits at the EDGE, so a
        primary button's border was measured against the button's own accent fill
        and came back 1.00:1. (4) THE CANVAS IS A SYSTEM COLOUR, SO MEASURE IT —
        nothing declares a background on the root, `getComputedStyle` reports
        transparent, and both defaulting to white and refusing to answer are
        wrong; `color: Canvas` resolves what the agent is actually painting.
        THE HARNESS MUST NOT BE THE THING UNDER TEST, twice: the primitive
        catalogue plants a subtree with no contract above it in colours its own
        comment calls "nothing any palette would choose", and measured as part of
        the layer that probe is two competing accents and two unreadable pairs.
        DIST-PATCH VERIFICATION three ways: widening `ACCENT_HUE_TOLERANCE_DEG`
        to 180 so one family swallows every hue failed 4 controls, dropping
        `CHROMATIC_CHROMA_FLOOR` to 0.001 so every grey became a colour failed 4,
        and silencing `namesTheStatus` — the only channel reaching both readers —
        failed 5 including both coverage checks. Restoring returned 33/33. -->
        <!-- OPEN, and recorded rather than resolved: the layer's two ratified
        palettes disagree about `warning`. The shell's is 13.5° from its
        `failure` and the components' is 41°, so unifying on the shell's palette
        would DEGRADE the separation this item asks for. Both now resolve through
        the contract, so the choice is one host block rather than forty-five
        literals — but which values a host publishes is a product decision this
        audit deliberately does not make. -->

  - [x] S7.22.f Verify each route opens on the actual working surface with one
        dominant workspace, readable dense information, concise utility copy,
        and contextual inspector/status. Evidence: hierarchy review.
        <!-- `libs/oshun/workbench-kit/src/working-surface.ts` (74 tests, 26
        fault kinds) + `apps/oshun/web/tools/working-surface-browser.mjs` (60
        controls, 20 surfaces, real Chromium). Evidence:
        `evidence/v1-workbenches/workbench-working-surface.json` — 20 surfaces,
        every one measured, every route opening on its own working surface, 80
        regions ranked, narrowest workspace 57% of the 50% a majority requires.
        THE VEIN, AND IT WAS IN THE INSTRUMENT BEFORE IT WAS IN THE LAYER. The
        shell fixture read its route with `useState(currentRouteId)`, which takes
        an initial value and nothing else, so a host re-rendering it in place —
        which is what every browser tool here does, because remounting would
        throw away the selection and focus those tools measure — got the route it
        asked for once and the first route forever after. S7.22.c mounted five
        routes and was handed one, five times: 16 of the 20 captures under
        `evidence/v1-workbenches/first-screen/` were byte-identical duplicates of
        four images filed under twenty route names, and not one check in that run
        could have noticed, because nothing compared the surface against the
        route it was asked for. `surface-does-not-name-the-route-it-opened` is
        that missing join, asked before every other rule, since a run that
        measures the wrong page perfectly reports a clean layer. After the fix
        the captures are 18 distinct images of 20, and the two that still match
        are the detail route in both archetypes, which is what the archetype
        axis means here.
        A ROUTE WAS NOT A PROP EITHER, SO FOUR OF FIVE ROUTES SHOWED A FIFTH
        ROUTE'S OBJECTS. The fixture rendered one list everywhere and swapped its
        rows on the ARCHETYPE, so the claim this item makes could not be failed or
        passed. Each route now renders what it is named after and the archetype
        lands on the axis where it is genuinely a choice: an editor is read
        closely and a console is scanned, so S7.1.b's two archetypes take S7.5's
        two row heights.
        "OPENS ON" IS A CLAIM ABOUT A STATE NOBODY HAD PRODUCED. Every reading in
        this layer was taken after arriving, which is the same moment — but the
        only way to be wrong about `opens` is to measure after touching
        something, so a landing reading is taken with nothing touched and a
        second after exactly one interaction. The two together separate a surface
        that is empty from one that hides its work behind a choice, which a
        single reading cannot. Driving it also exposed a harness defect of the
        same family: re-rendering between surfaces kept the inspector's state, so
        the "arrival" reading of the second route showed the first route's object
        open in the panel. The page is loaded again for every surface (S7.2's
        same-document-navigation lesson, one surface on).
        DOMINANCE IS A MAJORITY AND A MAJORITY MAKES "ONE" A THEOREM. S7.1
        published `DOMINANT_WIDTH_SHARE` = 0.5 against "the arrangement's content
        width" — a denominator never defined, worth twenty points of verdict
        depending on whether the rails are inside it — and the argument it judges
        was DECLARED by its caller and had never been measured off a painted
        document. Over the sum of what the shell paints, a majority is held by at
        most one region, so the item's word "one" needs no second rule that could
        disagree with the first. The number is taken from S7.1 rather than
        repeated, so the two cannot drift.
        THE BROWSER FOUND THREE DEFECTS SOURCE-READING DID NOT. (1) THE INSPECTOR
        RESIZE CONTROL RESIZED NOTHING: `--wb-inspector-width` was declared on
        the inspector and the rule that reads it is `grid-template-columns` on
        its PARENT, and custom properties inherit downwards only, so the region
        announced `aria-valuenow` moving 320 to 720 while its box stayed at 320px
        for every keystroke and every drag. (2) Once it worked, the shell's own
        flat 720px maximum put it into an arrangement its own thesis forbids:
        1440 less a 56px rail, a 220px section rail and a 720px inspector leaves
        the workspace 444px, which is less than the inspector beside it.
        `inspectorCeilingPx` derives the ceiling the dominance claim implies from
        geometry the browser measures — 417px at the reference viewport — and the
        shell now clamps to it. (3) The fixture's heading used the shell's
        `.workspaceTitle`, which carries `flex: 1 1 auto` for a horizontal header;
        in a column-flex surface it grew along the column and measured 820x267, a
        heading taking a third of an editor.
        "READABLE DENSE" IS A TENSION AND NEITHER HALF IS A FONT SIZE. They pull
        on one knob in opposite directions, so a rule written against the knob is
        answered by the wrong half every time. Density is INFORMATION STATED per
        screen, its floor derived from two constants this layer already ships
        (S7.5's 44px comfortable row and S7.6's 220px column minimum give 9,680
        square pixels as the most generous room one fact needs; four times that
        is 25 facts per megapixel). Readability is ROBUSTNESS — the criteria a
        crammed layout is the thing most likely to break — plus the 75-character
        measure S7.21.d already published. They are not opposites on every axis:
        a denser arrangement puts text in narrower columns and IMPROVES the
        measure, which is why one number would have been wrong in both directions
        at once.
        S7.22.e's VEIN, ONE TOKEN OVER. ADR-S4 publishes `--wk-text-scale` so a
        reader who needs larger text can have it, and `primitives.module.css` was
        the only file in the layer that consulted it: doubling it moved nine
        declarations, left sixty-seven fixed, and 23 of 58 text runs on a shell
        surface did not move at all. Sixty-seven `font-size` declarations across
        seventeen stylesheets now resolve through the token, so at the default
        scale of 1 not one rendered pixel moves and the control finally reaches
        the reader who needs it.
        MOST TEXT ON A WORKING SURFACE IS NOT COPY, so a word count over a
        rendered document measures the tenant's titles and reports them as the
        product being verbose. Every run carries a PROVENANCE and a run
        attributable to neither side is REPORTED rather than counted — the audit
        found three of those on the first run and they were real omissions. The
        budget is then per ROLE, because one budget over every string convicts
        the refusal that has to explain itself and acquits the button labelled
        "Go"; and the ordering is proved rather than asserted, with one planted
        sentence driving both arms (unattributable, so the register rule stays
        silent; the same run attributed to the product, so it fires).
        A DENSITY FLOOR NEEDS ONE EXEMPTION AND IT MUST BE MEASURED. An editor's
        information is what is IN it, not the labels around it, so a rule
        counting stated facts would ask every editor in the product to become a
        form. The exemption is not "this is an editor" but a measurement that the
        editable region really is the majority of what the surface drew — which
        is why the case it must not cover, a sparse page with a note box on it,
        stays convicted.
        AND CONTEXTUAL IS A RELATION, SO IT IS DRIVEN: two selections, joined to
        what the harness selected rather than to what the panel says it selected,
        plus the mirror — a panel showing a subject nobody chose is a second
        workspace wearing an inspector's label.
        HARNESS TRAP: `browserHost` prepends a button outside the shell for
        another tool's focus proof, and twenty pixels of furniture above a 100vh
        shell pushes the status strip off the bottom of every first screen.
        Removed for this run, and CONTROL 6 puts it back to prove that removing
        it was necessary rather than convenient.
        DIST-PATCH VERIFICATION three ways: raising `DOMINANT_AREA_SHARE` to 0.9
        failed 6 controls, silencing `marketingMarksIn` failed 1, and flattening
        `inspectorCeilingPx` to the shipped 720 failed 2. Restoring returned
        60/60. -->
        <!-- Re-ran after the fixture and stylesheet changes, all clean:
        `workbench-shell-browser` (every control and fault as declared),
        `card-use-browser` (1037 containers, 19 cards, every one driven),
        `chrome-browser` (54 effects, all conveying), `composition-browser` (520
        nested boundaries, all warranted), `signals-browser` (1 accent family,
        189/189 paints, 153/153 pairs at AA), `first-screen-browser` (20
        surfaces, no pattern found), `ship-boundary-scan` (25/25) and
        `packed-consumer-smoke` (127 subpaths). The primitive chain is untouched:
        the text-scale migration deliberately skipped `primitives/`, which
        already consulted the token. -->
        <!-- OPEN, and recorded rather than resolved: the density floor is stated
        per SCREEN, so a surface listing a genuinely short collection can sit
        under it for a reason belonging to the record rather than to the layout.
        The run reports the measured figure for every surface rather than
        exempting the case, and the fixture's release list was lengthened to the
        length of its other lists rather than the floor being lowered — but a
        product surface over a four-record tenancy will read as sparse and that
        is a product decision this audit does not make. -->

  - [ ] S7.22.g Review all supported themes/densities/viewports and record
        approved exceptions with product/design/accessibility rationale.
        Evidence: signed design conformance report.
        <!-- NOT LOCALLY MARKABLE, AND THE REASON IS THE ITEM'S OWN EVIDENCE
        CLAUSE. §2.1: human governance approvals remain unchecked until
        performed by the named human, and an agent may prepare an
        engineering-readiness packet but may not fabricate approval. An EXCEPTION
        is not a failure somebody explained, it is one somebody with authority
        ACCEPTED — a rationale with no approver is a note — and the item names
        three rationales that are not interchangeable, because a product
        rationale ("this surface is not offered on handsets") cannot excuse an
        accessibility non-conformance: the reader that criterion protects is not
        a market segment. So the signature is the evidence, and nothing in this
        session may produce it.
        WHAT ALREADY EXISTS AS MEASURED EVIDENCE toward the packet, all of it
        re-runnable: the fourteen render contexts (three densities, the alternate
        theme, forced colours, both viewport ends, text zoom, type scale, reduced
        motion, RTL, bidi, long strings and font fallback) are declared in
        `libs/oshun/workbench-kit/src/primitive-contexts.ts` and measured over
        the primitive catalogue by `primitive-contexts-browser.mjs`;
        `signals-browser.mjs` measures every colour pair against the recorded
        WCAG 2.1 AA floors; `working-surface-browser.mjs` measures the layer at
        two viewport ends including the reflow width and the reader's text
        scale. WHAT THE PACKET STILL NEEDS: the cross-product run over the
        workbench SURFACES rather than the primitive catalogue, an exception
        register keyed to the failing cells and carrying an approver per
        rationale kind, and three signatures. Left unchecked deliberately;
        under-reporting is safer than a fabricated approval. -->

### S8 — Collaboration, review, notifications, and activity

- [x] S8.1 Extract approval workflows with role, quorum, sequence,
      separation-of-duties, and conflict-of-interest rules. Evidence:
      known-value policy tests.
      <!-- `libs/oshun/workbench-kit/src/approval-workflows.ts` (61 tests, 30
      fault kinds), subpath `./approval-workflows`. Rollup of S8.1.a-g below;
      every child is a section of the one module because the seven are one
      policy — an eligibility rule that could not see the quorum, or a quorum
      that could not see a recusal, would be seven policies that agree by
      accident.
      COMPOSE S2.13, DO NOT RE-DECIDE IT — eighth sighting of the S7.12 rule.
      `signoff.ts` had already settled what a SIGNATURE is: automation may
      schedule, remind, escalate, display and record a human gate and may never
      produce one; a service actor with the right role claim satisfies every
      role check ever written and is refused on its KIND before its role; a
      delegated actor may read everything and sign nothing; a step-up reused
      from login is a property of the session rather than of the judgement; and
      a rejection STANDS. None of it is repeated here, and `stageStanding` is
      the seam that hands the signature half back to `evaluateQuorum`.
      Gates run: `packed-consumer-smoke` (128 subpaths, no import-time side
      effects), `router-vocabulary-scan` (119 modules), the export-map generator
      (128 kit-owned entry points, 0 violations) and the API reference (19
      pages). -->
  - [x] S8.1.a Define workflow/version, target/revision, approval stages,
        verdicts, actors/eligible pools, deadlines, and terminal outcomes.
        Evidence: approval workflow schema.
        <!-- `STAGE_KINDS` (5), `STAGE_VERDICTS` (3), `WORKFLOW_OUTCOMES` (6),
        `WORKFLOW_ROLES` (4), `StageDefinition`, `WorkflowDefinition`,
        `ApprovalTarget`. TWO OUTCOMES ARE NOT VERDICTS: `withdrawn` is the
        proposer taking it back, and recording it as a rejection attributes a
        judgement to reviewers who never made it (S7.13's rule about the word a
        vocabulary cannot say); `expired` is terminal because a workflow that
        quietly stays open is one nobody is accountable for. A workflow with no
        stage is REFUSED rather than passed — it approves everything handed to
        it and reads in an audit as a workflow that ran. `policyVersion` is on
        the definition rather than beside it, because S8.1.g needs it pinned at
        the moment of signing. -->
  - [x] S8.1.b Define role requirements and eligibility by tenant, object,
        lifecycle, assignment, specialist qualification, and delegated scope.
        Evidence: role policy tests.
        <!-- `eligibility()` over eight `ELIGIBILITY_REASONS`, one of which is a
        yes. EVERY ANSWER IS A REASON RATHER THAN A BOOLEAN: a check that says
        no without saying why produces a workflow nobody can unblock, and the
        person who has to unblock it is by construction not the person the rule
        was protecting against. The ACTOR KIND IS ASKED FIRST, for S2.13's
        reason — it is the only disqualification no configuration may switch
        off. `redacted` is on that list for a different reason from the other
        three: an erased identity cannot be shown to be independent of anybody.
        A DELEGATION IS SCOPED TO STAGES: somebody handed the legal review while
        a colleague is away has not been handed the release approval, and a
        deputy whose delegator is not in the pool is not in the pool either. -->
  - [x] S8.1.c Define quorum math for eligible, completed, approving, rejecting,
        abstaining, recused, replaced, unavailable, and delegated reviewers.
        Evidence: quorum truth tables.
        <!-- `POPULATION_RULES` is the truth table written out as data and
        asserted row by row, over three columns: counts toward the quorum, stays
        in the pool, lowers the bar.
        THE DENOMINATOR IS THE WHOLE OF QUORUM MATH, AND THE TWO THAT LOOK ALIKE
        ARE OPPOSITES. A RECUSAL removes somebody from the pool AND lowers the
        bar, because it says this person MAY NOT judge and a quorum that outlives
        its own pool deadlocks with everybody behaving correctly. An
        UNAVAILABILITY removes them and lowers NOTHING, because it says they are
        NOT HERE TODAY, and a bar that falls for that is one anybody can lower
        with a calendar — which is how a two-of-three becomes a one-of-one in
        August. Known values: two-of-four with one recusal requires one and is
        satisfied; the same stage with one absence still requires two and is not.
        A DELEGATION MOVES AN AUTHORITY AND DOES NOT ADD A HEAD, so the count is
        over distinct authorities and two seats held by one authority are one
        approval with a count beside it.
        THE ADVERSARIAL PASS CAUGHT A DEAD RULE IN THIS MODULE'S OWN CODE, which
        is the shape it was written to hunt. `unavailability-lowered-the-bar`
        first compared `readQuorum`'s reduction against `readQuorum`'s own
        arithmetic — `required = max(1, quorum - recusals)` against
        `quorum - recusals` — and could never fire. THE CHECKED PARTY MUST NOT
        SUPPLY BOTH SIDES: a deployment that lowered its bar for absences did so
        before handing anything here, so `appliedQuorum` arrives as data and the
        rule compares it to what the populations justify. Split into two faults,
        both driven: the named case where absences account for the difference,
        and `applied-quorum-below-what-the-populations-justify` where nothing
        does. -->
  - [x] S8.1.d Define sequential, parallel, conditional, repeat-on-change,
        escalation, and veto stages. Evidence: sequence tests.
        <!-- A VETO IS NOT A REJECTION WITH MORE WEIGHT. A rejection is counted
        and can in principle be answered by a revision; a veto ends the workflow
        whatever the count says, which is what makes it worth having and what
        makes handing it out a decision rather than a default — so a veto stage
        that names no holder is refused, a veto held by somebody outside the pool
        is refused (an authority to end a review held by somebody who cannot take
        part in it is one nobody in the room can see), and a veto the count
        outlived is reported. A CONDITIONAL STAGE THAT STATES NO CONDITION runs
        on everything, and a legal review that runs on every change is a legal
        review nobody reads. AN ESCALATION REACHED BEFORE THE STAGE IT RESCUES is
        the ordinary path with a different name, and A DEADLINE WITH NOTHING ON
        THE OTHER SIDE OF IT is a note in a ticket. Two stages may share a
        position only where both are parallel; a sequence in which two stages are
        both next has no order to be out of. `deriveOutcome` asks the veto first
        and the rejection second, and the outcome is DERIVED so a stored one that
        disagrees with its own stages is reportable. -->
  - [x] S8.1.e Enforce proposer/reviewer/approver/publisher and other
        separation-of-duties constraints. Evidence: self-approval negative
        tests.
        <!-- THE TYPE CHECKER FOUND THE FINDING, AND IT INVERTED WHERE THE RULE
        BELONGS. The first draft read the authority off the signature, and a
        `SignoffRecord` CANNOT BE DELEGATED at all — `admitSigner` refuses
        `acting-on-behalf-of-somebody-else` and `by` is a `HumanPresence` no
        delegated actor can produce. So a separation check written over
        signatures is safe BY CONSTRUCTION and reads exactly like a check that
        had done its job. THE GUARANTEE IS ONE LEVEL DOWN AND THE HOLE IS ONE
        LEVEL UP: the workflow SEAT carries the authority it draws on separately
        from the person filling it, which is how a proposer's authority reaches
        an approval without their name appearing on it. The rule reads seats and
        joins signatures to them.
        AND IT IS OVER THE WHOLE HISTORY OF THE OBJECT, NOT OVER THIS REVISION.
        Somebody who proposed revision three and approves revision seven is still
        the proposer, and a check against the current revision's proposer says
        yes — which lets one person hold every role over an object's life, one
        revision at a time. `publisher` is in the vocabulary because the
        separation people forget is the last one: the person who approved a
        release pressing publish is the same person deciding twice, by which
        point everybody has stopped watching. -->
  - [x] S8.1.f Define conflict disclosure, recusal, reassignment, confidential
        detail, override authority, and audit. Evidence: COI lifecycle tests.
        <!-- A CONFLICT'S DETAIL IS OFTEN CONFIDENTIAL AND ITS EXISTENCE NEVER
        IS. The quorum has to know two reviewers share a conflict without anybody
        reading what it is, which is why the shared thing is an identifier —
        S2.13's `conflictId` composed rather than re-decided — and a deployment
        that can only see the sharing by opening the detail is reported.
        A DISCLOSURE THAT CHANGES NOTHING is a paragraph nobody has to act on, so
        a disclosure with neither a recusal nor an override is a fault. AN
        OVERRIDE IS ITSELF A JUDGEMENT and needs its own separation: the
        conflicted party overriding their own recusal clears their own conflict,
        and the proposer overriding it is the person who wants it approved
        choosing who may judge it. Both are reported and an override by anybody
        else stands. -->
  - [x] S8.1.g Preserve exact evidence/policy/target versions and invalidate
        stale approvals. Evidence: dependency change tests.
        <!-- S2.13 already invalidates on a moved TARGET and moved EVIDENCE; the
        third one is THE RULE ITSELF. An approval collected under a two-of-three
        does not satisfy a three-of-three, and nothing in the record can say
        which was in force unless the policy version is pinned at the moment of
        signing — so `signedUnderPolicyVersion` is recorded per signature and
        compared against the definition's current `policyVersion`.
        REPEAT-ON-CHANGE IS WHAT MAKES AN APPROVAL ABOUT A REVISION rather than
        about an object, and an object is whatever it was last edited into. The
        stage DECLARES whether a material change reopens it, because a stage that
        says no is saying the judgement survives an edit — a real and rare thing,
        a licence check over a fixed dependency set — and it has to be said
        rather than defaulted. A record added after a terminal outcome is
        reported: a decision that keeps accepting signatures is one that can be
        changed after it was announced. -->
- [x] S8.2 Extract anchored comment threads with stable target adapters.
      Evidence: anchors survive unrelated revisions or are explicitly orphaned.
      <!-- `libs/oshun/workbench-kit/src/comment-threads.ts` (101 tests, 66 fault
      kinds), subpath `./comment-threads`. Rollup of S8.2.a-g below.
      THE VEIN: S1.4.e WROTE THE ANSWER DOWN IN FULL AND NOTHING HAS EVER
      PRODUCED ONE. `AnchorResolution` — three statuses, five orphan causes, a
      rebase confidence, and a sentence instructing callers to treat a
      low-confidence rebase as ambiguous — is a complete and correct description
      of the only question an anchor exists to be asked, and a repository-wide
      search finds it referenced in exactly two places, both of them its own
      declaration. `StableAnchor` has producers (S2.5 hangs two on every diff
      operation, S7.9 asks a block type for its own); the RESOLUTION had no
      constructor and no reader anywhere. The judge was written and the case
      never came. This module is the producer.
      AND WRITING THE PRODUCER FOUND THE CASE THE TYPE WAS SHORT, which is the
      whole argument for writing it: all five shipped causes are statements about
      the CONTENT, and none of them can say the thing that actually happens in a
      deployment, which is that this build has no adapter for this anchor kind
      and did not look. `unsupported` is now the sixth. A type nobody constructs
      is a type nobody discovers is missing a case.
      THE EVIDENCE CLAUSE IS A TAUTOLOGY OVER THE TYPE AND THAT IS THE POINT:
      "survive or be explicitly orphaned" is checkable only because nothing here
      can return a fourth thing, and the number that makes the summary worth
      reading is `unexamined` — how many anchors this build did not look at.
      COMPOSE, DO NOT RE-DECIDE — ninth sighting of the S7.12 rule, and it
      reached six modules this time: S1.4.e owns the anchor kinds and the
      resolution vocabulary, S2.5 owns the change types and the semantic /
      presentation split, S7.6 owns flicks and frame rates, S7.8 owns the three
      identity sources and which one survives an edit, S3.13 owns the sensitivity
      classes and which of them make existence the disclosure, S4.3 owns conceal
      outranking deny. Nothing on that list is restated here.
      Verification: `npx tsc -p tsconfig.lib.json` and `-p tsconfig.spec.json`
      clean, 101 tests, the full kit suite at 7280 across 125 files, eslint and
      prettier clean, `packed-consumer-smoke` at 129 subpaths, the router
      vocabulary scan clean, the stub-indicator scan at zero actionable hits, the
      NUL scan clean, both kit generators regenerated with the export map at zero
      violations and the API reference violation count unmoved from its committed
      73. Five SOURCE INJECTIONS proved the spec is not vacuous — see S8.2.d. -->
  - [x] S8.2.a Define thread/comment identity, target adapter/type, stable
        anchor, base revision, author/delegate, visibility, state, timestamps,
        and parent/reply. Evidence: comment schema.
        <!-- `threadSchemaFaults` over `ThreadRecord`/`CommentRecord`, 22 fault
        kinds. THE ANCHOR BELONGS TO THE THREAD, NOT TO THE COMMENT — a comment
        carrying its own would let a reply re-point the conversation, so a reader
        scrolling to the third one lands somewhere the first two were not talking
        about, and "resolve this thread" stops being a sentence that means
        anything. The record makes it unexpressible and the hole it leaves is
        `reply-to-a-comment-in-another-thread`, which is that defect arriving
        through a parent id.
        A COMMENT IS NOT A SIGNATURE AND THE DIFFERENCE RUNS THE OTHER WAY FROM
        S8.1's: S2.13 refuses a delegated SIGNER because a signature is a
        judgement nobody may hold on somebody's behalf; a comment is a STATEMENT
        and a deputy may perfectly well make one. What may not happen is that it
        READS as the principal's. And since `ActorRef` carries both identities by
        construction, a check over `author` alone is safe for free and reads
        exactly like a check that had done its job — the S8.1 shape again — so
        the record carries `attributedTo`, the identity the SURFACE displays, and
        the rule is there. `presentedAsAutomated` is the same argument for a gate
        bot: it may comment, and a machine remark rendered like a colleague's is
        answered like a colleague's.
        VISIBILITY IS A FLOOR ON WHO MAY BE TOLD, NEVER A GRANT: the vocabulary
        is ordered by breadth so a target's ceiling is arithmetic rather than a
        table somebody keeps in step, and `reviewers` sits between `tenant` and
        `participants` because a review note about a contributor's work is read
        by more people than the conversation has and fewer than the object has.
        ARCHIVE IS TERMINAL AND RESOLVE IS NOT — an answer can be wrong, so a
        resolution is reversible; an archived thread is off the object. -->
        <!-- THE ADVERSARIAL PASS OVER MY OWN MODULE FOUND THE EMPTY-ARGUMENT
        SHAPE, eleventh sighting: every rule that consults the target's policy
        was written `policy !== undefined && …`, so a thread whose target has no
        policy row had its visibility ceiling, its classification and its
        who-closed-it rule all silently not run and came back with no findings —
        indistinguishable from a thread that passed. An absent ceiling is not a
        ceiling of unlimited; it is a check that did not happen, and
        `thread-on-a-target-with-no-declared-policy` says so. -->
        <!-- SEPARATION OF DUTIES REACHES THE COMMENT SURFACE TOO:
        `thread-resolved-by-the-reviewed-party-with-no-answer` is S8.1's rule one
        floor down — the person whose work is being reviewed closing the
        reviewer's mark with nothing said back is one person deciding twice, and
        the mark disappears with no record that anybody agreed. Reported rather
        than refused, because a typo the author simply fixes is the ordinary
        case; a reply of any kind clears it. -->
  - [x] S8.2.b Register entity/field/block/range/temporal/graph/canvas/hierarchy
        anchor resolve/rebase/context adapters. Evidence: adapter contract
        tests.
        <!-- `buildAnchorRegistry` over `AnchorAdapterDeclaration`, 8 fault
        kinds, all eight of S1.4.e's anchor kinds required.
        THE REGISTRY IS BUILT BY THE DEPLOYMENT RATHER THAN FROZEN INTO THE KIT.
        A kit shipping it as a constant is one in which no deployment can ever
        add the temporal adapter, so every temporal anchor everywhere is
        permanently unsupported with nothing anybody can do about it — the
        frozen-capability-registry shape, caught before it was written.
        AN ADAPTER'S IDENTITY SOURCE IS THE CEILING ON EVERY ANSWER IT CAN GIVE,
        and this is the finding that makes the declaration load-bearing rather
        than decorative. S7.8 already found the three ways a thing can be named
        and that only an assigned id survives an edit; an adapter addressing by
        POSITION or by CONTENT HASH cannot distinguish "it survived" from
        "something else is in that slot now", so over any structural edit its
        best honest answer is `ambiguous` whatever its rebase arithmetic
        believes. `resolveAnchor` applies it before dispatching to any adapter,
        and the test shows the same edit rebasing cleanly on stable ids and
        orphaning on positional ones.
        A CONTEXT KIND A COORDINATE SPACE CANNOT PRODUCE IS A PROMISE THAT
        DELIVERS WHATEVER WAS NEAREST: an entity anchor has no excerpt, there is
        no passage, only the whole object; a temporal anchor has no structural
        path. `CONTEXT_KINDS_FOR_ANCHOR` is the map and an adapter claiming
        outside it is reported. And an adapter that REBASES what it cannot
        RESOLVE is moving an anchor to a place nobody checked. -->
        <!-- The version field is S2.5's, for S2.5's reason: a cached resolution
        computed under an older rebase rule is not a resolution, it is last
        week's answer to a question whose rules moved. -->
  - [x] S8.2.c Preserve original excerpt/thumbnail/structural context and exact
        revision without leaking protected content. Evidence: context security
        tests.
        <!-- `contextFaults` over `AnchorContext`, 9 fault kinds.
        A CONTEXT SNAPSHOT IS A COPY THAT OUTLIVES ITS ACCESS CONTROL. The
        excerpt the item asks for is the sentence the comment is about, stored in
        the COMMENT table — a different table, with different permissions, that
        no retention sweep over the document will ever visit. So it is bounded
        against a kit-owned ceiling (past some length it stops being a pointer
        and becomes a second copy of the document), it carries the class of what
        it was taken from, it is re-checked against the target's CURRENT
        protection rather than the one at capture, and when the source is erased
        it goes too: erasing a document and keeping the passage in a comment is
        not erasure, it is a copy nobody looked for.
        AND THE MIRROR DEFECT IS JUST AS REAL AND POINTS THE OPPOSITE WAY. A
        context RE-READ from the current revision makes the comment look like it
        was about text that did not exist when it was written. Never refreshed,
        sometimes withheld — and the two do not conflict, because withholding is
        not refreshing. The tell for a refresh needs no ordering and none is
        available (a revision id is not a number): the captured revision being
        the one BEING READ, while the anchor was taken at another, is the whole
        of it.
        A WITHHELD CONTEXT MUST SAY SO. An excerpt silently blanked renders as a
        comment about nothing rather than as a refusal, which is S1.6.e's rule
        about fabricated fallbacks reaching a review surface.
        FOR A PRESENCE-DISCLOSING CLASS THERE IS NO SAFE EXCERPT AT ALL — S3.13's
        `protected-source`, `health`, `biometric`, where the existence of the row
        IS the disclosure — so a stored context answers exactly the question the
        object's own access control refuses, sealed or not. -->
        <!-- The budget is counted in CODE POINTS rather than UTF-16 units, and
        the test pins it: ten astral characters are twenty units, so a
        unit-counting budget convicts every emoji and lets twice as much Latin
        text through as it meant to. -->
        <!-- NOT MEASURED HERE, said rather than implied: nothing in this module
        reads a document. Whether the excerpt a host captured is really the
        passage under the anchor is the host's to prove; what is checked is the
        REVISION it was captured at, the CLASS it was taken from, its SIZE, and
        whether the adapter that owns the anchor preserves that kind at all. -->
  - [x] S8.2.d Rebase anchors across unrelated edits, moves, split/merge, timing
        conversion, and schema migration where unambiguous. Evidence: rebasing
        fixtures.
        <!-- `resolveAnchor` + eight per-kind resolvers + `applyTextEdit` +
        `applyConfidenceThreshold`, 33 known-value rebasing tests.
        THE EDITS ARE S2.5's VOCABULARY, NOT A SECOND ONE — `ChangeType` and
        `DiffSignificance` are imported, and the coordinate SPACES are
        deliberately fewer than the anchor kinds: `text` covers both `block` and
        `range`, because a model with one arm per kind makes the caller describe
        the same deletion twice and lets the two descriptions disagree.
        A RANGE ANCHOR IS ABOUT ITS CONTENT, NOT ABOUT ITS COORDINATES, which
        settles the boundary case — and the boundary case is the COMMON case,
        somebody typing at the caret where a highlight begins. THE TEST FOUND
        WHAT THE OBVIOUS SHAPE CANNOT SAY: the first draft shared one
        `shiftOffset(o)` between the two ends, which is what everybody writes,
        and it treats a range as two independent points. A RANGE'S TWO ENDS HAVE
        OPPOSITE GRAVITY: text arriving AT the start pushes the start right, text
        arriving AT the end does not push the end right, because a range is the
        content between its ends rather than the ends. Under one map an insertion
        at the start either extends the selection at both ends or at neither, and
        the comment ends up about a sentence the author never highlighted. Known
        values: insert 4 at 10 into 10..20 gives 14..24 (ten code points, the
        same ten); insert 4 at 20 gives 10..20 unchanged.
        THE TYPE ALREADY REFUSED THE ROUNDED ANSWER AND NOBODY HAD EVER MADE IT
        SAY SO. `parseStableAnchor` rejects a fractional frame with the words
        "rounding it silently moves the anchor" — a rule written for S1.4.e and
        never exercised, because nothing had ever converted an anchor. So a note
        on frame 100 conformed from 24fps to 25fps has NO REBASE IT IS ALLOWED TO
        RETURN: 2 940 000 000 flicks over a 28 224 000-flick frame is 104.1666…,
        and it orphans as `ambiguous` for a human to pick. The same conversion at
        frame 24 lands on 25 exactly and rebases at confidence 0.96, which is how
        much of the original frame the new one covers.
        AN ANCHOR MUST NOT MOVE FOR A CHANGE THAT WAS NOT A CHANGE TO THE WORK.
        S2.5 separated `presentation` from `semantic` so a review would not report
        forty-seven changes for an afternoon of tidying a graph; the same
        operations reach here, and a comment reporting itself rebased because
        somebody dragged a node teaches its reader that "rebased" means nothing.
        A SPLIT IS THE OPERATION THAT DECIDES THE WHOLE SECTION'S CHARACTER: a
        BLOCK anchor over a split paragraph orphans, because no arithmetic picks
        a half; a RANGE anchor over a split OUTSIDE its selection follows the
        half that holds it with the offsets adjusted; and a range split THROUGH
        its selection orphans, because the range now spans two blocks and a
        `range` anchor cannot say that. The item's word is orphan rather than
        move, and coarsening a canvas part up to its object is a silent move
        dressed as a rescue.
        A SCHEMA THAT MOVED WITH NO DECLARED MAPPING IS `migrated`, derived from
        `RevisionRef.schemaVersion` rather than from a flag: the field may still
        be spelled the same and mean something else, and no evidence available
        here can say otherwise.
        THE CONFIDENCE IS AN UPPER BOUND AND IT IS MONOTONE — the smallest the
        range has ever been is the most of it that can have survived, because an
        insertion cannot restore what a deletion took. Deleting 15..20 out of
        10..20 gives 10..15 at confidence 0.5. -->
        <!-- `applyConfidenceThreshold` is S1.4.e's own sentence — "below a
        caller's threshold, treat as ambiguous" — carried out for the first time,
        and it is a separate function rather than a parameter because the
        threshold is a PRODUCT decision: a legal review wants every doubt in front
        of a human, a copy edit does not, and one resolution serves both. -->
        <!-- A programming error is thrown rather than orphaned: S1.4.e says an
        anchor from another entity is a wiring bug, and returning `orphaned` would
        hide a bug behind a product state the UI already knows how to render. -->
        <!-- VERIFIED BY FIVE SOURCE INJECTIONS, since a headless module has no
        dist-patch ritual: sharing one gravity between the two range ends failed
        3 tests, rounding the timing conversion failed 1, reporting a missing
        adapter as `deleted` failed 3, dropping the identity-source ceiling
        failed 1, and letting presentation edits through failed 1. -->
  - [x] S8.2.e Mark deleted/replaced/ambiguous/permission-hidden/unsupported
        anchors orphaned with reason and original context rather than moving
        silently. Evidence: orphan tests.
        <!-- `orphanFaults` over `OrphanPresentation`, 7 fault kinds, plus the
        addition to S1.4.e that writing the producer forced.
        THE ITEM NAMES FIVE CAUSES AND THE TYPE SHIPPED FIVE AND THEY WERE NOT
        THE SAME FIVE. `AnchorResolution` had deleted / replaced / migrated /
        permission-hidden / ambiguous; the item asks for deleted / replaced /
        ambiguous / permission-hidden / UNSUPPORTED. All of the shipped five are
        statements about the CONTENT. `unsupported` is a statement about the
        DEPLOYMENT: this build has no adapter for this kind of anchor and DID NOT
        LOOK. Reporting that as `deleted` tells a reviewer their clip is gone when
        what is missing is the code that would have gone to find it, which is the
        fabricated answer S1.6.e forbids — so `ANCHOR_ORPHAN_CAUSES` is now six,
        and `unsupported-reported-as-a-fact-about-the-content` is the fault that
        catches a surface collapsing them.
        A TYPE NOBODY CONSTRUCTS IS A TYPE NOBODY DISCOVERS IS SHORT A CASE. The
        gap had been sitting in the file since S1.4.e and is only visible from
        the producer's side.
        AN ORPHAN IS NOT A DELETION: the thread stays, readable, and it must keep
        the ORIGINAL anchor and the ORIGINAL context or no human can put it back
        — a comment reading "this sentence is wrong" with no sentence is a remark
        about a document nobody can reconstruct. The one cause exempted from the
        context requirement is `permission-hidden`, where there was never
        anything this reader could be shown.
        A REFUSAL THAT EXPLAINS ITSELF PRECISELY IS A DISCLOSURE. Rendering
        "there is a comment here you may not see" over a `protected-source`
        target answers exactly the question the class exists to refuse, so for
        S3.13's presence-disclosing classes S4.3's conceal outranks deny reaches
        the comment layer: the thread is ABSENT rather than explained.
        AND THE MIRROR IS A FAULT TOO — a surviving anchor advertised as broken
        sends a reviewer looking for a deletion that never happened. -->
        <!-- The spec proves all six causes are REACHABLE from real inputs rather
        than merely declared: a removed block, a replaced one, a moved schema
        with no mapping, a reader who may not see the target, a split paragraph,
        and a registry with the canvas adapter left out. -->
        <!-- `summarizeResolutions` builds its per-cause counters FROM the
        vocabulary rather than writing them out, so a seventh cause added to
        S1.4.e cannot land here as an `undefined + 1` that turns a count into NaN
        and a summary into a blank. `unexamined` is surfaced separately because
        it is the one number an operator needs: how many anchors this build did
        not look at. -->
  - [x] S8.2.f Implement create/reply/edit-window/resolve/reopen/archive/link/
        navigate with authorization, expected revision, notifications, and
        audit. Evidence: lifecycle tests.
        <!-- `lifecycleFaults` + `editWindowState` over `OperationAttempt`, 14
        fault kinds, all eight operations.
        AN EDIT WINDOW IS ABOUT THE READER, NOT THE WRITER, and every
        implementation that is only a clock has the rule backwards. The reason a
        window exists at all is that somebody may already have ANSWERED; once
        they have, editing rewrites the thing they answered and their reply
        becomes a response to words nobody wrote. So it closes on the first reply
        as well as on the clock, whichever comes first — five minutes in, well
        inside any clock, an answered comment is closed — and the two closures
        are reported separately, because a product offering "edit anyway, marked
        as edited" needs to know which one it is arguing with. A WITHDRAWN reply
        does not close it, since there is no longer an answer to invalidate.
        ARCHIVE IS TERMINAL FOR BOTH DIRECTIONS: a reply arriving after it and a
        reopen of it are both faults, which is S8.1's rule about a record added
        after a terminal outcome — a decision that keeps accepting input is one
        that can be changed after it was announced.
        NAVIGATION IS THE OPERATION WHOSE FAILURE IS INVISIBLE. Asked to go to an
        orphaned anchor a surface either does not move or moves to whatever is
        now at those coordinates, and BOTH look like the product working, so the
        refusal has to be shown. It is also the one operation that must not be
        written to the mutation audit: a read filed as a change makes every "who
        changed this" query answer with the people who looked.
        A LINK THAT PINS NO REVISION opens against head and shows different text
        than the notice that carried it described.
        THE NOTICE IS THE LEAK, NOT THE LINK — S7.19's first decision, and a
        comment body is the one string in this product that lands on a lock
        screen, so a notification quoting it is reported whatever the thread's
        visibility says. -->
        <!-- Every mutation carries an expected revision, which is S2.3's rule
        rather than a new one: there is no nullable precondition anywhere in this
        kit because a caller who does not know what they are anchoring to must
        not be able to say so. `navigate` and `link` are exempt because they
        change nothing. -->
        <!-- THE ADVERSARIAL PASS FOUND THE EMPTY-ARGUMENT SHAPE HERE TOO: an
        `edit` attempt naming no comment in the thread has no window to be inside,
        so the window rule never ran and the attempt reported clean — the same
        defect as the missing policy row in S8.2.a, in a different section of the
        same file. `edit-that-names-no-comment` closes it. -->
  - [x] S8.2.g Provide keyboard/screen-reader bidirectional navigation between
        anchor and thread. Evidence: accessible review journey.
        <!-- `reviewJourneyFaults` over `ReviewJourneyObservation`, 8 fault
        kinds, the shape S7.4's `treeJourneyFaults` established for a declared
        journey.
        BIDIRECTIONAL MEANS THE ROUND TRIP ENDS WHERE IT STARTED. Two one-way
        links are two one-way links, and the rule that checks only for their
        existence passes the failure that actually happens: a "back to the text"
        that returns focus to the top of the document has ended the review for
        everybody not using a mouse, and both links work perfectly. So the
        journey is measured as a round trip and the position before is compared
        against the position after.
        THE ANNOUNCEMENT MUST SAY WHAT THE COMMENT IS ATTACHED TO, because the
        sighted reader is told that by the highlight and this reader is told it
        by nothing — "comment, 2 replies" is a different amount of information
        depending on who is receiving it. And an ORPHANED thread announced as
        though it still points somewhere is the silent move of S8.2.e reaching
        the one reader who cannot see that it did not move.
        A MARK THAT IS ONLY A COLOUR is S7.22.e's second reader arriving here:
        somebody who can see the screen and cannot separate its hues.
        AND THE FOCUS ORDER FOLLOWS THE DOCUMENT RATHER THAN THE THREAD LIST — a
        review read in creation order asks its reader to hold the document in
        their head while the sighted reader reads it off the page. -->
        <!-- Headless, and deliberately: the parent's evidence clause is about
        anchors surviving revisions, and S7's browser-tool recipe belongs to the
        items whose evidence is a rendered surface. What is checked here is that
        a host which DECLARES a journey has declared a coherent one; driving a
        real review surface belongs to the workbench that ships one. -->
- [x] S8.3 Add suggestions distinct from comments and direct edits. Evidence:
      accept/reject creates attributed revisions.
      <!-- `libs/oshun/workbench-kit/src/suggestions.ts` (101 tests, 113 fault
      kinds), subpath `./suggestions`. Rollup of S8.3.a-g below; every child is
      a section of the one module, because a proposal whose schema could not see
      its own acceptance rule would be two policies agreeing by accident.
      THE VEIN — AN ACCEPTED SUGGESTION HAS TWO PARTIES AND THE REVISION RECORD
      HAD ONE SLOT. A comment says a change is needed, an edit makes it and takes
      the responsibility, and a suggestion is the third thing: somebody who can
      see what the change should be, writing it out, without the standing to
      commit it. It is the only one of the three with two people in it.
      `RevisionRecord` carries one `author`, which was enough for exactly as long
      as nothing could put somebody else's content into a revision — and then one
      field has to answer both "who changed this?", whose answer is the person
      accountable for the object being in this state, and "whose words are
      these?", whose answer is the person who wrote them.
      AND THE LOSS IS NOT THE CREDIT, IT IS A CONTROL. S8.1 checks separation of
      duties over the WHOLE HISTORY of the object, against
      `roleHistory.proposer`, documented as "every authority that has ever held
      each role on this object, all revisions" — and a host can build that list
      from nowhere but the revision history. So the laundering path S8.1 closed
      at the SEAT reopens one level down at the CONTRIBUTION: write the change as
      a proposal, have a colleague admit it, then approve it yourself. Every name
      on the way is a different name and the words are yours. `revisions.ts` now
      carries `RevisionContribution`, `checkHistoryIntegrity` gained the two ways
      it can be a fiction, and `proposingAuthorities()` is the seam that hands
      S8.1 the list it was always documented to receive — with a negative control
      asserting that the same history WITHOUT a contribution hands back only the
      accepter.
      `ActorRef` CANNOT EXPRESS IT AND STATES THE INVERSE: `delegated`'s
      `onBehalfOf` is somebody else's AUTHORITY over your act, and this is your
      own authority over somebody else's words. The two are not the same relation
      pointed the other way; they are different relations, and the union has one
      of them.
      COMPOSE, DO NOT RE-DECIDE (ninth sighting of the S7.12 rule): S2.5 owns the
      change vocabulary and the semantic/presentation split, S1.4.e owns the
      anchor and S8.2 owns its resolution, S2.4 owns lineage and immutability,
      S2.10 owns submit-versus-apply, S4.3 owns the object decision, S7.8 owns
      dirty state, S2.13 owns what automation may never do.
      Verification: 9 SOURCE INJECTIONS, each caught, against a control arm that
      passes — a null lineage base (5 fails), a contribution never written (3), a
      rebase trusted without the threshold (3), the proposer-authorization check
      guarded on its own presence (2), the dirty-comparison move unchecked (2),
      a withdrawal compared at the actor id rather than the authority (1), a
      partial acceptance left live (1), the path collision underived (2), and
      automation allowed to admit (3). Plus a coverage test asserting all 113
      declared fault kinds are reachable — which is what found that this module's
      OWN `acceptance-whose-revision-is-authored-by-the-proposer` was guarded by
      the condition that would have made it fire, so it could never fire and read
      exactly like a rule that had passed. The check written to hunt that shape,
      built with it. -->
  - [x] S8.3.a Define suggestion identity, typed operation/patch, target/base
        revision/anchor, proposer, rationale, evidence, state, and expiry.
        Evidence: suggestion schema.
        <!-- `SuggestionRecord` + `ProposedOperation` + `SuggestionEvidence`,
        `suggestionSchemaFaults` over 27 kinds.
        THE PATCH IS S2.5's VOCABULARY RATHER THAN A SECOND ONE — the change
        types, the semantic/presentation split and the `DiffValue` refusals are
        the ones a diff already speaks, so a client that can render a review can
        render a proposal without a second renderer that disagrees with the first
        about what `moved` means. And `from` is the value AS THE PROPOSER SAW IT,
        which is the field that makes a stale proposal detectable at all: if the
        target no longer holds it, somebody has been here since, and admitting
        `to` silently discards their work.
        SEVEN STATES, AND `accepted` IS NOT `partially-accepted` — the first says
        the document now contains everything the proposer asked for and the
        second says it contains some of it, which is a different thing to have
        agreed to and a different thing to be told your proposal received.
        EVIDENCE IS A POINTER AND A RATIONALE IS AN ARGUMENT. Every evidence kind
        names something outside the proposal that the reader can go and check, so
        an entry with an empty reference is a second rationale wearing evidence's
        name — and a reviewer told a proposal is evidenced has been told
        something that is not true. The note may be empty; the reference may not.
        AN EXPIRY IS A PROMISE TO THE PROPOSER, so one that precedes the proposal
        means it was never open for anybody to read, and an `expired` record with
        no declared expiry means something closed it on a clock nobody published.
        ACCEPTANCE AND ENDING ARE TWO NULLABLE FIELDS RATHER THAN ONE OUTCOME
        UNION, because they are not alternatives of equal weight: an acceptance
        produces a revision and has to point at it, and the other four produce
        nothing and must be provably unable to. Their coherence with `state` is
        checked rather than assumed, which is the only way a stored row saying
        `rejected` while carrying an acceptance is ever found. -->
  - [x] S8.3.b Render proposed change and semantic diff without applying it to
        authored truth or dirty state. Evidence: proposal isolation tests.
        <!-- `PreviewObservation` + `isolationFaults`, 14 kinds.
        THE SECOND HALF OF THE ITEM IS THE ONE THAT BITES. Nobody writes a
        revision to render a preview — that fault is real and rare. Everybody
        renders the preview by putting the proposed text into the editor's
        buffer, and from that moment it is indistinguishable from the author's
        own unsaved work: the next save, or the autosave nobody pressed, commits
        somebody else's proposal under the author's name with no acceptance
        anywhere in the record.
        S7.8 HAD ALREADY CHOSEN THE PROPERTY THAT SURVIVES A PROJECTION and did
        not know it. Dirty as a COMPARISON AGAINST THE BASE can ignore something
        that was never in the base; dirty as a COUNT OF KEYSTROKES cannot tell
        the author's typing from the projection's insertion, so it either loses
        their work or attributes the proposal to them. The `counted-keystrokes`
        source is a fault here for a reason S7.8 never had to state.
        THE MIRROR OF THE DIRTY FLAG IS AS REAL AS THE FLAG: a projection that
        makes a clean document dirty warns the author about unsaved changes they
        did not make, and the one prompt that could have saved them is now the
        one they have learned to dismiss. A document that was ALREADY dirty is
        left alone, or the finding trains its reader to ignore it.
        A PROPOSAL IS NOT A LOCK. Proposals are the cheapest thing in the system
        to create, so a preview that takes the document away from its author lets
        anybody who may propose stop anybody who may write.
        AND THE SEMANTIC DIFF FAILS BOTH WAYS: counting a presentation part
        reports tidying as change (S2.5's whole reason), and omitting a semantic
        one means the reader who reads the summary instead of the document is
        deciding without the change in front of them. -->
  - [x] S8.3.c Authorize creation independently from direct edit and approval
        rights. Evidence: permission matrix.
        <!-- `PermissionMatrix` + `CapabilityRule` + `permissionMatrixFaults`,
        14 kinds.
        "INDEPENDENTLY" FAILS IN BOTH DIRECTIONS AND ONLY ONE OF THEM LOOKS LIKE
        A BUG. A deployment that CONFERS `propose` on everybody holding
        `edit-directly` has renamed a right rather than created one, and the
        population the feature exists for — the people who can see what the
        change should be and may not make it — still has nothing. A deployment
        that REQUIRES `edit-directly` in order to propose has made the feature
        unavailable to exactly that population, so nothing is ever proposed. That
        is why the rule carries a RELATION rather than being a graph edge:
        `confers` and `requires` are not inverses and they break the item's claim
        in opposite directions.
        THE SHARP EDGE IS THAT ACCEPTANCE MUST BE A SUBSET OF DIRECT EDIT. An
        accepter who may not edit is an escalation channel, not a safeguard: a
        proposer puts any change at all in front of them, and admitting it writes
        content into the document that nobody trusted to write it ever agreed to.
        The reading that feels safer — "they can only accept, not author" — is
        the one that is wrong.
        A GRANT SAYS WHETHER SOMEBODY MAY EVER, AND AN OBJECT DECISION SAYS
        WHETHER THEY MAY HERE. So an exercise carries the S4.3 disposition, and
        one with `null` is the empty-argument shape made a fault rather than a
        silent pass (twelfth sighting): a system holding only the matrix
        authorizes the reader of one tenant's proposal against another tenant's
        rules. Both disagreements are reported, and the one that cannot be safe
        is named as such. -->
  - [x] S8.3.d Revalidate target revision, anchor, schema, authorization,
        validation, and dependencies at acceptance. Evidence: stale suggestion
        tests.
        <!-- `revalidate` → `RevalidationReport`, the item's six checks by name,
        14 fault kinds.
        THE AUTHORIZATION REVALIDATED AT ACCEPTANCE IS THE PROPOSER'S, NOT THE
        ACCEPTER'S — and this is the check nobody writes, because at the moment
        of acceptance the proposer is not the one making a request. The
        accepter's rights get checked because they are acting; the content is
        coming from somebody who may have left the team, had a grant revoked, or
        been moved out of the workspace, and admitting it then is admitting
        content from outside the trust boundary through a door that has closed.
        A MOVED DOCUMENT IS A FACT AND NOT A REFUSAL, which is why the verdicts
        are four-valued. "The document has moved under this proposal AND the
        anchor still resolves" is exactly the sentence the accepter needs and
        exactly the one a boolean cannot say. `not-checked` is the fourth,
        because a build that did not look must not report what a build that
        looked and was satisfied reports.
        ACCEPTANCE IS S8.2's `applyConfidenceThreshold` FINDING ITS SECOND
        CALLER, at a higher floor, and the asymmetry is the argument: a comment
        shown in the wrong place is read by somebody who can see it is the wrong
        place, and a patch applied in the wrong place is a change to the document
        that looks exactly like a change somebody meant.
        A SCHEMA MIGRATION IS REFUSED RATHER THAN REPORTED, because the paths in
        the patch are the old shape's paths and a migration is precisely the
        event that changes what one of them means. Nothing revalidates that
        except a person.
        A DECLARED DEPENDENCY WITH NO STATE IS THE EMPTY-ARGUMENT SHAPE AGAIN and
        is reported rather than skipped — a patch whose citation was deleted last
        week still applies cleanly and says something nobody can stand behind.
        AND THE WHOLE THING IS TAKEN AT APPLY, not at submit: everything between
        the two is a change nobody revalidated against, and the window is exactly
        as long as the queue is deep. -->
  - [x] S8.3.e Accept whole or supported atomic parts by creating an attributed
        new revision linked to proposer and accepting actor. Evidence:
        acceptance lineage tests.
        <!-- `acceptSuggestion` + `partSelectionRefusals` + `acceptanceFaults` +
        `proposingAuthorities`, 20 kinds. THE VEIN, and see the parent.
        `revisions.ts` gained `RevisionContribution` (contributor, proposalId,
        amended), `contribution` joined `IMMUTABLE_FIELDS` — an attribution that
        can be rewritten is one that can be removed by whoever the history was
        going to embarrass — and `checkHistoryIntegrity` gained two kinds. A
        revision that credits its OWN AUTHOR manufactures a second party, and
        every count taken over that history gets a head there is nobody to fill;
        a contribution naming no proposal has lost the only thing that makes the
        credit checkable.
        `amended` IS ON THE RECORD BECAUSE AN AMENDED PROPOSAL IS STILL THE
        PROPOSER'S CONTRIBUTION AND IS NO LONGER THEIR TEXT, and a reader told
        only "contributed by X" will quote them on words they did not write.
        THE LINEAGE FIELD FOR THIS WAS ALREADY THERE. `Lineage.linear` separates
        `parent` — where the content is going — from `base` — what the change was
        expressed against — and a proposal admitted after the document moved on
        is that separation's own case. An acceptance writing `base: null` throws
        away the only record of what the proposer could see, and after that
        revision nothing anywhere else has it.
        "SUPPORTED" IS LOAD-BEARING IN THE ITEM'S OWN SENTENCE. Two couplings and
        only one is declarable: a DECLARED requirement is a MEANING — this
        sentence only reads correctly after the one before it was rewritten — and
        nothing but the proposer can know it, while a DERIVED one is a collision
        the paths can see. Admitting one of a coupled pair produces a document
        neither party proposed, signed by both.
        A PARTIAL ACCEPTANCE IS TERMINAL, because the parts left behind were
        judged against a document the acceptance itself replaced; leaving them
        live invites a second accepter to admit a change revalidated against
        nothing.
        SELF-ACCEPTANCE IS COMPARED AT THE AUTHORITY, NOT THE ACTOR ID — a deputy
        admitting the proposal of the principal they act for is two names and one
        judgement, which is S8.1's seat walk-around arriving at the contribution.
        Where a deployment permits it, the result is produced as a DIRECT EDIT
        with no contribution written, because "X's proposal was accepted by X" is
        the sentence that puts the phantom head into the count.
        AND AUTOMATION MAY PROPOSE AND MAY NOT ADMIT (S2.13 composed, not
        re-decided): a linter that can write the fix it found is the best case
        for this whole feature, and deciding that somebody else's words belong in
        the document is a judgement that needs somebody who can be asked about
        it. -->
  - [x] S8.3.f Reject/withdraw/expire/supersede with reason while retaining
        immutable proposal/audit and no content mutation. Evidence: rejection
        tests.
        <!-- `SuggestionEnding` + `endingFaults`, 15 kinds.
        FOUR ENDINGS, FOUR DIFFERENT FACTS, AND ONE OF THEM HAS NO ACTOR. The
        item says "with reason" and the type deliberately does not give `expired`
        one: nobody decided an expiry, the clock did, so a shape demanding a
        human reason there is answered by whatever string the sweep job was
        written with, and every expired proposal in the system carries the same
        invented sentence in the field a reader goes to for a person's thinking.
        THE S7.13 RULE, A THIRD TIME, AND IT IS CHECKABLE IN BOTH DIRECTIONS. A
        rejection recorded on an ending the PROPOSER made is a withdrawal filed
        as a refusal — it puts a rejection on the record of somebody who was
        never refused. A withdrawal recorded on an ending SOMEBODY ELSE made
        files a reviewer's judgement as the proposer changing their mind, and the
        reviewer who made it cannot be asked why. Compared at the authority, so
        a deputy withdrawing on the proposer's behalf is correctly the proposer.
        A SUPERSESSION IS THE ONLY ENDING THAT POINTS SOMEWHERE, so it is the
        only one that can point wrong: at nothing, at another object, at a
        proposal that PREDATES it (the older thinking presented as the newer), or
        at one that dropped the discussion — and that last is the expensive one,
        because every objection raised against the first proposal has to be
        raised again by somebody who remembers it.
        DECLINING A CHANGE IS THE ONE OPERATION THAT MUST LEAVE THE DOCUMENT
        EXACTLY AS IT FOUND IT, so the content hash and the head are both
        compared across the ending, and the proposal's own operations, rationale,
        evidence, proposer, base, anchor and timestamp are frozen: the words that
        were declined are the evidence for the decision, and a proposal that can
        be tidied after it is judged is one nobody can be held to. -->
  - [x] S8.3.g Keep comment discussion and suggestion state linked but
        semantically distinct. Evidence: thread/suggestion tests.
        <!-- `LinkObservation` + `linkFaults`, 9 kinds.
        THE COUPLING FAILS IN BOTH DIRECTIONS AND BOTH ARE SHIPPED. Resolving the
        discussion must not admit the change — people agree in words all the time
        without anybody pressing the button, and a system that presses it for
        them has recorded an acceptance nobody made. Admitting the change must
        not settle the discussion — the change going in is not an answer to the
        argument about it, and the objection that was open is now filed as
        settled.
        AND THE FAILURE IS NOT ONLY COUPLING. An archived thread with a live
        proposal hanging off it takes a pending change off every surface the
        discussion appears on while it is still waiting for somebody to decide
        it; one control standing for both states leaves a reader unable to tell
        an unanswered question from an unadmitted change, which need different
        people to act.
        S8.2's EIGHTH DECISION REACHES HERE UNCHANGED — two one-way links are two
        one-way links — and the two directions are separate faults because they
        are separate fixes: a reviewer at the change with no way to the argument
        has lost the half they need, and everybody arguing about the change
        without the change in front of them has lost the other.
        A LINK NAMING A THREAD WITH NO STATE IS THE EMPTY-ARGUMENT SHAPE, and the
        test asserts it is the ONLY finding returned: every rule below reads that
        state, so guarded on its presence they would all silently not run and the
        observation would come back clean. A proposal argued NOWHERE is not a
        fault — a one-line fix needs no discussion — and inventing one would make
        every trivial proposal a finding. -->
- [x] S8.4 Add mentions with tenant-scoped identity resolution. Evidence:
      unauthorized identities cannot be discovered.
      <!-- `libs/oshun/workbench-kit/src/mentions.ts` (104 tests, 66 fault
      kinds), subpath `./mentions`. Rollup of S8.4.a-f below; every child is a
      section of the one module, because a picker whose scope rule could not see
      the resolution that happens at submission would be a control on a door
      beside which there is no wall.
      THE HOLE HAS TWO FACES AND THEY ARE ONE HOLE. First: THIS KIT CAN DECIDE
      WHETHER YOU MAY SEE AN OBJECT AND HAS NEVER BEEN ASKED WHETHER YOU MAY SEE
      A PERSON. `ObjectMetadata` carries an owner and a list of assignees and
      `ENTITY_KINDS` lists twelve things a row can be, none of which is a human
      being — people appear in this kit only as attributes of things. So every
      visibility decision ever written here has the shape "given this object, may
      this actor read it", and a mention search asks the one question that shape
      cannot hold, which is who is there. THAT IS WHY THE ITEM'S EVIDENCE CLAUSE
      SAYS DISCOVERED RATHER THAN READ: the harm is enumeration, and an object
      model answers reads. `authorization.ts` gets halfway, since `conceal` and
      `presence-is-the-disclosure` already exist so that a 403 does not appear
      where a 404 belongs — but A CONCEALMENT PROTECTS A NAMED SUBJECT AND A
      SEARCH HAS NO NAMED SUBJECT. Nobody asks whether they may see one person;
      they type three letters and receive a set, and what leaks is not a refusal
      but the difference between two answers that both succeeded.
      Second: A MENTION IS THE FIRST IDENTITY REFERENCE IN THIS KIT THAT LIVES IN
      CONTENT. Every identity mechanism here operates on FIELDS — `ActorRef` is a
      column on a revision, `parseActorRef` refuses display data by name so
      identity cannot drift into a display string, and `deletion.ts`'s
      `residueIn` hunts survivors by walking `Object.entries(record)`. A mention
      is a substring in a body, so the sweep that redacts every actor field on
      every row will not touch it, and after an erasure the thread still says the
      name in prose. The `redacted` `ActorRef` variant — which exists so history
      can still say a mutation had an actor — has no counterpart anywhere in
      content, and `identityResidue` is that counterpart.
      COMPOSE, DO NOT RE-DECIDE (tenth sighting of the S7.12 rule): S4.3 owns
      `conceal` versus `deny`, S3.2 owns the tenant and workspace scoping, S4.2
      owns the principal mapping and the rule that the kit holds no roster, S8.2
      owns the thread a naming lands in and the finding that a context snapshot
      outlives its access control, S7.19 owns the notification taxonomy and keeps
      `mention` among the kinds a storm collapse may never swallow, S7.13 owns
      withdrawal, and `identity.ts` owns what an id may not be made of.
      Verification: 14 SOURCE INJECTIONS, each caught, against a control arm that
      passes; `npx tsc -p tsconfig.lib.json` and `-p tsconfig.spec.json` clean;
      the full kit suite green at 127 files / 7492 tests; export map 0 violations
      and 131 entry points; API reference 21 -> 22 pages at the unchanged
      73-violation baseline; packed-consumer smoke and router-vocabulary scan
      both OK; stub-indicator scan and NUL scan clean. -->
  - [x] S8.4.a Define mention token/reference independent from display name and
        support human/team/role/on-call aliases as policy permits. Evidence:
        mention schema.
        <!-- `MentionReference` / `MentionOccurrence` / `mentionSchemaFaults`,
        12 fault kinds. THE FOUR ALIAS KINDS DIFFER IN WHAT THEIR RESOLUTION IS A
        FUNCTION OF, and `RESOLUTION_DEPENDS_ON` writes the axis down rather than
        leaving it in four branches: nothing, membership, the object, the
        instant. A human names a person. A team names a set that changes. A role
        names the holders OF THIS OBJECT, so the same token in two threads names
        different people. And on-call names a rota position whose entire purpose
        is that it moves — re-derived in August, March's "@on-call" names
        somebody who has never seen the thread, and a reader chasing why nobody
        answered asks the wrong person. So every group kind PINS the set it was
        derived over with the instant it was derived at, and the pin is the
        population while the delivery is what happened to each of them.
        "Independent from display name" is enforced by shape rather than by
        assertion: a reference that IS the typed text, or that carries an
        address, a handle or a written-out name, is the `parseActorRef` argument
        arriving where identity lives in content — a name changes and a handle is
        reassigned, and either used as identity makes every mention of somebody a
        mention of nobody the day they marry. `resolvableKinds` is separate from
        `permittedKinds` for the reason S8.2 needed `unsupported`: "policy forbids
        it" and "this build cannot do it" are different facts and a reader told
        the wrong one debugs the wrong system. -->
  - [x] S8.4.b Search only identities visible to actor in current tenant/
        workspace/object and authorized mention scope. Evidence: identity search
        negative tests.
        <!-- `isDiscoverable` / `searchFaults` / `DirectoryEntry`, 7 fault kinds.
        The predicate is ONE named function because S8.4.d needs the same one at
        submission and the two paths must not be able to drift; everything else
        in the module calls it. The negative tests cover the five ways a served
        row should not have been there plus the two nobody writes: the SEARCHER'S
        own workspace (the candidate belongs to the partition and the person
        asking does not, so the picker is answering for a partition they are
        outside — one of the fourteen injections, and the suite catches it), and
        serving anybody at all to a searcher who cannot read the target, since a
        mention picker is a directory query wearing a document's clothes and the
        document is the only thing that scoped it. A CANDIDATE WHO CANNOT READ
        THE TARGET IS A POLICY DECISION RATHER THAN A BUG, because mentioning
        somebody into a thread is how they get pulled into it — but where the
        deployment does not permit it, offering them makes the picker a
        membership oracle for every object in the tenant. Suspended is
        deliberately NOT hidden: they are still here, they still get named, and
        it is the delivery that waits. -->
  - [x] S8.4.c Prevent enumeration through query timing, result counts, avatars,
        errors, autocomplete, copied markup, or guessed stable IDs. Evidence:
        discovery abuse tests.
        <!-- `searchIdentities` / `responseDifferences` / `probeForOracle` /
        `discoveryFaults` / `AvatarSession`, 12 fault kinds. THE SEVEN CHANNELS
        DIVIDE IN TWO AND THE DIVISION IS THE HONEST PART. Four are IN THE
        RESPONSE, so a real function can be held to them — and the property is
        not "the hidden rows are withheld", which is true of every implementation
        with the bug, but that THE RESPONSE IS A FUNCTION ONLY OF THE VISIBLE SET.
        The suite proves it by running the real search over two directories that
        differ by five people the actor may not see and asserting the responses
        are identical, then by probing a hidden colleague against somebody who
        does not exist and asserting no channel separates them. AN ORACLE DOES
        NOT NEED A POSITIVE ANSWER: two queries that both return nothing are
        still a directory if one of them says so differently. Three channels are
        properties of the SYSTEM AROUND the response and no returned value can
        measure a database's plan or intercept a paste, so those are declared and
        the declaration is checked — and a filter applied AFTER retrieval is a
        timing oracle whatever the milliseconds say, because the work done before
        the answer is proportional to what the actor may not see. AVATARS ARE
        ISSUED, NOT DERIVED: a url that is a function of the subject id is a
        directory lookup with an image content type, so an attacker cannot
        construct the avatar of somebody they have not been shown; the cost is
        that a cross-session cache needs exactly the function that is the oracle,
        and it is taken deliberately. The prefix minimum is recorded as a COST
        rather than a control, and claiming it as one is its own fault kind,
        because it is the reason nobody builds the control that would work. -->
  - [x] S8.4.d Resolve at submission, validate current eligibility, and preserve
        redacted historical reference if identity later changes/leaves.
        Evidence: lifecycle tests.
        <!-- `resolveMention` / `renderMention` / `identityResidue` /
        `lifecycleFaults`, 11 fault kinds. THE PICKER IS NOT THE ONLY PRODUCER,
        SO THE SEARCH RULE IS NOT THE CONTROL: a mention token is TEXT and
        arrives by paste, by quotation, by typed markup, by an API client that
        never opened a picker, which is the only reading of "resolve at
        submission" that makes it a security property rather than a caching
        decision. AN UNAUTHORIZED REFERENCE THEREFORE FAILS EXACTLY AS A TYPO
        FAILS — `ActorId` segments are lowercase alphanumerics with hyphens, so
        `actor:ama-mensah` is a name away from being guessed, and the defence
        cannot be that ids are hard to guess but that HOLDING ONE CONFERS
        NOTHING. The test asserts deep equality across a typo, a guessed id of a
        hidden colleague, a guessed id from another workspace, a reference pasted
        from another tenant, an erased identity and nothing claimed at all: six
        failures that differ by a field are six answers. The three refusals that
        ARE distinguishable are facts about the deployment rather than about a
        person. AND `redacted` CANNOT CARRY A DEPARTURE: its three causes are
        `erasure-request`, `retention-expiry` and `legal-hold`, every one of them
        a statement that DATA WAS REMOVED, and somebody leaving removes nothing —
        rendering a departed colleague as redacted tells every reader the record
        was destroyed when it was not, which is a worse lie than the one it was
        avoiding. So a render answers TWO questions with two answers: what the
        text meant when it was written, which does not change, and what this
        reader may learn from it now, which is re-derived every time. For an
        ERASURE the typed text IS the name, so the render is a fixed label and
        `identityResidue` is the content-level counterpart to `residueIn`'s field
        walk. -->
  - [x] S8.4.e Create idempotent notification/activity refs with source context,
        urgency, privacy/redaction, locale, and preference policy. Evidence:
        mention delivery tests.
        <!-- `mentionDeliveryKey` / `planDeliveries` / `sentDeliveryFaults` /
        `acknowledgeToAuthor`, 9 delivery and 5 acknowledgement fault kinds. WHAT
        IS NOT IN THE KEY IS THE DECISION: no offset, so one person named three
        times in a comment is told once; no revision, so a re-save is not a
        second naming; no occurrence id, so removing a mention and adding it back
        does not ring twice. `planDeliveries` takes a COMMENT rather than an
        occurrence for that reason — given one at a time each call sees a clean
        slate and the duplicate rule becomes a property of the caller's iteration
        order. THE SOURCE CONTEXT IS THE PAYLOAD THAT CROSSES THE BOUNDARY, and
        S8.2's finding that a context snapshot outlives its access control
        arrives here on the other axis: a mention of somebody who cannot read the
        thread is an ACCESS REQUEST, and "Ama named you: '...the settlement figure
        is 4.2m...'" hands them the document through the notification system,
        which has no idea it did. So the naming travels and the excerpt does not,
        as two dispositions rather than one boolean, and the withholding is
        `conceal` rather than `deny` because a notice saying "there is an excerpt
        you may not see" has disclosed the excerpt. THE ACKNOWLEDGEMENT TO THE
        AUTHOR IS ABOUT THE RECORD AND NEVER ABOUT THE DELIVERY: every honest
        sentence about a delivery is a fact about the recipient, and "3 of 12
        could not be reached" measures a team roster the author can already see
        against an access control list they cannot — repeat it with a second team
        and the list is enumerated with no refusal anywhere. The audience note is
        a CONSTANT, because the moment it takes a parameter somebody puts a count
        in it. What the author IS told is what resolved and what did not, both
        facts about their own text. `two-deliveries-under-one-key` is checked
        over the OUTBOX rather than over the planner's output, because the
        planner cannot emit it and a fault kind only its own producer can never
        produce is a hole with a name — a retry or a second worker is where the
        invariant is worth having. -->
  - [x] S8.4.f Handle self, duplicate, nested quote, edit-add/remove, deleted
        content, suspended user, and inaccessible target. Evidence: edge-case
        tests.
        <!-- `classifyOccurrence` / `occurrenceFaults` / `OCCURRENCE_RULE_ORDER`,
        6 dispositions, 13 reasons and 10 fault kinds. ONE FUNCTION RATHER THAN
        EIGHT, because several of the cases are true at once in the situations
        that actually go wrong — a suspended colleague named twice inside a
        quotation of a deleted comment is what a thread looks like after a week —
        and eight independent checks agreeing is eight chances for two of them to
        disagree. A MENTION INSIDE A QUOTATION IS A REPLAY AND NOT A NAMING:
        identical token, different act, and nothing in the token can say which,
        so resolution is POSITIONAL and quoted regions are a structural fact the
        host declares. AN EDIT CANNOT UN-SEND, so a removed naming is RETRACTED
        rather than deleted (S7.13 arriving a fourth time) or somebody who read
        the notification goes looking for why they were named and finds a thread
        that never names them. `hold` and `record-only` are the pair that must
        not merge — not now against never — or a suspended colleague's mentions
        are lost forever or every self-mention re-sends on the next sweep.
        THE PRECEDENCE TEST HAD TO BE MADE CUMULATIVE TO BE A TEST AT ALL: with
        thirteen contexts each carrying one wrong fact, every rule fires
        correctly in isolation and the suite passes against ANY ordering, which
        the injection that moved the duplicate check above the quotation check
        proved by escaping. The descent now starts with every condition true at
        once and turns off only the one above at each step, so a rule that moves
        up is caught by the step whose answer it steals. `occurrenceFaults`
        compares a host's actual handling against the rule in ONE direction,
        ranked by `DISPOSITION_PERMISSIVENESS`: a deployment that held something
        this module would have sent has been careful and its users will tell it;
        one that sent something this module would have held has already
        published, and nobody tells it anything. -->
  <!-- S8.4 marked after reading `src/mentions.ts` and `src/mentions.spec.ts` in
  full in this session: 104 tests, every one of the 66 declared fault kinds
  proved reachable by the test that runs last, 14 source injections each caught
  against a control arm that passes, and the whole kit suite green. -->

- [x] S8.5 Add assignments, due dates, escalation, reassignment, and workload
      views. Evidence: state-machine tests.
      <!-- `src/assignments.ts` + `src/assignments.spec.ts`, 159 tests, 95 fault
      kinds, every one proved reachable by the test that runs last.
      THE VEIN: AN ASSIGNMENT IN THIS KIT IS A COLUMN ON THE THING BEING WORKED
      ON, AND S8.5 ASKS FOR A THING OF ITS OWN. `indexes.ts` declares the
      physical model and `workbench_entity` carries the whole of assignment in
      four columns — `assigneeId` nullable, `reviewerId` nullable, `dueAt`
      nullable and `openState` with a declared `distinctValues: 2` against this
      item's TEN named states — while no `assignment` entity appears anywhere in
      the statistics. So an assignment has no id, no assigner, no reason, no
      priority, no SLA class and NO HISTORY, and `UPDATE workbench_entity SET
      assigneeId = 'kofi'` does not end the previous assignment, it makes it
      never have existed. S8.5.a asks first for identity and S8.5.e asks for
      preserved history: a state machine over a column overwrite is a state
      machine with one state. THE MODEL ALREADY ADMITTED ITS CARDINALITY WAS
      WRONG — `reviewerId` is a SECOND singular column added because a second
      person needed to hold one object at one time in a different role, an error
      paid for one role at a time, and there is no third column for the person
      who has been OFFERED it. THE GREP: take an item that asks for a NOUN and
      look for a TABLE with that name in the storage model; where the concept
      exists only as columns on somebody else's row it has no identity, no
      history and no state machine. Corroborating tell: two singular columns for
      one relation in different roles. -->
  - [x] S8.5.a Define assignment identity/type, target/revision, assignee/
        eligible pool, assigner, role, priority, state, reason, and timestamps.
        Evidence: assignment schema.
        <!-- `AssignmentRecord` + `assignmentSchemaFaults`, 14 fault kinds.
        THREE DECLARATIONS OF ASSIGNMENT EXIST A LIBRARY APART AND DISAGREE:
        `authorization.ts`'s plural `assignees`, `functions.ts`'s plural
        `assignedTo`, and `indexes.ts`'s one nullable `assigneeId` with a partial
        index defined by IS NULL. THE RESOLUTION IS NOT THAT ONE IS WRONG — they
        answer different questions and nothing ever said so: the plural list is
        who may TOUCH it, the singular column is whose PLATE it is on, and an
        OBSERVER proves they are different by belonging in the first and in
        neither column. Both are derived from one ledger by `derivedAcl` and
        `derivedWorkColumns` and checked in BOTH DIRECTIONS by
        `projectionFaults`, because they fail differently and both silently: a
        column ahead of the ledger authorises somebody no assignment supports, a
        ledger ahead of the column leaves the person doing the work locked out
        and drops the row into `wbe_team_unassigned`, which is partial on
        assigneeId IS NULL, so it appears in the UNCLAIMED queue while somebody
        is already doing it. `everAssigned` is the producer `functions.ts` never
        had: `not-the-assignee` reads `ActorHistory.assignedTo` and gives the
        right reason for reading the object rather than the request, then stops
        one step short, because the object's assignee column is also something
        the actor can have changed — take yourself off the assignment and the
        rule meant to catch you finds an empty list, which is S8.1's laundering
        at the SEAT and S8.3's at the CONTRIBUTION arriving a third time at the
        ASSIGNMENT. Negative control: an offer that was never accepted is NOT a
        past assignment, or a declined offer disqualifies people from work they
        refused. -->
  - [x] S8.5.b Define offered/accepted/active/blocked/completed/declined/
        cancelled/overdue/escalated/reassigned transitions and authorization.
        Evidence: state machine tests.
        <!-- `ASSIGNMENT_STATES` (EIGHT of the item's ten), `TRANSITIONS` (15
        edges), `transitionFaults` (13 kinds), `hostStateModelFaults` (4).
        THREE OF THE TEN ARE ON DIFFERENT AXES AND THE EXTENSION IS THE PROOF:
        if `overdue` is a STATE then extending a deadline has to decide which
        state to go back to, and the record no longer says whether the person had
        accepted it or was blocked on somebody — a state machine with a
        transition that time makes and an extension unmakes is not one. If
        `escalated` is a state then an escalated assignment is not `blocked` and
        the cause has been erased by the response to it. So both live on their
        own axis (`dueStanding`, `EscalationRecord`) and `hostStateModelFaults`
        is how a deployment that put them in its state column finds out — it also
        catches the storage model as declared, a two-valued flag. THE TWO
        ABSENT EDGES ARE THE CONTENT: no `offered -> active`, because work
        cannot start before somebody agrees and an `accepted` state nobody
        accepted is a consent that never happened; and no `blocked ->
        completed`, because finishing out of a block leaves nothing saying
        whether the blocker was overcome or ignored. COMPLETE IS THE ASSIGNEE'S
        AND CANCEL IS THE ASSIGNER'S: an assigner marking work complete is one
        person deciding another person did something, indistinguishable in the
        record from the work having been done — with an EXEMPTION for
        self-assigned work, where there is only one person and the rule is about
        the second. An offer LAPSING has no actor (S8.3's expiry rule), and the
        sweep job's principal in the field a reader goes to for a person's
        decision is the invented sentence. THE PRECEDENCE DESCENT IS CUMULATIVE
        — every condition wrong at once, one turned off per step — because with
        one wrong fact per case every ordering passes. -->
  - [x] S8.5.c Define due instant, originating time zone, reminder schedule,
        extension, SLA class, pause behavior, and DST handling. Evidence:
        clock-controlled tests.
        <!-- `instantOfWallClock` / `commitDue` / `extendByCalendarDays` /
        `scheduleReminders` / `slaStanding` / `pauseFaults`, 13 clock faults,
        and REAL arithmetic over the platform's own zone database rather than
        declarations about it. A DUE DATE IS A PROMISE MADE IN A PLACE AND
        `Instant` REFUSES TO CARRY ONE: `parseInstant` takes Z only and says
        why, that string order is time order, which is right for a log and wrong
        for a commitment — and the entity's own statistics agree, 900 distinct
        `dueAt` values across 400,000 rows being the signature of dates rather
        than moments. So a commitment stores the WALL CLOCK, the ZONE and the
        INSTANT, and DST proves the three are not redundant. KNOWN VALUES:
        17:00 New York on 2026-03-07 is 22:00Z and on 03-08 is 21:00Z, so "one
        more day" moves the instant TWENTY-THREE HOURS and `+24h` delivers at
        18:00 local — an hour late, on the day the deadline was already tight;
        25 hours over the autumn transition; exactly 24 in Accra, which has no
        summer time. 02:30 on 2026-03-08 denotes NO instant in New York and
        01:30 on 11-01 denotes TWO an hour apart, both reported rather than
        silently resolved. Auckland goes back in April and forward in September,
        which is the control that nothing assumes March means forward. A
        reminder authored as "24 hours before" fires at 16:00 local and one
        authored as "17:00 the day before" fires at 17:00 — different rules, not
        two spellings. A PAUSE IS ON THE CLOCK THAT MEASURES, NOT ON THE PROMISE
        THAT WAS MADE: stopping a measurement is something anybody may do and
        moving a deadline is a renegotiation, and one control for both lets a
        stopwatch rewrite a promise. Genuinely clock-controlled: the module reads
        no ambient clock at all — zero `Date.now()`, every instant an argument. -->
  - [x] S8.5.d Escalate overdue/declined/blocked/unavailable/policy-triggered
        work to configured actor/role/on-call with deduplication. Evidence:
        escalation tests.
        <!-- `ESCALATION_CAUSES` (all five), `EscalationTarget`,
        `escalationKey`, `escalationFaults` (11 kinds). ESCALATION IS AN
        AUTHORIZATION EVENT WITH NO HUMAN IN IT: it creates an assignment, and
        an assignment grants access, so A CLOCK CANNOT GRANT ACCESS and the
        target must be independently eligible — decided by rules that already
        exist rather than by the deadline passing. S8.4's rota lesson arrives
        with a bigger consequence than a misdirected notification, because a
        rota re-derived at read time hands August's on-call the object March's
        escalation opened; the axis is IMPORTED from `RESOLUTION_DEPENDS_ON`
        rather than restated, and the spec asserts the two agree on every kind.
        THE DEDUP KEY IS (assignment, cause, GENERATION) and the third part
        earns its place: an escalation fires, somebody extends the deadline, the
        deadline passes again — that is a second situation, and a key over
        (assignment, cause) alone suppresses it forever. Escalating is asking
        for help and is NOT a renegotiation, so it must not move the deadline;
        an unavailability escalation to an unavailable target has moved work
        from one absence to another; and an escalation that resolves back to the
        person who is stuck reminds somebody who already knows while nobody with
        the authority to help hears anything. -->
  - [x] S8.5.e Reassign with reason, preserved history/comments/work, old/new
        notifications, authority, and due/SLA policy. Evidence: reassignment
        tests.
        <!-- `Reassignment` / `reassignmentFaults` (11 kinds) /
        `handoverNotices` / `handoverNoticeFaults` (2). The predecessor goes
        TERMINAL in `reassigned` and the successor names it, which is what makes
        "preserved history" a row somebody can find rather than a value that was
        overwritten. THE DUE POLICY IS A VALUE AND NOT A NULL: `inherit` keeps a
        promise made outside the team, `reset` starts the clock for somebody
        beginning from nothing, and `renegotiate` says the deadline is not yet
        agreed — which is the honest answer more often than either, and a null
        due date and a due date nobody has agreed are different things to a
        queue. ACCESS THAT OUTLIVED THE WORK is decision 2's consequence nobody
        plans for: on a protected object, taking somebody off the assignment
        locks out the person best placed to explain it, and the answer is not to
        leave them on the list — that grant never expires, because no future
        event is about them — but an explicit handover grant WITH AN END. And
        S8.4's acknowledgement rule arrives where it is easiest to get wrong:
        the reason somebody came off a job is about that person, so
        "reassigned from Ama (falling behind)" is a performance assessment
        delivered to a colleague by a workflow engine. Two audiences, two
        payloads, and the incoming notice has no parameter to put a reason in. -->
  - [x] S8.5.f Build authorization-aware workload counts/queues by assignee,
        role, priority, state, due/SLA, domain, capacity, and locale/time zone.
        Evidence: workload query tests.
        <!-- `WorkloadQuery` / `workloadFaults` (9 kinds) / `countWorkload` /
        `calendarBand` / `weekShapeFor`. THIS ITEM BREAKS A DECLARED
        AUTHORIZATION IN S3.5. `indexes.ts` permits `inbox.assignee-open` to
        drop its workspace predicate and writes down what stands in for it —
        "the assignee predicate is the authorization: the caller reads their own
        assignments and no query shape lets them ask for anybody else's" — and
        S8.5.f IS that query shape, so the moment a manager binds another
        person's id the thing authorizing the widening is gone and the scope has
        to come back. The count is then `mentions.ts`'s count oracle moved from
        people to rows: "Ama has 14 open items" told to a reader who may see 9
        discloses the 5, so the total is taken AFTER the reader's filter and a
        filtered figure says that it is one. Capacity is the derived form of the
        same oracle. LOCALE IS NOT DECORATION — `weekShapeFor` reads real week
        data, and the known value is that ONE Sunday deadline is `this-week` for
        a reader in London and `later` for one in New York, while a Saturday is
        `this-week` in the United States and `later` in Egypt, whose week starts
        on Saturday and whose weekend is Friday and Saturday. Work with no SLA
        class is counted as `unmeasured` rather than `within`, because reporting
        the absence of a promise as the keeping of one is how a queue says
        everything is fine. -->
  - [x] S8.5.g Reconcile assignment state with review/gate/target changes and
        avoid ghost/duplicate work. Evidence: dependency tests.
        <!-- `assignmentStanding` / `reconciliationFaults` (7 kinds) /
        `duplicateWorkFaults`. GHOST WORK IS AN ASSIGNMENT WHOSE SUBJECT MOVED,
        and `approval-workflows.ts` already owns the question one level up:
        `repeatOnChange` says whether a judgement survives an edit, so it is
        COMPOSED rather than re-decided. An assignment has a third answer an
        approval never needs, because an approval is asked about a revision and
        an assignment is asked about a JOB, and a job can be called off — VOID.
        The descent puts the three void causes first, because work nobody wants
        is not work somebody is behind on; a DEPARTURE ORPHANS rather than voids,
        because the obligation was the organisation's and somebody leaving does
        not discharge it, so closing it removes what was actually needed from
        the queue. THE DUPLICATE IS THE FAULT THE COLUMN MODEL COULD NOT
        REPRESENT: with one `assigneeId` per row a second simultaneous
        assignment for one (target, ask, role) is an OVERWRITE rather than a
        collision, so nothing has ever detected it — and it is the ordinary
        result of an escalation that assigned somebody without terminating the
        original. Keyed on the ask because two reviewers reading one document
        for different things are two jobs; one AUTHORITY holding two records is
        bookkeeping, not two people doing one job. -->
  <!-- S8.5 marked after writing and reading `src/assignments.ts` and
  `src/assignments.spec.ts` in full in this session: 159 tests, every one of the
  95 declared fault kinds proved reachable by the test that runs last, 17 source
  injections each caught against a control arm that passes, and the whole kit
  suite green at 7652 tests. -->
- [x] S8.6 Add presence and active-selection awareness with privacy controls.
      Evidence: reconnect and stale-presence cleanup tests.
      <!-- `libs/oshun/workbench-kit/src/collaborator-presence.ts` (136 tests, 58
      fault kinds), subpath `./collaborator-presence`. Rollup of S8.6.a-f below;
      every child is a section of the one module because the six are one
      question — an expiry rule that could not see the publication, or a
      publication that could not see the lease, would be six policies agreeing
      by accident.
      THE HOLE: PRESENCE IS THE FIRST THING IN THIS KIT THAT MUST STOP BEING
      TRUE ON ITS OWN, AND EVERY DURABILITY RULE THE KIT HAS SAYS THAT IS A BUG.
      `indexes.ts` declares EIGHT entities — `workbench_entity`, `gate_run`,
      `waiver_grant`, `entity_taxonomy_link`, `taxonomy_term`,
      `editorial_calendar_slot`, `audit_event`, `recompute_job` — and every one
      is a durable row that exists until something deletes it; before this module a
      grep for `ttl`, `heartbeat` or `ephemeral` across all 124 shared modules
      returned NOTHING.
      The discipline on top of that model is explicit and load-bearing:
      `release-locks.ts` decision 5 is "A LOCK THAT EXPIRES QUIETLY IS NOT A
      LOCK. Expiry is an event with an outcome and an actor, not the absence of
      one", and `audit.ts` decision 1 is that a decision surface with no event
      is "a thing the platform does and does not record". S8.6.d asks for the
      inverse of the first IN ITS OWN TEXT — "without durable online claims" —
      and S8.6.f asks for the inverse of the second.
      THE RESOLUTION IS NOT THAT ONE OF THEM IS WRONG. A DECISION IS DURABLE AND
      ITS END NEEDS AN ACTOR; AN OBSERVATION IS PERISHABLE AND ITS END NEEDS
      ONLY A CLOCK. A lock is something the system CLAIMED on somebody's behalf
      and a claim that evaporates leaves nobody able to say who decided;
      presence claims nothing, so the honest end of a report whose subject
      stopped reporting is that it CEASES. Demanding an actor there is
      `suggestions.ts`'s invented sentence with the volume up — a laptop lid
      closing is not an act of the platform. So the module checks for a record
      CROSSING the line, because presence does not leak as a table marked
      presence: it leaks as a `lastSeenAt` column on `workbench_entity` and as a
      metric labelled by actor id. -->
  - [x] S8.6.a Define ephemeral presence session ID, actor-safe identity,
        workbench/object, permitted sublocation/selection, device/tab, state,
        heartbeat, and expiry. Evidence: presence schema.
        <!-- THREE IDENTITY LEVELS, because RECONNECT AND A SECOND TAB ARE
        DIFFERENT EVENTS AND ONE IDENTIFIER CANNOT TELL THEM APART: key presence
        on the actor and a reconnect looks like a second person, key it on the
        connection and closing one tab looks like leaving while another is open.
        So ACTOR / ENDPOINT (device+tab, survives a reconnect) / CONNECTION
        (ephemeral, does not). WHAT THE CLIENT CLAIMS IS SPLIT FROM WHAT THE
        SERVER CONCLUDES — `REPORTED_ACTIVITIES` against `PRESENCE_STANDINGS` —
        because a single `status` field lets a client declare itself live; every
        instant on the record is server-stamped except `reportedAt`, which is
        labelled and read by nothing. ROOM_ACTOR_KINDS IS NARROWER THAN
        `ActorRef`: a retention sweep reading a document is not a person in the
        room, and an erased identity has no present tense. And a session id
        DERIVED from the actor is a name badge — `release-locks.ts` decision 1
        again, with a sharper consequence, since streams are addressed by
        session id and a viewer reduced to `pseudonymous` de-anonymises the room
        by reading the identifier the transport handed them; the check covers
        plain text, hex and base64url, and the base64 arm is case-SENSITIVE
        because its alphabet carries bits in its case. -->
  - [x] S8.6.b Authorize publish/subscribe by tenant/object/property/protected
        context and minimize precision/content exposed. Evidence: privacy tests.
        <!-- DISCOVERABILITY IS NOT TRACKABILITY, AND THE KIT ONLY HAS THE
        FIRST. S8.4 established that `ENTITY_KINDS` names twelve things a row
        can be and none is a human being, so every authorization here reads
        "given this OBJECT, may this ACTOR read it"; presence points the arrow
        the other way and discloses a fact about a PERSON to an object's
        viewers. `isDiscoverable` answers whether you may FIND somebody and
        NOTHING answers whether you may WATCH them — a directory lookup you are
        entitled to perform is not a trace you are entitled to keep. So a
        publication is the JOIN of two permissions and `exposureFor` refuses to
        derive the second from the first; the one-way implication (not
        discoverable => never named) is asserted in the spec against the REAL
        `isDiscoverable` over a 32-case cross-product, and the reverse is shown
        NOT to hold. PRESENCE IS ALSO THE FIRST SIGNAL GENERATED BY LOOKING, and
        looking is the one act that has never disclosed itself: a comment or an
        assignment is a deliberate act its author made public, while
        `authorization.ts` already carries `support-may-not-see-presence` and an
        auditor, a legal-hold reviewer and an approver checking a conflict are
        the same shape. THAT IS WHY THE LADDER HAS FOUR RUNGS AND NOT THREE —
        `counted` still says somebody is here, and "and 1 other" told to the
        subject of an investigation is the disclosure, so an observer
        contributes nothing, not even a change in a number. A SELECTION IS
        CONTENT: a range is a block id and two offsets, `end` states the block
        is at least that long, and an EMPTY RANGE IS A CARET, which is not where
        somebody is reading but where they are about to type — so precision
        DEGRADES down a ladder per viewer rather than vanishing, and the ceiling
        is a function of the reader, applied on the way out. -->
  - [x] S8.6.c Handle multiple tabs/devices, reconnect/session replacement,
        network partition, browser sleep, logout, permission loss, and object
        deletion. Evidence: lifecycle tests.
        <!-- AN AUTHORIZATION DECIDED ONCE AND NEVER RE-DECIDED IS A
        SUBSCRIPTION: every check in this kit runs at request time and a
        presence stream is a request that stays open for an afternoon, so
        "logout, permission loss, object deletion" is a list of things that must
        reach INSIDE an authorization that already returned allow. The roster
        counts distinct PEOPLE, because "3 people are here" over three of one
        person's tabs is a false statement about a room, and the fault that
        catches a reducer keyed on the ACTOR cannot be seen by comparing rosters
        — after such a reducer runs the person is correctly absent from both
        sides — so it compares the sibling connections by id. A LATE HEARTBEAT
        DOES NOT REVIVE AN EXPIRED CONNECTION (found by chasing an unread field
        on the observation): a socket unreachable for longer than its lease has
        been reported gone to everybody, and extending it when the packet lands
        makes somebody reappear who was announced as having left. AND A
        DEPARTURE DISCLOSES IN BOTH DIRECTIONS: identical to the ROOM for every
        cause, asserted exhaustively over the union rather than by eye, because
        a channel distinguishing "closed the tab" from "lost access" from "left
        the company" is an HR feed; and DISTINCT to the SUBJECT, because
        `actors.ts` decision 2 already settled that a rejection says which kind
        and "you were disconnected" and "you may no longer open this" need
        opposite responses from the person they happened to. -->
  - [x] S8.6.d Expire stale presence by server clock/lease and reconcile missed
        disconnects without durable online claims. Evidence: cleanup tests.
        <!-- THE VEIN, MADE CHECKABLE. A HEARTBEAT IS THE CHECKED PARTY
        SUPPLYING ITS OWN LIVENESS (S8.1's rule, in its most direct form): a
        client that supplies the TIME of its own heartbeat holds a room open
        forever with a clock set forward and places itself in a document at an
        hour it was not there — so the server stamps, and the spec asserts the
        standing does not move as `reportedAt` sweeps a decade in both
        directions. The lease is read from the SESSION, not from the policy in
        force today, so a policy change cannot retroactively expire a connection
        granted something longer; and a lease shorter than TWO heartbeats is a
        fault, because one dropped packet then empties the room, which on a
        train is every few minutes. `ExpiryReconciliation` HAS NOWHERE TO WRITE
        A DEPARTURE INSTANT OR AN ACTOR and the spec asserts that on the field
        list: a sweep knows ONE thing, the last instant a heartbeat arrived, and
        "Ama left at 16:42" is a fabrication built from it — she left at some
        point in the lease window and the sweep observed its own timer.
        `DeclaredExpiryRecord` is the shape a host produces when it models this
        the way the kit models the end of a lock, and every field it fills in is
        a fault. -->
  - [x] S8.6.e Render collaborator presence/selection with accessible identity
        and noncolor cues without obscuring authoring content. Evidence: UI
        accessibility tests.
        <!-- COMPOSED FROM `signals.ts` RATHER THAN RE-DECIDED, and headless,
        because what the clause actually names is measurable without a browser. COLOUR IS THE ONLY CHANNEL BY WHICH
        COLLABORATOR IDENTITY IS CONVENTIONALLY CARRIED, which is precisely the
        S7.22.e failure, so the comparison set for a cue is the OTHER PEOPLE IN
        THE ROOM — a vocabulary whose members arrive at runtime — and cues that
        all land on one reader are reported too, since an assistive name is
        nothing on a greyscale screen and a glyph is nothing in the
        accessibility tree. "WITHOUT OBSCURING AUTHORING CONTENT" IS A CONTRAST
        MEASUREMENT AND THE OBVIOUS WAY TO TAKE IT IS WRONG: a translucent
        presence wash sits BETWEEN the ink and the page, so the number that
        reaches the reader is the composite, and an audit measuring ink against
        the page passes while the selected paragraph is the one nobody can read.
        Known values from the WCAG definition computed independently of
        `signals.ts`: black on white is 21:1, and blue at 85% drops it to
        2.8078:1. Also the badge drawn over the insertion point, and a live
        region wired to the heartbeat, which interrupts a screen reader every
        few seconds and makes the document unusable. -->
  - [x] S8.6.f Keep presence out of immutable authored state/audit by default
        while emitting bounded operational telemetry. Evidence: persistence/
        telemetry tests.
        <!-- THE TELEMETRY IS THE BACK DOOR THROUGH WHICH THE EPHEMERAL BECOMES
        DURABLE, and it is the half of this item that a persistence check alone
        would miss. The durable surfaces are named individually because they are
        reached by different mistakes — authored state by a convenience column
        (`workbench_entity.lastViewedBy`, added to save a join), the audit by
        `uncoveredSurfaces` pulling presence in as a decision surface, the
        search index by a denormalising writer, the backup by being in any of
        them. Then the word doing the work in "bounded operational telemetry" is
        BOUNDED: a counter labelled by actor id is a durable online claim
        wearing a metrics costume, kept by the time series store long after the
        lease expired, and it answers "when was Ama at work" better than the
        table we declined to write. So labels identifying a PERSON, a SESSION or
        a single OBJECT are refused OUTRIGHT rather than budgeted — the spec
        proves a per-person metric sits well inside the series budget and is
        still refused — and everything else is bounded by a cardinality computed
        as the product of the declared label domains, plus a retention window
        that answers "is the socket layer healthy" and not "who was working late
        in March". -->
        <!-- S8.6 marked after writing and reading `src/collaborator-presence.ts`
        and `src/collaborator-presence.spec.ts` in full in this session: 136
        tests, every one of the 58 declared fault kinds proved reachable by the
        test that runs last, 25 source injections each caught against a control
        arm that passes, and the whole kit suite green at 7789 tests. -->

- [x] S8.7 Add notification routing by event, role, urgency, locale, channel,
      quiet hours, and frequency caps. Evidence: known-value routing suite.
      <!-- `libs/oshun/workbench-kit/src/notification-routing.ts` (138 tests, 98
      fault kinds), subpath `./notification-routing`. Rollup of S8.7.a-g below;
      every child is a section of the one module because the seven are one
      question — an audience that could not see the preferences, or a preference
      that could not see the transport, would be seven policies agreeing by
      accident.
      THE HOLE: THERE IS A READER AND THERE IS A SENDER AND NOTHING TURNS AN
      EVENT INTO A RECIPIENT. S7.19 (`notification-center.ts`) is the reader, and
      its header names what it does not do — "It does not deliver a notification,
      hold a device token, evaluate a quiet-hours window against a clock, or
      decide what a category means. `libs/veritas/notifications` owns all of
      that." That library is real and it is not small: roughly seven thousand
      lines of FCM, APNs and Web Push; SendGrid, SES and SMTP; Twilio, Africa's
      Talking and Hubtel; Twilio and Meta WhatsApp. Its entry point is
      `send(request: NotificationRequest)`, and `NotificationRequest` carries
      `userId: string` — ONE person — beside `channels` already chosen, `title`
      and `body` already rendered, and `category` and `urgency` already decided.
      Every question this item asks is answered BEFORE that call is made.
      THE CENSUS CLOSES THE GAP: nothing in the monorepo imports that library.
      `tsconfig.base.json` carried no path mapping for `@veritas/notifications`,
      so nothing could; its `createNotificationService` is called from exactly
      one place, which is its own `service.ts`; and the only references to it
      outside its own tree are three doc comments and two hand-copied fixtures —
      the kit's own spec transcribes its vocabulary under the note "copied as a
      fixture rather than imported". (Every same-named `NotificationService`
      elsewhere in `apps/` belongs to a different domain.) The delivery system
      has no caller, and the step that would produce its argument is this item.
      THE GREP THAT FINDS THIS VEIN: TAKE THE SENTENCE IN A MODULE THAT SAYS WHO
      OWNS THE REST OF THE PROBLEM, AND GO AND COUNT THAT OWNER'S CALLERS. A
      deferral reads as diligence and is only true if somebody makes the call. It
      is S8.2's producer-less judge turned around — not a judge nothing feeds but
      a SENDER NOTHING CALLS, which is harder to see because the sender is the
      part that obviously works. The corroborating tell is one line in the kit's
      own idiom: `mentions.ts` declares `RecipientContext.channel`, documented as
      the weakest urgency wanted "ON THIS CHANNEL", and `planDeliveries` reads
      the threshold, the locale, the standing and the mute and never reads
      `channel`. S8.6's rule — an unused field on an input type is a missing rule
      — and the missing rule is the whole of S8.7.e.
      Twelve decisions, of which the load-bearing ones: the fan-out is where
      authorization happens and it happens ONCE PER PERSON, so the unit is the
      triple (event, recipient, channel); a person reached by four routes is one
      person and FOUR GROUNDS and the strongest wins rather than the first, which
      is the only defect here that leaves both audiences identical; escalation is
      the one ground that reaches OUTSIDE the audience, so its notice is composed
      from classes rather than values; mandatory pierces preference and timing
      and NEVER pierces authorization; quiet hours are a wall clock, `start <= t
      && t < end` is unsatisfiable for every wrapped window, and the release is
      itself a wall clock that may denote no instant; a cap is a cap on
      INTERRUPTION and not on record, and counted over its own suppressions it
      never reopens; fallback must not increase exposure; an unverified address
      is not a weak address, it is somebody else's; a bounce is evidence about
      the ADDRESS and a failure about the PROVIDER and only one may be retried;
      and in one state machine `acknowledged` needs an actor while `expired`
      needs only a clock — S8.6's boundary with both halves side by side.
      Verified: 44 SOURCE INJECTIONS, all caught, against a control arm that
      passes. The first 36 found FOUR places where the spec asserted a constant
      AGAINST ITSELF (looping over `DISPOSITIONS_THAT_STOP_A_NOTICE` rather than
      naming its members, `TERMINAL_DELIVERY_STATES.every(isTerminalDelivery)`,
      and two more) — removing `conceal` from the authorization gate and
      `bounced` from the terminal states both passed. Every ordered or
      meaning-bearing vocabulary is now written out in the spec rather than read
      back from the source, and the last 8 injections delete or reorder those
      lists to prove it. The per-fault reachability test
      forced two fixes in the module itself: `mandatory-notice-silenced-by-a-
      preference` could never fire because the floor always won the comparison it
      was checking, and `quiet-window-evaluated-as-a-simple-range` compared the
      module against its own answer. Writing the spec found two more: the
      escalation renderer demanded two different strings for one notice, and
      `planNotifications` reported `required-notice-muted-rather-than-moved` and
      then obeyed the mute anyway — the constant encoding that rule,
      `SILENCEABLE_OBLIGATIONS`, had been declared and never used.
      Clock known values computed from UTC offsets independently of both
      `assignments.ts` and this module: a 22:00-07:00 New York window is 480
      minutes the night the clocks go forward and 600 the night they go back,
      never 540; a release at 02:30 on 2026-03-08 is `skipped` and resolves to
      2026-03-08T07:30:00Z; Auckland goes BACK on 2026-04-05, so the same 02:30
      is `repeated` there — the control that nothing assumes March or forward. -->
  - [x] S8.7.a Register notification event types with source, recipients/
        audience rules, default urgency, required/optional delivery,
        sensitivity, template, dedupe key, and expiry. Evidence: event registry.
        <!-- `EventRegistration` + `eventRegistryFaults`, 16 fault kinds. Three
        checks are not schema: a dedupe key built from the RENDERED summary
        rather than the event's fields silently un-dedupes everything in flight
        when somebody edits a template; an escalation window past the event's own
        expiry means the unanswered case is the one nobody is ever told about;
        and a `scheduled` event whose template has an `{actor}` slot fills it
        with whichever sweep job ran, which is S8.3's invented sentence. -->
  - [x] S8.7.b Resolve explicit user, assignee, mention, owner, reviewer, role/
        team, escalation, and delegated recipients at event time with tenancy/
        authorization. Evidence: recipient tests.
        <!-- All nine grounds the item names, each carrying its own
        `ResolutionDependency` — S8.4's axis reused rather than a second one
        invented. `foldGrounds` is the one place the fold happens and takes the
        strongest obligation AND the strongest refusal (`OUTCOME_PRECEDENCE`);
        `resolutionParityFaults` compares the derivation with a host's claim in
        BOTH directions, because a weakened obligation leaves both lists
        identical. Tested with the grounds in both orders. -->
  - [x] S8.7.c Compose event/category, urgency, domain/project, role, user,
        mandatory notice, and channel preferences with explicit precedence.
        Evidence: preference truth tables.
        <!-- The finding here is that a precedence over ONE list conflates two
        axes — WHO SET IT and HOW SPECIFIC IT IS — and both single-axis answers
        are wrong: ordering by specificity makes a person's own choice advisory,
        ordering by authorship makes a narrow control unsettable. So
        `comparePrecedence` is authority then scope. The truth table is
        CUMULATIVE: eleven settings present at once, one removed per step, so
        every step still has all the weaker ones under it. -->
  - [x] S8.7.d Render localized templates using recipient locale/time zone,
        accessible/plain alternatives, safe redaction, and target link.
        Evidence: locale/security fixtures.
        <!-- Redaction composes S7.19's `renderSummary` rather than substituting
        again, because its own doc comment says it is the ONE place substitution
        happens. Three leaks that are not the summary: the PLAIN part (an email's
        text alternative is where redaction is forgotten, and it is what a screen
        reader reads), the LINK (`/doc/Q3-payroll-restructure` beside a body that
        withheld the title), and the transport itself — `EXPOSURE_CEILING` maps
        all nine `protection.ts` classes, asserted over the full cross-product
        against an independently transcribed table. -->
  - [x] S8.7.e Route in-app, email, push, SMS, webhook, or other registered
        channel through capability/consent/verification and fallback policy.
        Evidence: channel tests.
        <!-- Capability, consent and verification are three different false
        answers with three different remedies and are refused separately. AN
        UNVERIFIED ADDRESS IS NOT A WEAK ADDRESS, IT IS SOMEBODY ELSE'S: a typo'd
        mailbox does not bounce, it succeeds, at a stranger. Holding a phone
        number is not consent, which is gated by EXPOSURE rather than by channel
        name. Fallback is an ordered bounded walk, not a search, so it cannot
        answer a push outage with SMS; and a suspended account — often a security
        response — is reachable in-product and nowhere else. -->
  - [x] S8.7.f Apply quiet hours/time zone/DST, urgency bypass, digest
        eligibility, frequency caps, and mandatory safety/security exceptions.
        Evidence: clock/routing tests.
        <!-- Reuses `assignments.ts`'s zone toolkit rather than a second clock.
        The wrap and the skipped release are both silent failures: the first
        makes the window unsatisfiable so nothing is ever quiet and nothing is
        logged, the second holds a batch until a moment that never arrives. The
        cap counts DELIVERIES only — counted over its own suppressions the
        ceiling is self-sustaining and the symptom is the absence of
        notifications — and the in-product centre is never capped, because what a
        cap rations is the interruption and not the record. -->
  - [x] S8.7.g Persist planned/sent/delivered/failed/bounced/suppressed/deduped/
        expired/acknowledged state and safe retry. Evidence: delivery lifecycle.
        <!-- Exactly the nine states the item names, with `TRANSITION_AUTHORITIES`
        deciding what each end needs: `acknowledged` is the only state the
        RECIPIENT authors (and opening the centre is not reading — S7.19 decision
        6), `expired` has no actor and no reason. `failed` returns to `sent` and
        `bounced` does not, and no edge departs a terminal state at all. The key
        is the EVENT's (S3.6 decision 1), so replanning is byte-identical and an
        at-least-once dispatcher is safe. -->
        <!-- S8.7 marked after writing and reading
        `src/notification-routing.ts` and its spec in this session: 138 tests,
        98 fault kinds all proven reachable, 44/44 source injections caught
        against a passing control, export-map violations 0, API pages 24 -> 25,
        full kit suite 7924 tests green. -->
- [x] S8.8 Add digest coalescing and deduplication. Evidence: event storms do
      not produce notification storms.
      <!-- `libs/oshun/workbench-kit/src/notification-digests.ts` (101 tests, 91
      fault kinds + 7 admission refusals), subpath `./notification-digests`.
      Rollup of S8.8.a-f below; the six are one module because they are one
      accumulator — a key that could not see the window, or a cap that could not
      see what must stay separate, would be six policies that agree by accident.
      THE HOLE: THERE IS A DIGEST AND THERE IS NOTHING TO PUT IN IT.
      `@oshun/messaging-channels/digest` is real and careful — eight locales, a
      Sunday-17:00 send window computed DST-correctly in each locale's home
      zone, corrections leading a deterministic section order, five items per
      section with an overflow line, and an empty week returning do-not-send
      rather than an email — and it is mounted at `GET /v1/me/digest/preview`.
      Its input arrives through `DigestInputsReader`, whose PRODUCTION
      implementation is `emptyDigestInputsReader`, whose `get` returns `null`
      for every member, always. The file's own first sentence says why: "No
      production producer writes a digest aggregate in this BFF." The only
      reader that ever returns anything is marked test-only. THE GREP: TAKE THE
      INTERFACE A WORKING COMPONENT READS ITS INPUT THROUGH AND GO AND LOOK AT
      WHAT THE PRODUCTION IMPLEMENTATION OF THAT INTERFACE RETURNS. S8.2 found a
      judge nothing produces for, S8.7 a sender nothing calls, and this is a
      CONSUMER WHOSE PRODUCTION FEED IS A CONSTANT NULL — the hardest of the
      three, because the null is deliberate, documented and correct.
      The other end of the gap is in the kit: `applyTiming` returns
      `defer-to-digest`, the only disposition that delays a notice without
      saying until when. `hold-until-release` carries the instant the hold lifts
      and reports `held-past-its-own-expiry`; `defer-to-digest` returns
      `releaseAt: null` and no finding at all, so a deferred notice is
      indistinguishable from a dropped one. That is structural rather than an
      oversight — a HOLD is a property of a NOTICE and a DIGEST is a property of
      a PERSON over an INTERVAL, so no function of one event could have computed
      it, which is why nothing accumulated. -->
  - [x] S8.8.a Define dedupe identity by recipient, event family, target/scope,
        state transition, urgency, channel, and time window. Evidence: key
        contract.
        <!-- `DEDUPE_COMPONENTS` is exactly the seven the item names, and
        `MISSING_COMPONENT_FAULTS` gives each omission its own casualty:
        without the RECIPIENT the first person told is the only person told,
        without the TRANSITION a failure and its recovery collapse and it is the
        SECOND that is suppressed, without a WINDOW the suppression has no
        horizon and a reminder is sent once ever. `deliveryKey` AND THIS KEY
        ANSWER DIFFERENT QUESTIONS AND ONE MUST CARRY THE EVENT ID WHILE THE
        OTHER MUST NOT — the first asks whether this is the same DELIVERY under
        at-least-once dispatch, the second whether this is the same NOTICE when
        the world repeats itself — and a key carrying the id is decorative,
        which is the one everybody writes because the right one is already
        there. The TENANT is in the key even beside the recipient: one person in
        two tenants each holding a `doc:atlas` is a cross-tenant boundary
        crossed as a SILENCE. `dedupeIdentityOf` is what finally reads
        `EventRegistration.dedupeKeyFields`, which S8.7 declared, documented and
        validated and nothing ever built a key from. `dedupeKeyComponents` is
        the inverse, and it exists because at a fixed arity the separator escape
        ALONE is already injective — so injectivity cannot show the escape is
        incomplete and a round trip can. -->
  - [x] S8.8.b Distinguish exact duplicate, update/supersession, aggregateable
        related events, and events that must remain separate. Evidence:
        classification tests.
        <!-- `classifyPair` over the item's four relations, tested first on
        audience, then tenant, then the same-object case, then the
        same-family-different-object case. COLLAPSE AND DEDUPLICATION ARE
        DIFFERENT OPERATIONS AND S7.19 NAMED ONLY THE FIRST: a protected kind
        may be exact-duplicate-suppressed — two rows for one mention is not two
        mentions — and may never be aggregated, so reading
        `NEVER_COLLAPSED_KINDS` as "never touch these" ships the at-least-once
        bug on the seven kinds that matter most. SUPERSESSION IS DECIDED BY THE
        EVENT'S OWN ORDER AND NEVER BY ARRIVAL, because out-of-order arrival is
        normal and deciding on it lets the stale event win; the symptom is a
        digest reporting a failure after the recovery, which somebody acts on.
        The host's own reading arrives as a `RelationClaim` so the check has
        something to disagree with (S8.7's reachability lesson). -->
  - [x] S8.8.c Define digest windows/schedules by user locale/time zone,
        preference, urgency, quiet hours, channel, and maximum delay. Evidence:
        scheduling tests.
        <!-- `nextWindowClose` resolves through `assignments.ts`'s
        `instantOfWallClock` rather than by arithmetic, so a close anchored in
        the spring gap denotes no instant and says so. Known values: 08:00 in
        New York is 13:00Z on 03-07 and 12:00Z on 03-08, so `windowSpanMinutes`
        is 1380 there and 1500 across 11-01 — never 1440. A WEEK IS WORSE:
        `weekShapeFor` makes `ar-EG` start on the Saturday, `en-US` on the
        Sunday and `fr-FR` on the Monday, so one weekly schedule resolves to
        three dates from one instant, and a runtime that cannot say returns null
        rather than guessing. The composition faults are the ones no single rule
        can see: a close inside quiet hours whose HOLD outruns a maximum delay
        the close itself respected, and a hold long enough that two windows fall
        due at the same instant and must merge. -->
  - [x] S8.8.d Aggregate counts, actors, targets, status, earliest/latest,
        failures/blockers, and accessible links without leaking hidden items.
        Evidence: digest content tests.
        <!-- A COUNT IS A DISCLOSURE THAT CARRIES NO FIELD. S7.19's machinery
        governs FIELDS — a slot has a disposition and a sensitivity class — and
        "and 4 others" has neither while still saying how much is happening
        where the reader cannot look, so an inadmissible item leaves the COUNT
        and the remainder is returned for the caller's audit rather than for the
        page. A DIGEST IS THE ONLY THING IN THE SYSTEM THAT PUTS HOURS BETWEEN
        THE DECISION AND THE DELIVERY, so it is the only place authorization can
        go stale while a notice waits, and it is re-asked at composition against
        the audience as it stands now. `composeDigestContent` re-authorizes
        BEFORE it counts, because counting first and filtering the rows
        afterwards is the version that ships — the rows visibly disappear and
        the number visibly does not. A failure or blocker is NAMED rather than
        counted; a target whose own summary withheld what it is called is
        counted rather than named; and a link carrying a credential onto a
        surface nobody authenticated to is refused outright. -->
  - [x] S8.8.e Atomically claim/send digest items with idempotency and handle
        concurrent events, retry, partial channel failure, and late arrivals.
        Evidence: race/fault tests.
        <!-- A DIGEST'S IDEMPOTENCY KEY IS A FUNCTION OF THE WINDOW AND NEVER OF
        ITS MEMBERS: `digestIdempotencyKey(tenant, recipient, channel,
        windowId)`. A key hashed over member ids is stable right up until a late
        arrival lands, at which point it matches no previous send and the whole
        digest goes out again with one extra line in it — and the only way to
        SEE that is to ask the host for its key twice over the same window with
        different contents, which `hostKeyAfterALateArrival` does. A late
        arrival therefore belongs to the NEXT window, which is why there must
        always be one to belong to. The lease, the fencing token and the holder
        are all checked, a claim taken before the close fixes nothing because
        the set it was taken over is not the set that is sent, a partial channel
        failure is a fact about ONE channel (S7.19 decision 11) and a retry that
        reopens the window re-sends the channel that worked, and a window that
        gives up while still holding notices is a drop with a state name in
        front of it. An empty digest and a digest of one are both refused. -->
  - [x] S8.8.f Enforce per-recipient/type/channel caps and
        escalate/drop/summarize according to policy rather than silently discard
        critical events. Evidence: storm load tests.
        <!-- THE DIGEST IS THE BOTTOM OF THE LADDER, SO ITS ONLY LAWFUL OVERFLOW
        IS A SUMMARY. Quiet hours delay rather than suppress (S7.19 decision
        10); a frequency cap displaces into a digest rather than into nothing
        (S8.7 decision 7); there is nothing underneath a digest, so a `drop`
        there is the same data loss reached through the last door left open.
        `escalate` is not a third option at the same level — it is the admission
        gate's answer for something that should never have been digested, and
        used as an overflow it turns the cap into a delay before the storm. AND
        THERE ARE TWO CEILINGS RATHER THAN ONE: a limit on how many digests with
        none on their size is one envelope with four hundred lines in it, and a
        limit on size with none on number mints another every twenty items, so a
        deployment that sets one has capped nothing. The evidence clause is a
        NUMBER: `coalesceDigest` takes 603 arrivals — 500 distinct comments, 100
        at-least-once repeats, and a mention, a blocker and a required
        assignment buried in them — and produces 6 notifications, with all three
        criticals still their own notification, 440 members summarised rather
        than discarded, and `digestCapFaults`'s independently computed bound
        agreeing at 6. -->
      <!-- S8.8 marked after writing and reading `src/notification-digests.ts`
      and its spec in this session: 103 tests, 91 fault kinds and 7 refusals all
      proven reachable by the suite's own sweep, 64/64 source injections caught
      against a passing control arm, export-map violations 0, API pages 25 -> 26,
      full kit suite 8032 tests green. Named `dedupeIdentityKey` and not
      `dedupeKey`, because `envelopes.ts` already exports the latter for the
      TRANSPORT's redelivery key (`subject#sequence`) — decision 2's confusion,
      which the kit's own namespace must not carry. THE ADMISSION GATE also
      closes a hole in
      S8.7: `planNotifications` marks a delivery `defer-to-digest` whenever
      `preference.outcome.decision === 'digest'`, consulting neither the
      obligation nor `registration.digestEligible`, and
      `preferenceParityFaults` — the function that refuses a digest choice on a
      notice above `optional` — is not one the plan calls. `admitToDigest`
      refuses it downstream and returns `send-now`, because declining to DELAY
      something is not a decision to withhold it. -->
- [x] S8.9 Add immutable activity-feed events with safe summaries. Evidence:
      rebuild from audit produces the same ordering.
  - [x] S8.9.a Define activity event ID/version/type, target/revision,
        actor-safe reference, outcome, time, causation/correlation, source
        audit/event, and visibility. Evidence: activity schema.
  - [x] S8.9.b Generate events from immutable audit/domain events rather than UI
        guesses and prevent update/delete outside retention/redaction policy.
        Evidence: source linkage tests.
  - [x] S8.9.c Generate structured localized safe summaries with no raw
        content/secrets/protected identity/learner/biometric/unreleased asset.
        Evidence: canary-data tests.
  - [x] S8.9.d Apply tenant/object/property/audience authorization to events,
        counts, actors, targets, linked refs, and export. Evidence: disclosure
        tests.
  - [x] S8.9.e Order deterministically by authoritative time/sequence and stable
        tie-breaker; handle late/out-of-order/replayed events. Evidence:
        ordering tests.
  - [x] S8.9.f Rebuild from empty feed using audit/event history and compare
        event IDs/order/content/visibility to stored projection. Evidence:
        replay parity test.
      <!-- S8.9 marked after writing and reading `src/activity-feed.ts` and its
      spec in this session: 169 tests, 130 fault kinds and 4 projector refusals
      all proven reachable by the suite's own sweep, 95/95 source injections
      caught against a passing control arm, export-map violations 0, entry
      points 135 -> 136, API pages 26 -> 27, full kit suite 8033 -> 8202 tests
      green. THE HOLE: the activity feed is not a projection of anything. `GET
      /v1/activity` is mounted, authorized, cached, paginated and rendered by a
      668-line component, and every row in it is composed at read time from the
      CURRENT value of a mutable entity — `notebook.updatedAt`,
      `habit.lastKeptAtIso`, `session.lastActivityAt`. Its ids are the ENTITY's
      (`nisaba-notebook-${notebookId}`, and `state.ts` writes
      `notebook.updatedAt = annotation.updatedAt` in place); one row is stamped
      `occurredAt: fallbackTimestamp`, which is `generatedAt`, which is
      `new Date().toISOString()` at the top of the handler; and the sort has no
      tie-breaker, so rows at one instant keep the arrival order of the
      per-domain batches of `Promise.all(authorizedDomains.map(...))` — an array
      derived from the VIEWER'S SCOPES. No table anywhere stores an activity
      event, so S8.9.f had neither of its two operands, which is why this item
      needed a PROJECTOR (`projectActivityFeed`) and not only a comparator. The
      kit-side tell is `audit.ts`, whose `replay`/`parityFaults` look like they
      cover this and are per-stream: a gapless sequence is a total order inside
      one stream and no order at all across the streams a feed merges. Two
      escapes in the injection harness were real gaps in my own spec rather than
      in the module — the arity check in `activityEventIdComponents` is
      load-bearing only for an OVER-LONG id, and the stream component of the
      order key is invisible to any test whose tied rows share a sequence
      number.

      2026-08-26 — THE ROUTE WAS FIXED, and the module is the reason it could
      be. `GET /v1/activity` now carries `timeSource` on every row
      (`recorded` | `current-state` | `observed`), so a reader can tell an
      append-only record from a rendering of a mutable entity from a row with
      no source time at all; sorts through a TOTAL comparator built to
      `compareFeedOrder`'s rule minus the stream sequence, which none of the
      tables behind this route carries; REFUSES a row whose source time will
      not parse instead of stamping it with `generatedAt` and floating it to
      the top; drops `occurredAt: fallbackTimestamp` in favour of an `observed`
      row that sorts BELOW every timed row; carries the derived-from revision
      in the ids of the two rows read off mutable entities
      (`nisaba-notebook-${id}@${updatedAt}`, `metis-tutoring-${id}@${updatedAt}`);
      and reads arete check-ins from `habitCheckIns` — the append-only log the
      same write path was already filling, one row per (habit, user, day),
      which this route had never opened. It had been reading `habit.todayKept`
      and `habit.lastKeptAtIso`, two columns `updateStreak` overwrites, so one
      habit had exactly one check-in row for its whole lifetime: today's.
      8 known-value cases in `src/routes/activity-ordering.spec.ts`. What is
      still open is `projectActivityFeed` itself: adopting `ActivityEvent`
      needs a per-stream sequence, and `v1_arete_check_in`,
      `v1_nyx_logged_observation` and `v1_tara_sitting_completion_event` have
      no such column — that is a migration, not a route change.

      The route's own suite was 5-of-10 RED before this pass and is now 10/10.
      Every one of the five failed on its FIXTURE, not its subject: they named
      `veritas` and `metis`, which `V1_SCOPED_DOMAIN_IDS` deferred to V1.2.
      That deferral is a deliberate single-point cut in
      `libs/oshun/navigation/src/release-scope.ts` — "their libraries,
      contracts, routes, and BFF services all stay in the tree" — so the
      route's veritas and metis branches are RETAINED CODE, not dead code, and
      lifting the deferral is one edit there. Fixtures moved onto V1.0 rooms
      the way `assistant-session-store.spec.ts` already records doing; the
      partial-failure test now fails `nyx` instead of `veritas`; and the metis
      test became a lock on the cut itself — it still seeds a real ingest job
      and now asserts the job reaches no feed and a deferred-only grant is
      refused. A DEFERRED ROOM'S STATUS IS ABSENT, NOT `forbidden`: out of
      release scope is a different claim from withheld from this member. -->

- [x] S8.10 Add review inbox and saved queues. Evidence: server-side filters,
      assignment, SLA, and pagination are tested.
  - [x] S8.10.a Define inbox item projection for review/assignment/gate/signoff/
        escalation target, revision, role, priority, state, due/SLA, freshness,
        blockers, and safe summary. Evidence: projection schema.
  - [x] S8.10.b Resolve eligible items from current assignment/role/delegation/
        quorum/stage/authorization and remove stale or completed work. Evidence:
        eligibility tests.
  - [x] S8.10.c Implement server-side full-text/structured filters for domain,
        workbench, type, state, stage, assignee/role, priority, due/SLA,
        freshness, gate, and target. Evidence: query tests.
  - [x] S8.10.d Implement deterministic sort/cursor pagination and live count/
        status reconciliation under concurrent assignment or decision changes.
        Evidence: pagination tests.
  - [x] S8.10.e Persist private/team saved queues with query, columns, grouping,
        sort, density, sharing, schema migration, and authorization. Evidence:
        saved queue tests.
  - [x] S8.10.f Support accept/start/block/complete/decline/reassign/escalate/
        bulk actions with expected revision, impact, audit, and partial result.
        Evidence: inbox action tests.
  - [x] S8.10.g Provide keyboard/screen-reader/virtualization/deep-link and
        narrow review experience. Evidence: accessible journey.
      <!-- S8.10 marked after writing and reading `src/review-inbox.ts` and its
      spec in this session: 221 tests, 84 fault kinds plus the admission,
      action, saved-queue and cursor refusal vocabularies and all five
      eligibility grounds proven reachable by the suite's own sweep, 61/61
      source injections caught against a passing control arm, export-map
      violations 0, entry points 136 -> 137, API pages 27 -> 28, full kit suite
      8207 -> 8428 tests green. THE HOLE: the narrowing happens where the rows
      are DRAWN and not where they LIVE. `AdminWorkspaceStateStore
      .getInboxWorkspace` folds six per-workspace queues into one array, ships
      the whole thing, and says so in its own comment — "Filtering / sorting
      happen client-side (the contract owns the predicates)" — which
      `UnifiedInboxPanel.tsx` repeats: "The panel is fully client-side: filters
      and sort live in local state". `matchesAdminInboxFilter` and
      `sortAdminInboxItems` in `@oshun/contracts` are correct, tested, and on
      the wrong side of the wire, so every row the filter removes has already
      crossed it — including the ones a free-text search reaches by
      concatenating `title` and `subtitle`, where subtitle is a customer display
      name for support and a region for a privacy request. Four faults are
      visible in that same source: the count is `items.length` over an array
      that still holds every terminal row (`buildInboxItemFromReview` computes
      `terminal` only to set `slaStatus: 'paused'` and pushes anyway), so "open
      items" counts finished work; `deriveInboxSlaStatus` returns `no_sla` both
      for a surface with no clock and for a due date that failed to parse, so a
      breach and a promise nobody made are one value; `normalizeInboxPriority
      (null)` puts every model row in a band called `unknown` that
      `PRIORITY_RANK` then ORDERS; and `sortAdminInboxItems` ends at
      `Date.parse(b.lastUpdatedAt) - Date.parse(a.lastUpdatedAt)` with no unique
      backstop, which is both not total — so `indexes.ts`'s own rule says it
      cannot be paged over, and it is not — and NaN for an unparseable time,
      which every JavaScript sort reads as equal. Saved queues repeat the shape
      one level up: `saved-views-store.ts` stores `isShared` and lists only
      `users.get(input.userId)`, so a queue marked shared is visible to exactly
      one person and wears a badge saying otherwise, and `importState`
      rehydrates a saved filter verbatim with no version and no migration. THE
      GREP: the parent's evidence clause begins with the words "server-side" —
      take the surface it names and go and look at which side of the wire its
      predicates run on. Three faults I declared were unreachable until the
      registry sweep found them: `count-includes-what-the-list-excludes` had no
      producer, the four `cursor-not-bound-to-*` kinds had none either, and
      `off-axis-segment-moves-with-the-direction` was UNSATISFIABLE BY
      CONSTRUCTION because it asked `orderInbox` whether it agreed with itself.
      One injection escape was a real point about the module rather than the
      spec: the grounds were collected in the same order as their strengths, so
      the sort that decides between them was dead code that looked
      load-bearing — collecting cheapest-lookup-first makes the rule the only
      thing that answers. -->
- [x] S8.11 Add reviewer handoff and temporary delegation. Evidence: expired
      delegations stop authorizing actions.
  - [x] S8.11.a Define handoff for selected assignment/review target with
        source/ destination reviewer, reason, context, unresolved work, due/SLA,
        and effective time. Evidence: handoff schema.
  - [x] S8.11.b Validate destination eligibility, role/qualification, tenant/
        object access, workload/capacity, COI, and availability. Evidence:
        eligibility negative tests.
  - [x] S8.11.c Transfer ownership atomically, preserve prior reviewer history,
        comments/evidence, due/SLA policy, notifications, and audit. Evidence:
        handoff integration tests.
  - [x] S8.11.d Define delegation scope by action/role/domain/workbench/object/
        target, delegator/delegate, start/expiry, reason, and nondelegable
        actions. Evidence: delegation schema.
  - [x] S8.11.e Require delegator authority and delegate eligibility, prohibit
        privilege amplification/transitive redelegation unless explicit, and
        enforce separation/COI. Evidence: authorization tests.
  - [x] S8.11.f Activate/revoke/expire delegation by server time and re-evaluate
        pending work/approvals without retroactively altering completed actions.
        Evidence: clock-controlled tests.
  - [x] S8.11.g Display effective delegated actor on actions/audit and notify
        affected parties. Evidence: attribution tests.
      <!-- S8.11 marked after writing and reading `src/reviewer-handoff.ts` and
      its spec in this session: 141 tests, 107 fault kinds plus the six
      grant standings, the eight authorization refusals, the eight sweep
      outcomes, the six attribution shapes and the six destination axes all
      proven reachable by the suite's own sweep, 141/141 source injections
      caught against a passing control arm, export-map violations 0, entry
      points 137 -> 138, API pages 28 -> 29, full kit suite 8429 -> 8570
      tests green. THE HOLE: the parent's evidence clause is "expired
      delegations stop authorizing actions", and before writing anything the
      question worth asking is what would make that sentence FALSE rather than
      merely untested. It takes a delegation that cannot expire, and that is
      what was here. `CanonicalClaims` (S4.2.a) carries two grants on adjacent
      lines: `supportGrant` is a `SupportGrantClaim` of `{ grantId, expiresAt,
      reason }` which `resolveSession` turns into `supportGrantLive =
      expiresAt > now`, which `authorization.ts` threads through
      `RequestContext`, `tenancy.ts` refuses on and `router.ts` carries to the
      edge — five modules of lifecycle for one grant kind — while
      `delegationGrant` is `string | null`, validated for spelling and for
      being present whenever `onBehalfOf` is, then copied verbatim into
      `ActorRef.delegated.grant`. There is no `delegationGrantLive`; the
      identifier does not occur anywhere in this repository. Fourteen sites
      read that string and two of them AUTHORIZE on it — `restore.ts` refuses
      unless `request.actor.grant === policy.restoreGrant`, `stages.ts` unless
      `command.actor.grant === transition.requiresScope` — so authority is a
      string comparison against a name, and a string does not expire.
      `parseActorRef` even states the reason the field must exist, "or a
      revoked delegation cannot be traced to what it authorised", and the
      register it would be traced to was never built. The product side matches:
      `AdminWorkspaceStateStore.delegateReview` takes `{ reviewId,
      delegateToReviewerId, note }`, overwrites `review.assignedReviewerId`,
      increments a scalar `delegationCount` and returns, with no window, no
      scope, no revocation route and no second actor, so "temporary delegation"
      is a permanent transfer of ownership and no delegation in the product has
      an end that could lapse. Four of `AdminReviewerRecord`'s fields —
      `active`, `role`, `specialties`, `availabilityStatus` — are read by
      nothing on that path, which is S8.11.b's five eligibility axes with none
      of them enforced; the capacity counter is `Math.min(dailyCapacity,
      currentAssigned + 1)`, so overload is not merely unrefused but
      unrepresentable and the `>= dailyCapacity` test after it can only ever be
      an equality; the predecessor survives only inside the free text of
      "Delegated from Ama to Kofi: <note>"; and the audit event is written with
      `actorId: userId` and `actorLabel: target.displayName`, so the row
      records the giver's id under the receiver's name, which reads correctly
      to a person and wrongly to a query and is unrecoverable afterwards. THE
      GREP: when the parent's evidence clause names a LIFECYCLE, find the field
      that carries the thing whose lifecycle it is and ask whether it has a
      shape a lifecycle could be stored on — then find its SIBLING that does,
      because the sibling proves the omission was not ignorance. So: A HANDOFF
      MOVES THE WORK AND A DELEGATION LENDS THE AUTHORITY, the first permanent
      and the second nothing without an end; A GRANT IS A ROW IN A REGISTER AND
      NOT A TOKEN IN A REQUEST, so `resolveGrant` can FAIL and a work row whose
      only ground names a grant the register cannot resolve is a fault rather
      than an absence; EXPIRY IS EVALUATED AGAINST A SERVER CLOCK AND THE
      CLOCK'S PROVENANCE IS PART OF THE ANSWER; AN EXPIRY THAT IS ONLY CHECKED
      ON WRITE IS NOT AN EXPIRY, so the lifecycle has a SWEEP whose last run is
      part of the observation and a register whose ended grants are older than
      its last sweep is refused; A REVOCATION IS NOT AN EARLY EXPIRY, so the
      grant carries `issuedNotAfter` beside `notAfter` and an end that moved
      with no revocation is a fault; WORK ALREADY DONE UNDER A LIVE GRANT STAYS
      DONE while work STARTED and not finished is withdrawn rather than
      grandfathered, because half an approval is not an approval; WITHDRAWN
      WORK HAS TO GO SOMEWHERE, since a row belonging to nobody looks on every
      surface in this kit exactly like a row that has been dealt with; A GRANT
      CANNOT OUTLIVE THE AUTHORITY IT WAS CUT FROM, which is why the
      delegator's CURRENT standing is an input and `lapsed-with-its-source` is
      its own standing — S5.12's frozen `delegatorMaxBreadth` is the right cap
      in the wrong tense and no window check will ever find it; UNSTATED SCOPE
      IS NOT UNLIMITED SCOPE across all six dimensions, so `all` is a variant
      carrying a written justification and an empty `these` selects nothing;
      NONDELEGABLE ACTIONS ARE DERIVED from `ACTIONS_BY_GROUND.delegation`
      rather than restated, and the one rule that is not derivable is a PAIR,
      completing a sign-off, because S2.13 is about what is attested to and not
      about the verb; ELIGIBILITY IS EVALUATED AT THE INSTANT THE HANDOFF TAKES
      EFFECT and not at the instant it is asked for; and A DELEGATED ACTION HAS
      TWO ACTORS AND BOTH GO IN THE RECORD, where the failure worth its own
      fault kind is neither omission but the CROSS. -->
- [x] S8.12 Add redaction rules for protected sources, learner records,
      biometric data, and unreleased assets. Evidence: notification and activity
      snapshots contain no protected payloads.
  - [x] S8.12.a Define protected-source identity/contact/document/relationship/
        location fields and allowed pseudonym/role summaries per audience.
        Evidence: source redaction policy.
  - [x] S8.12.b Define learner identity/enrollment/activity/assessment/
        accommodation/integrity/credential fields and educational-role access.
        Evidence: learner redaction policy.
  - [x] S8.12.c Define face/voice/body/motion/health/embedding/template/derived
        biometric fields and consent/purpose/role access. Evidence: biometric
        redaction policy.
  - [x] S8.12.d Define unreleased content/title/media/script/build/research/
        model/product/embargo metadata and audience/embargo access. Evidence:
        unreleased asset policy.
  - [x] S8.12.e Apply structured allowlist/redaction before template rendering,
        summary generation, channel routing, activity projection, search/index,
        export, logs, and support tools. Evidence: boundary coverage tests.
  - [x] S8.12.f Prevent inference through counts, actor names/avatars, target
        titles/URLs, snippets, grouping, timing, errors, and deep links.
        Evidence: side-channel tests.
  - [x] S8.12.g Use canary values across notifications/activity/digests/emails/
        push/webhooks/exports/snapshots and fail on any payload leak. Evidence:
        automated leakage suite.
      <!-- S8.12 marked after writing and reading `src/redaction.ts` and its spec
      in this session: 119 tests, 75 fault kinds across nine vocabularies, and
      the four embargo standings and six encoding forms all proven reachable by
      the suite's own sweep. 85/85 source injections caught against a passing
      control arm; the two that escaped the first pass were real spec gaps and
      are recorded below. Export-map violations 0, entry points 138 -> 139, API
      pages 29 -> 30, packed-consumer smoke OK at 139 subpaths, full kit suite
      8688 -> 8690 tests green.

      THE HOLE: TWO OF THE FOUR CATEGORIES THIS ITEM NAMES COULD NOT BE STATED
      ON THE FIELD TYPE THAT DECIDES WHETHER A VALUE LEAVES THE PRODUCT.
      `activity-feed.ts` has carried all four in `ACTIVITY_CLASSIFICATIONS`
      since S8.9, with an audience ceiling for each and a rule that an authored
      title may not appear on a row carrying one — its own comment even says
      "these are the classes S8.12 will write rules for". `protection.ts`'s
      `SensitivityClass`, which is the vocabulary a `SummaryField` is classified
      in and therefore the one `EXPOSURE_CEILING` is keyed on, named two. There
      was no member for a learner record or an unreleased asset, so both could
      only be declared `sensitivity: null` — and `exposurePermits` answers
      `true` for a null class at EVERY rung, including `carrier-plaintext` and
      `third-party-endpoint`. The activity feed refused to carry the event and
      the notification renderer, which is the surface that actually leaves the
      building, waved the same fact through to an SMS and to a webhook. Both
      classes are added here with ceilings (`unreleased-asset` in-product, since
      an embargo is broken by a mail preview; `learner-record` account-mailbox,
      since that is where an institution has always written to a student and the
      next rung is a lock screen somebody else is reading), and the regression is
      tested as a PAIR through `exposurePermits` — the refusal at
      `carrier-plaintext` beside the permission for `null` — because the first
      case could not previously be written at all.

      THE PRODUCT SIDE HAS THE SHAPE THAT GAP PRODUCES. In
      `@oshun/messaging-channels`, `decideMemoryIngestion` returns a
      `MemoryIngestionDecision` whose `redactionRules` is a list of English verb
      phrases — `strip-channel-handles`, `strip-phone-numbers`,
      `strip-initdata`. The identifier `redactionRules` occurs THREE times in
      this repository: the interface, that producer, and one test asserting the
      array `.toContain('strip-initdata')`. Nothing interprets a rule and no
      production call site calls the function. Its two neighbours in the same
      file are the same shape: `consentRevocationCascade` returns strings like
      `revoke-binding:u1:telegram-bot` that no executor consumes, and
      `crisisCrossChannelSuppression` returns `[...new Set(input.channelIds)]` —
      every channel it was handed, deduplicated — from a function whose name is
      suppression. Downstream, `buildPushPayload` puts `deep-link` into `data`
      even on a SILENT push, and `/v1/notifications` composes a flat
      `{ title, body }` per row with no field structure and no class, so there is
      nowhere on the wire for any of this to be said. THAT REMEDIATION IS NOT IN
      THIS ITEM and is recorded here as found.

      THE GREP THAT FINDS THIS VEIN: take the vocabulary the parent phase names
      its protected classes in, find the module that already carries all of them,
      then check the vocabulary the module that decides TRAVEL is keyed on. The
      surface that refuses to RENDER is never the surface that puts a value on a
      wire, and an unstatable class defaults to unlimited.

      TWELVE DECISIONS. REDACTION IS AN ALLOWLIST AND THE DEFAULT FOR AN UNKNOWN
      FIELD IS TO WITHHOLD IT, because a denylist is a list of the leaks somebody
      has already had — the projection returns `released`, `withheld` and
      `unregistered` as three outcomes, and a denylist implementation passes
      every other test in the spec and fails exactly that one. DISCLOSURE IS A
      LADDER AND NOT A BOOLEAN (`none < existence < value < basis`), because an
      instructor must know a student HAS an accommodation, since they have to
      implement it, and must not know what it is FOR. PRESENCE-DISCLOSURE IS A
      PROPERTY OF THE FIELD AND NOT OF THE CATEGORY: `enrollment` existing is
      what being a student means and `accommodation` existing is a disability
      disclosure, in one category on one row, so `protection.ts`'s class-level
      list stays a FLOOR this refines rather than a duplicate. THE AUDIENCE
      LADDER MEASURES BREADTH AND NOT ENTITLEMENT, so the substitute map is
      deliberately not monotone in it — `auditors` ranks wider than
      `workbench-operators` and receives MORE, and the pair is asserted, because
      a map sorted by rank is what somebody writes when they tidy this up. A
      PSEUDONYM IS ASSIGNED AND NEVER DERIVED, and ONE CODENAME PER SOURCE PER
      STORY, because a codename reused across two stories publishes that they
      share a source: the redaction is intact in both documents and the leak is
      in the PAIR. A ROLE SUMMARY IS A REDACTION ONLY ABOVE A COHORT FLOOR, and
      an unmeasured cohort is refused separately from a measured small one. A
      DERIVED BIOMETRIC IS THE BIOMETRIC — an embedding re-identifies exactly as
      well as the image, survives every deletion aimed at the image, and is the
      artefact a withdrawal fails to reach, so a withdrawal that has not reached
      the derivatives is its own fault and not a variety of "withdrawn". CONSENT
      IS PER PURPOSE AND THREE PURPOSES MAY NEVER BE INFERRED FROM ANOTHER
      (`model-training`, `voice-synthesis`, `research`), because each makes
      something durable out of a capture the subject agreed to for something that
      ends. AN EMBARGO WITH NO LIFT INSTANT IS NOT AN EMBARGO, and an ABSENT
      embargo record is a fault rather than a permission, since defaulting it to
      release makes forgetting to write one the fastest route to publication.
      THE PROJECTION HAPPENS ONCE UPSTREAM OF ALL EIGHT BOUNDARIES and each
      boundary declares which projection it consumed, because eight redactors is
      eight places to forget and the one that gets forgotten is the log — the
      fault that matters is `boundary-re-read-the-record-after-the-projection`,
      since enrichment downstream of a redaction reads as a feature in the diff.
      THE SIDE CHANNEL IS THE PAYLOAD: a small count, an avatar URL that is a
      hash of an address, a deep link whose path carries the record id, a
      GROUPING key the reader may not see, and an emission time equal to the
      event time each disclose with every field withheld — and a small count is
      SUPPRESSED rather than rounded, because a rounded count still moves when
      one row is added, so two readings difference back to the row. AND A CANARY
      IS FOLLOWED THROUGH THE ENCODINGS THE CHANNELS APPLY, because a scan that
      only calls `includes` misses the value percent-encoded into a link,
      base64'd into a push `data` field, backslash-escaped by a serializer or
      entity-escaped into a mail part — which is five of the six forms.

      TRAPS. THE INJECTION HARNESS FOUND TWO REAL SPEC GAPS, both of which were
      holes in my own tests rather than in the module, and both are now closed:
      the base64 corpus mapped entirely into `[A-Za-z0-9]`, so swapping the
      alphabet for the base64url one — the mistake somebody makes while "fixing"
      a URL — passed every sample; and the token floor in the codename check was
      untested from below. THE TOKEN FLOOR IS TWO AND NOT THREE: `Li`, `Wu`,
      `Ho`, `Ng` and `Yi` are whole surnames, so three is a check that works for
      European names and not for Chinese, Korean or Vietnamese ones, and it is
      now pinned from both sides by two cases that each fail against exactly one
      of the wrong answers. A RAW NUL BYTE IN A SOURCE FILE MAKES IT INVISIBLE
      TO GREP: one reached this module as a composite-key separator and the
      mandatory stub-indicator scan came back clean because grep had skipped the
      file entirely. It is written as `\u0000` now. SIX OTHER PRODUCTION MODULES
      IN THIS PACKAGE CARRY RAW NULs and are invisible the same way —
      `collaborator-presence`, `determinism`, `diff`, `errors`, `retries`,
      `router` — so every `grep`-based signoff over them has been vacuous;
      re-scanned with `grep -a` here, they hide nothing actionable, but the
      hazard is real and unfixed. FINALLY, `pnpm nx production-scan` is RED and
      was red before this change: `gate-definitions.ts` (S2.9, 2026-08-02)
      exports `CONFORMANCE_FAULTS`/`ConformanceFault`, and
      `src/testing/model-provider-conformance.ts` (S5.1, 2026-08-04) later
      declared the same names, so the scan's name-derived token list flags the
      production module. All three inputs are byte-identical to HEAD. -->

### S9 — Taxonomy, localization, calendar, catalog, and discovery

- [x] S9.1 Extract tighten-only registry merge with code floors and provenance.
      Evidence: every attempted loosening fails.
      <!-- S9.1 marked after writing and reading `libs/oshun/workbench-kit/src/
      registry-merge.ts` and its spec (94 tests, 100% of lines and functions;
      the residual uncovered branches are the per-case shape guards that narrow
      the union for the compiler and two sort tie-breaks). Entry point
      `@oshun/workbench-kit/registry-merge` added to the DERIVED export map
      (`generate-workbench-kit-export-map.mjs`, 140 entry points, no drift, no
      violations), package.json, and the tsconfig.base.json alias table;
      `public-api.spec.ts` resolves it (149 tests) and `tools/production-scan.mjs`
      is GREEN over the emitted graph.

      THE VEIN: A TIGHTEN-ONLY RULE STATED OVER THE LABEL OF A CHANGE INSTEAD OF
      OVER ITS VALUE CANNOT REFUSE ANYTHING. The grep is the word the rule is
      written in — `tighten` — followed by, for every comment promising that a
      validator rejects relaxations, looking at the RECORD the validator is
      handed and asking whether both sides of the comparison are on it. Three
      answers in this tree. `libs/metis/learning/src/mastery/tenant-override.ts`
      is honest and narrow: `enforceTighten(candidate, baseline, 'higher' |
      'lower')` throws, over five fields named one by one, two layers, numbers
      only, no record, and a silent fall-through to the baseline for anything
      that is not a number. `policy-plugins.ts` (S5.9.e) has the general shape —
      `weakeningFaults` checks a requested value against a code floor AND a
      domain floor and carries the observation this module is built on, that a
      domain floor beneath the code floor "was accepted once and has been
      quietly ineffective ever since; whoever set it believes it is in force" —
      but `weakeningFaults`, `EvaluationRecord` and `PolicyOverride` each occur
      exactly three times in the repository: declaration, barrel, own spec.
      NOTHING PRODUCES AN `EvaluationRecord`, so it is a correct checker with no
      merge in front of it. And the live one, fixed in this commit:
      `@oshun/persona-registry`'s `launch-persona-config.ts` promised above
      `PersonaPolicyOverride` that "the validator rejects relaxations", the
      override was `{ ruleId, action, rationale }` with NO VALUE ON IT, and the
      check was `tighteningActions.includes(override.action)` over the same
      three strings the `action` union already permits — true for every
      inhabitant of the type. Eleven shipped in the launch catalog, including
      `{ ruleId: 'tone.pace', action: 'raise-floor' }`. Raise it to what?

      NINE DECISIONS, each with a test that fails without it. PRECEDENCE NEVER
      PICKS THE VALUE — the truth table's `stricter` and `looser` rows are
      identical whichever layer is broader, and rank appears only in the two
      `equal` rows, where it decides ATTRIBUTION and nothing moves. THE TIE GOES
      TO THE BROADER SOURCE and the narrower is reported redundant, because the
      broader one is what still holds when the narrower is deleted. A FLOOR IS
      NOT A DEFAULT AND A BELOW-FLOOR CONTRIBUTION IS REFUSED RATHER THAN
      CLAMPED, since clamping succeeds, discards the author's value and leaves
      them believing it is in force — which is why one refusal fails the WHOLE
      merge. THE CODE LAYER CANNOT BE CONTRIBUTED, because a source that can
      name itself `code` is a source that can raise its own authority. THE FLOOR
      TABLE IS AN ALLOWLIST AND AN UNDECLARED KEY IS REFUSED, because a merge
      that passes unknown keys through is last-write-wins for exactly the keys
      nobody has thought about. INCOMPARABLE IS A REFUSAL AND NOT AN INVITATION
      TO COMBINE: intersecting two audiences yields a value stricter than both
      that NOBODY WROTE. A PROHIBITION IS NOT A FLOOR AND STRICTER IS NOT SAFER
      (`retentionDays: 0` is a lower ceiling than 30 and destroys the audit
      trail). DELETION IS A LOOSENING. AND A CONTRIBUTION IS ADMITTED BY ITS
      PROVENANCE BEFORE ITS VALUE IS READ — expiry, staleness, scope, signature
      and a content hash recomputed against the value — because the strictest
      number in the file is worth nothing if the file was signed before the
      number was edited. -->
  - [x] S9.1.a Define registry entry identity/version, owner, value schema,
        scope, status, source, authority, effective period, and provenance.
        Evidence: registry contract.
        <!-- evidence: `RegistryEntry` carries all nine, and `authority` is
        `'proposal' | 'binding-floor'` rather than an issuer name, because the
        question a merge has to answer about a contribution is whether it
        COMPETES on strictness or BOUNDS everything narrower than it — the
        generalisation of the `domainFloor` policy-plugins.ts hard-codes.
        `REGISTRY_SOURCE_LAYERS` orders the seven from broadest to narrowest,
        and `CONTRIBUTABLE_LAYERS` excludes `code`. -->
  - [x] S9.1.b Define immutable code floors for required entries, minimum
        severity/constraint, prohibited values, trust/safety requirements, and
        schema/version bounds. Evidence: floor schema.
        <!-- evidence: `RegistryCodeFloor` + `defineCodeFloors`, which deep-
        freezes and fingerprints the table (order-independent) and REFUSES ten
        contract mistakes that would otherwise surface as a merge behaving
        strangely: a floor on its own prohibited list (the key then has no
        lawful value at all, including the one it falls back to), an ordinal
        floor outside its own vocabulary, a severity field with no vocabulary,
        a vocabulary on a field that never reads one, two floors for one key,
        an alias cycle, an alias to nothing, an alias shadowing a real key, a
        floor whose shape disagrees with its field kind, and a key the reading
        code requires that no floor declares. -->
  - [x] S9.1.c Define source precedence for code, signed policy, tenant,
        project, domain, environment, and runtime overrides without
        last-write-wins. Evidence: precedence truth table.
        <!-- evidence: `PRECEDENCE_TRUTH_TABLE`, eight rows over relation x
        breadth, total and duplicate-free by test. The `without last-write-wins`
        clause is discharged by two assertions rather than by prose:
        `precedenceOutcome('stricter', true) === precedenceOutcome('stricter',
        false)` and the same for `looser`. Rank is read in exactly two rows and
        both are `equal`, where it decides which source is CREDITED and the
        value does not move either way. -->
  - [x] S9.1.d Implement field-aware merge that permits additions and equal/
        stricter values while rejecting deletion, weakening, wider audience,
        lower threshold/severity, longer expiry, or unknown comparison.
        Evidence: tighten-only property tests.
        <!-- evidence: eight declared orderings (`REGISTRY_FIELD_KINDS`) are the
        eight this clause names — a lower threshold and a lower severity are the
        same move in two vocabularies, a wider audience is a superset, a longer
        expiry is a larger duration, an unknown comparison is `opaque` or an
        ordinal outside its vocabulary, and a deletion is the removal of any of
        them. `every field kind refuses a loosening of its own ordering` walks
        all eight, so a comparison written the wrong way round fails there even
        though every hand-written example still passes. A field kind is DECLARED
        and never inferred: the spec's `gate.minSeverityToBlock` runs its
        vocabulary critical -> info, because a gate that blocks at `low` blocks
        on strictly more, and the same five strings in the natural order mean
        the opposite. -->
  - [x] S9.1.e Preserve contributing source/version/hash/actor/decision/time and
        rejected override reason for every effective field. Evidence: field
        provenance tests.
        <!-- evidence: `FieldContribution` carries all six plus `role`
        (`code-floor` / `binding-floor` / `proposal`) and `disposition`
        (`effective` / `redundant-equal` / `superseded-by-stricter`) with a
        sentence for each non-effective one. The code floor appears AS a
        contribution, so a field held only by the floor still has a row.
        `explainField` renders it in the terms support is actually asked in:
        "your project set 90 days and the tenant requires 30" is the question,
        and a merge returning only the winning value leaves everyone staring at
        a config file that says 90. -->
  - [x] S9.1.f Reject conflicts, ambiguous partial order, incompatible schema,
        cycles/aliases, unsigned required inputs, and stale sources. Evidence:
        negative fixture matrix.
        <!-- evidence: 37 fault kinds in `REGISTRY_MERGE_FAULTS`, and the matrix
        is STRUCTURAL rather than exemplary — `every fault kind is reachable`
        iterates the union and fails if any member has no fixture that produces
        it, so a fault added without a way to trigger it is a test failure
        rather than a comment. Aliases are resolved BEFORE anything else is
        checked, which is what makes the old and the new name compete for one
        field instead of the looser one being in force under a name nothing
        reads. -->
  - [x] S9.1.g Generate deterministic effective registry and diff/explanation.
        Evidence: repeat generation and loosening mutation tests.
        <!-- evidence: three input permutations produce deep-equal registries
        and an identical digest; re-stamping provenance does not move the
        digest and any effective value does. `strictestOf` computes MAXIMAL
        ELEMENTS rather than folding a running maximum, because a fold's answer
        depends on visit order for an unordered set — [{a,b},{b,c},{a,b,c}] left
        to right reports the first pair and right to left never compares them —
        and the spec asserts three orderings agree. The loosening mutation test
        walks every entry, moves that one a step toward looser in its own
        ordering, and requires the merge to refuse. `diffEffective` adds
        `reattributed`: a value that did not move but is now held by a different
        source is one deletion away from moving, and calling that `unchanged` is
        true about the value and wrong about the system. -->
- [x] S9.2 Add registry draft, review, publish, deprecate, alias, and migration
      lifecycle. Evidence: published artifacts remain resolvable.
      <!-- S9.2 marked after writing and reading `libs/oshun/workbench-kit/src/
      registry-lifecycle.ts` and its spec (87 tests, 100% of statements, lines
      and functions; the residual uncovered branches are shape guards and `??`
      defaults). Entry point `@oshun/workbench-kit/registry-lifecycle` added to
      the DERIVED export map (141 entry points, no drift, no violations),
      package.json and the tsconfig.base.json alias table; `public-api.spec.ts`
      resolves it (150 tests), the whole kit suite is green (137 files, 8873
      tests) and `tools/production-scan.mjs` is GREEN over the emitted graph
      (132 production entry points, 264 files).

      THE VEIN: A LIFECYCLE STATE IS A STATEMENT ABOUT WHAT MAY BE WRITTEN NEXT,
      AND IT HAD BEEN INSTALLED ON THE PATH THAT READS WHAT WAS ALREADY WRITTEN.
      The grep is not a word. It is COUNTING RESOLVERS: one resolver means the
      read path and the write path are the same path, and every rule written for
      one of them is in force over the other. `libs/shared/event-bus/src/
      topic-registry.ts` is the live instance and it is a real registry with a
      real lifecycle — 65 topics, schema versions carrying `lifecycle: 'active' |
      'deprecated' | 'retired'`, a `defaultSchemaVersion`, a declared payload
      shape, and enforcement that throws `Schema version 1.0 for event topic X is
      retired`. It has exactly one resolver and it is called `resolveForPublish`.
      `EventBus.publish` stamps `eventSchemaVersion` onto every envelope and
      persists it under a TTL; `EventBus.subscribe` hands that envelope to a
      handler and NEVER RESOLVES THE VERSION IT NAMES. So the schema lookup that
      exists is the one the author performs, the lifecycle gate sits on it, and
      the reader of an already-written artifact has one way through:
      `allowRetired: true`, a flag whose name says the caller is doing something
      exceptional at the moment they are doing the only thing left. Retiring a
      version makes every event ever published under it unreadable through the
      registry, and that is the moment the archive of them is at its largest.

      The same file carries S9.1's vein in its own dialect, which is why it is
      worth naming twice. Each schema version declares `payloadSchema.fields` and
      `payloadSchema.required`, and the only check over them,
      `validateSchemaVersion`, asserts that every member of `required` appears in
      `fields` — THE DECLARATION COMPARED WITH ITSELF. 64 of the 65 topics ship
      no `validate` function and `validatePayload` answered
      `{ valid: true, issues: [] }` when there is none, so a payload missing every
      field the registry calls required was reported valid by the registry that
      calls them required. And `deprecates?: string` occurred twice in the whole
      repository: the declaration and one spec fixture. Nothing set it in
      production and nothing read it.

      BOTH ARE FIXED IN THIS COMMIT, not merely described.
      `EventTopicRegistry.resolveForConsume` is the read path: it never branches
      on a lifecycle state, reports it as advisory text instead, and returns
      nothing only for reasons about the registry (topic not registered, version
      not declared). `validateAgainstDescriptor` makes `required` a requirement
      and every result now carries `checkedBy`, so "nothing validated this" is no
      longer spelled the same way as "this is valid"; `EventBus` runs it, refusing
      in `enforced` mode and reporting in `advisory`, which is the dial that
      already existed for exactly this and keeps a check that was never on from
      refusing traffic on the day it is switched on. A `deprecates` naming a
      version its topic does not declare is now a registration error, and
      `resolveForConsume` reads it. A publish against a non-active version is
      logged rather than passing silently under `allowDeprecated: true`. The
      event-bus suite is green (42 tests, 5 new) and so is every dependent:
      veritas-cascade-worker, trust-safety, deletion-fanout, isis/event-publisher,
      service-discovery, domain-veritas (331).

      TEN DECISIONS in the kit module, each with a test that fails without it.
      THERE ARE TWO RESOLVERS AND THEY ANSWER DIFFERENT QUESTIONS —
      `resolveCurrent` answers what to write against, consults the lifecycle and
      names the replacement when it refuses; `resolveHistorical` answers what a
      reference meant and does not read the state to decide, only to report. A
      VERSION RESOLVES THROUGH ITS OWN ALIAS TABLE AND NOT TODAY'S, because an
      alias is a claim with a date and resolving a 2024 reference through the 2026
      table is how a document silently acquires a meaning nobody gave it — the
      reference did not move, the dictionary did. IDENTITY IS MINTED ONCE AND THE
      SEQUENCE POSITION IS ASSIGNED LATER, so an abandoned draft leaves no gap and
      nothing sorts by an id issued before the order was known. A REVIEW DECISION
      IS BOUND TO THE CONTENT IT REVIEWED, which is the only thing separating
      "three people approved this" from "three people approved something, and then
      it was edited". PUBLISH DECIDES AND DOES NOT WRITE, returning the artifact
      with every cache key to invalidate and notification to send, because a
      function that both computes and performs them is one where the decision can
      be skipped by calling the performer. AN EFFECTIVE TIME MAY BE IN THE FUTURE
      AND NEVER IN THE PAST. A LOOSENING ACROSS VERSIONS IS LAWFUL AND MUST BE
      NAMED BY KEY — S9.1 refuses a loosening between contributions competing in
      ONE merge, and across versions a control is sometimes deliberately relaxed,
      so refusing outright would be wrong and allowing it silently is how a
      control evaporates over three releases that each looked small. AN ALIAS IS
      AN INPUT VOCABULARY. A SPLIT IS NOT A FUNCTION. AND ABSENCE OF MEASUREMENT
      IS NOT ABSENCE OF USE. -->
  - [x] S9.2.a Define immutable registry version identity and draft/review/
        approved/published/deprecated/retired state machine. Evidence: lifecycle
        tests.
        <!-- evidence: `RegistryVersionIdentity` carries `versionId` (minted at
        draft open, never reused, opaque so nothing can sort by it) and
        `publishedOrdinal` (assigned at publish, dense — the abandoned-draft test
        opens `v2`, never publishes it, and requires `v3` to take ordinal 2).
        `LIFECYCLE_TRANSITIONS` is data, `successorStates` is DERIVED from it
        rather than written beside it, and the authority lives on the EDGE not
        the state, which is the only way `in-review -> draft` can be both an
        author withdrawing and a reviewer returning. There is no edge out of
        `retired`: retirement is taken on evidence about a closed interval, which
        cannot expire. `reaches every state from draft` and `names every
        authority on at least one edge` are structural. -->
  - [x] S9.2.b Create/edit drafts against an explicit base with validation,
        semantic diff, conflict, ownership, reason, and provenance. Evidence:
        draft tests.
        <!-- evidence: a base is REQUIRED whenever one exists, because an
        implicit base is whatever was head when the draft happened to be read —
        which makes the draft's meaning depend on a moment nobody recorded and
        makes the `base-moved` check at publish impossible to state. Ownership is
        longest-prefix so `gate.` and `gate.minSeverityToBlock` can belong to
        different teams, and a key with NO owning prefix is not a fault: an
        ownership table that has not reached a new area should slow nobody down.
        `contentHashOf` covers content and nothing else — the same entries opened
        by a different author at a different instant hash identically, which is
        what stops an edit to the reason invalidating every approval. `every
        semantic change kind is produced` iterates SEMANTIC_CHANGE_KINDS, so a
        diff that can name a change it cannot detect is a test failure. -->
  - [x] S9.2.c Define review assignments, required roles/quorum, comments,
        decisions, tighten-only/floor checks, and stale-review invalidation.
        Evidence: review tests.
        <!-- evidence: quorum counts DISTINCT reviewers whose approval carries
        the draft's current content hash, so one reviewer approving twice is one
        approval and an edit drops every tally to zero while still REPORTING the
        stale ones (a reviewer whose approval silently disappeared concludes the
        system lost it). A self-approval is a fault rather than an ignored row,
        for the same reason. One `request-changes` against the current content
        blocks however many approvals stand beside it, because a quorum that can
        outvote an objection is a headcount; a block with an empty comment is
        itself refused, since it cannot be actioned. An `abstain` is recorded and
        never counted, because "nobody looked" and "somebody looked and would not
        decide" are different facts. The tighten-only and floor checks are not
        reimplemented here: publish runs S9.1's `mergeRegistry` over the draft
        once per scope the version names. -->
  - [x] S9.2.d Publish atomically with immutable artifact/hash, effective time,
        compatibility, notifications, cache/index invalidation, and audit.
        Evidence: publish tests.
        <!-- evidence: `publishVersion` returns the artifact, the next catalog,
        the semantic diff, the invalidations, the notifications and the audit —
        and PERFORMS none of them, which is `stages.ts`'s rule for transitions
        applied to publication. The input catalog is asserted unchanged. Order
        matters: identity, base, review, content, then effect, because checking
        the effective registry first spends the expensive work on a change whose
        base moved under it. `base-moved` is the one that catches the real
        failure — an approved draft based on a version that is no longer head was
        reviewed against a registry that no longer exists, and publishing it
        reverts somebody else's version without anybody deciding to. A surface
        that declares NO key dependency is invalidated by everything, because a
        projection whose inputs nobody wrote down is always potentially stale.
        Compatibility is about RESOLVABILITY, not agreement: `migrated` is the
        distinction worth having, a change that would break references and
        shipped its bridge in the same version. Two bugs were found by coverage
        rather than by reading: a multi-scope version could never publish (S9.1's
        merge refuses an entry addressed elsewhere, correctly — the per-scope
        merge has to be handed the entries for that scope), and a rename read as
        a deletion until the base was aligned through the NEW version's alias
        table, which made every bridged rename report a regression. -->
  - [x] S9.2.e Deprecate with replacement, migration guide, warning, supported-
        through/sunset, owner, and usage telemetry. Evidence: deprecation tests.
        <!-- evidence: the artifact is untouched and the notice sits beside it,
        which is decision 1 seen from the storage side. `withdrawn` is a
        first-class replacement with a signed rationale rather than a null
        successor, because "there is no successor" is a decision somebody made
        and a missing field is a decision nobody made. A sunset not after the
        deprecation is refused: a sunset in the past is a retirement wearing a
        warning. `deprecationWarning` reads nothing but the notice, so two
        consumers receive byte-identical text — the form in which "without
        exposing other consumers" is checkable rather than promised. THE RULE
        THAT MATTERS IS THE THIRD RETIREMENT BLOCKER: zero rows in a telemetry
        table is not zero usage, it is zero measurement, and the two are
        indistinguishable to anything that only sums. The observation windows are
        unioned and compared against the whole interval from sunset to now, and
        each gap is reported AS AN INTERVAL so somebody can go and find out what
        was running then. The spec asserts the measured-zero and the unmeasured
        zero are the same number and only one of them is evidence. An amendment
        may extend a sunset and may not take one back. -->
  - [x] S9.2.f Add alias with stable source/target, scope, noncollision,
        noncycle, display/search behavior, and deprecation status. Evidence:
        alias tests.
        <!-- evidence: eight refusals — collision with a live key, missing
        target, duplicate in one scope, cycle, a target with no entry in a scope
        that covers the alias, displayed-but-unsearchable, deprecated-and-
        displayed, and self-reference. `searchable` and `displayed` are separate
        fields because they are separate decisions with OPPOSITE defaults: you
        may find a record by the name it was filed under, and rendering that name
        back to a person is how the rename fails to finish, since the retired
        spelling re-enters new documents by copy-paste. A cycle returns `null`
        rather than the last name before the loop, because a resolver that
        answers from inside a cycle gives a different answer depending on where it
        entered; `resolveHistorical` reports that as a key absence rather than
        resolving it. -->
  - [x] S9.2.g Define migration mappings for removed/merged/split/moved/renamed
        entries with loss/conflict report and resumable backfill. Evidence:
        migration tests.
        <!-- evidence: A SPLIT IS NOT A FUNCTION. One key becoming three has no
        mapping unless a discriminator picks the target, so the plan reports the
        row `ambiguous` rather than taking the first target — a backfill that
        silently picked is indistinguishable afterwards from one that knew. A
        merge is `lossy` and a removal is `dropped`, both named in
        `migrationLossReport`. The plan is deterministic (a shuffled mapping list
        produces an identical hash and an identical step order) and the cursor
        carries the plan hash: RESUMING A CHANGED PLAN FROM AN INDEX SKIPS ROWS
        THE NEW PLAN PUT BEFORE IT THAT WERE NEVER PROCESSED, and the run then
        reports completion over a set it never covered. `a backfill resumed at
        any point covers exactly what one pass covers` splits the run at EVERY
        index rather than one, because an off-by-one survives a single
        hand-picked break point half the time. -->
  - [x] S9.2.h Resolve exact historical published versions and aliases after
        later updates/deprecation/retirement. Evidence: historical resolution
        tests.
        <!-- evidence: `every published state still resolves history` iterates
        PUBLISHED_STATES, drives one catalog into each, and requires an answer in
        every one — the parent's evidence clause quantified over the state
        vocabulary instead of sampled at the state somebody remembered to test. A
        lifecycle gate added to the read path fails there, which is exactly what
        `topic-registry.ts` did and what no test of it caught. Nothing in
        `resolveHistorical` branches on the state: it reads it, puts it on the
        result and renders it as advisory, and `fails only for reasons about the
        artifact` iterates HISTORICAL_RESOLUTION_FAILURES to show all four are
        statements about the artifact. Resolution goes through the version's OWN
        alias table: v1 reads `gate.minSeverityToBlock` directly as `medium`, v2
        reads the same name through an alias onto the renamed key as `low`, the
        two answers differ, both are correct, and mutating v2's alias table does
        not move v1's answer. `resolutionHistory` shows the whole sequence,
        because "this document says X and the console says Y" is the question
        support is actually asked. -->
- [x] S9.3 Add taxonomy impact preview. Evidence: affected queries, artifacts,
      filters, exports, and URLs are enumerated before change.
      <!-- S9.3 marked after writing and reading `libs/oshun/workbench-kit/src/
      taxonomy-impact.ts` (+ spec, 123 tests, 100% of statements, branches,
      functions and lines, 37 fault kinds) and after FIXING the shipped preview
      the item is about. THE VEIN IS VISIBLE IN A RETURN VALUE WITHOUT READING
      THE BODY: `libs/oshun/studio-authoring/src/taxonomy-curation/curation.ts`
      accumulated `affectedArtifactIds` into a `Set` and
      `affectedRecommendationSlots` / `affectedSearchResults` with `+=` IN THE
      SAME LOOP, so one population came back under two cardinality rules at once
      — the union on one row and the multiset sum on the next — and the
      operation whose entire purpose is the overlap is the merge. THE GREP IS
      NOT A WORD, IT IS TWO CARDINALITY RULES INSIDE ONE RETURN VALUE; where a
      preview both unions and sums, one of them is wrong, and the wrong one is
      whichever number somebody later divides by to size a maintenance window.
      Its own test is what kept it: it merges `tag-a` and `tag-b`, puts `art-2`
      on both, and asserts 3, then 30, then 300 — three is the union, thirty and
      three hundred are 12+18 and 100+200 — and a test that agrees with itself
      twice in opposite directions reads as confirmation. Three more faults sat
      beside it, all fixed in the same commit rather than described: it read
      `request.targetIds` and nothing else, so a reparent of a node with four
      hundred descendants previewed the epicentre rather than the radius; every
      lookup was `map.get(id) ?? 0`, so an index that was down, a tag nobody
      scanned and a tag genuinely used by nothing produced the same zero, AND A
      ZERO IS THE ONE ANSWER THAT MAKES SOMEBODY CLICK APPLY; and
      `ONTOLOGY_ROLLOUT_STAGES` opened with a stage named `preview` whose
      transition function never mentioned one — `requestId` sat on both types,
      nothing joined them, and `preview -> staged -> rolled-out` advanced whether
      or not anybody had computed anything. A stage name is not a gate. The fix
      carries populations rather than numbers (a NUMBER CANNOT BE DEDUPLICATED,
      which is exactly why the artifact count was right and the other two were
      wrong), walks the closure, reports `unmeasuredTagIds` per dimension, and
      makes `tryAdvanceOntologyRollout` refuse `preview-missing`,
      `preview-for-another-request` and `preview-incomplete`. -->
  - [x] S9.3.a Accept a versioned proposed add/rename/move/merge/split/alias/
        deprecate/retire change against exact published base. Evidence: preview
        input contract.
        <!-- evidence: `acceptProposal` binds the proposal to the exact BYTES of
        the base, not to its name — `base-moved` is checked before anything is
        enumerated, because a base that merely matches by id is one that moved
        while the form was open and every count over it is a count of a
        population that no longer exists. All eight kinds are declared in
        `CHANGE_SHAPES` as DATA, and `refuses the wrong arity for every kind`
        walks that table rather than sampling the kind somebody remembered, so a
        ninth kind added without an arity rule fails instead of being accepted
        in any shape. `refuses every pre-publication state` quantifies over the
        state vocabulary the same way. The conflict rule reads `mutatedKeys`,
        not the keys a change NAMES: two moves onto one parent redefine nothing
        about that parent and their order changes nothing, so they are accepted,
        while two changes rewriting one declaration are still refused. And a
        split with no discriminator is deliberately NOT refused — telling the
        proposer that twelve thousand assignments have nowhere to go is the most
        useful thing the module does, and refusing would withhold exactly
        that. -->
  - [x] S9.3.b Enumerate affected stored entity assignments, inherited
        classifications, translations, rules, gates, permissions, and dependent
        registry entries. Evidence: data impact rows.
        <!-- evidence: the seven kinds the clause names are the seven members of
        `DEPENDENT_KINDS` that `SURFACE_OF_KIND` maps to `data`, asserted as a
        count rather than by spot-check. The rows are the correction of the
        shipped defect: `counts a record that carries both merged keys ONCE`
        rebuilds curation.ts's own fixture and gets 3 on EVERY dimension, and
        `partitions the affected records` proves no id appears under two rows,
        so a surface total is the size of a union. A translation on both merged
        keys is a `meaning-collision` rather than a retarget, because two labels
        for one concept is a question ("which wording survives"), not a
        migration. -->
  - [x] S9.3.c Enumerate affected search queries/index docs/facets/suggestions,
        saved views/queues, filters, reports, and dashboards. Evidence:
        discovery impact rows.
        <!-- evidence: nine kinds on the `discovery` surface, and the surface
        where WHO HOLDS THE REFERENCE stops being a detail: an index document is
        one we rewrite and a saved view is one a person holds, so the same
        rename is `retargeted` for both and only the second puts its owner on
        `owners` with `mustBeNotified`. A report and a dashboard carry a time
        series, so a merge, a split or a move gives them `series-discontinuity`
        and a `history-annotation` decision — the chart that silently spans the
        change is a chart nobody can read. `an add widens what its parent
        contains, which nobody edited` is the S9.3 case the shipped preview
        could not express at all: a saved view reading "everything under X"
        returns more rows tomorrow and no query changed. -->
  - [x] S9.3.d Enumerate affected bundle/export/import/interchange values,
        consumer contracts, external IDs, catalog/collection rules, and
        published artifacts. Evidence: integration impact rows.
        <!-- evidence: eight kinds, and the one place `REACH_OF_KIND` had to
        agree with S9.2 rather than with intuition — `published-artifact` is
        `outside`, because S9.2.d fixes its bytes forever and a preview that
        listed it among the things we rewrite would be promising to edit an
        immutable artifact. Export, interchange value, consumer contract and
        external id are `outside` for the ordinary reason: they are already in
        somebody else's system. An alias carries them and a rename without one
        BREAKS them, and no alias can carry a SPLIT at all, an alias being a
        function and one name to three targets not being one. -->
  - [x] S9.3.e Enumerate canonical/deep URLs, navigation, redirects, bookmarks,
        caches, and share links. Evidence: route impact rows.
        <!-- evidence: seven kinds, and the surface that reads a DIFFERENT
        mitigation from the other three. `an alias does not move a URL — a
        redirect does`: the registry vocabulary and the route table are separate
        machines, so `effectOn` reads `mitigations.redirectsInstalled` for
        `route` and `change.aliasInstalled` everywhere else, and a proposal that
        installs one and not the other gets rows saying so. A share link already
        sent and a bookmark in somebody's browser are the references a rename
        cannot reach, which is why they are `broken` rather than `orphaned` —
        those are different remedies, one a redirect and one a migration. -->
  - [x] S9.3.f Calculate counts, samples, ambiguous mappings, required
        decisions, migrations/backfills/reindex, cost/time, owners, and
        rollback. Evidence: impact plan.
        <!-- evidence: `ImpactCount` is the spine — exact, at-least, estimated
        (carrying its observed rows AND its fraction) or unknown (carrying a
        reason) — and there is exactly one path to a zero in the module: a kind
        covered by a COMPLETE source with no matches. An unscanned kind
        contributes an `unknown` to its surface total, so the ignorance reaches
        the number an operator reads instead of being absorbed at the leaf.
        `combineAll` is a symmetric function of the whole list because folding
        two at a time is NOT associative once ignorance is in the lattice, and
        `is NOT the repeated application of a binary operator` demonstrates it:
        the same three counts answer `at least 14` one way and `about 1004` the
        other. Cost is an `ImpactCount` for the same reason — a cost derived
        from a floor is a floor, and that is the number somebody schedules a
        maintenance window against. A required decision is ONE question about
        ONE change with every table aggregated behind it, kept out of the
        handled column because twelve thousand undecidable rows printed beside
        the retargeted ones read as twelve thousand handled. Rollback is DERIVED
        from what the change destroys, never asserted: a merge without a
        captured pre-image is irreversible and says which keys need one, and a
        reference outside our reach that will be dead keeps it irreversible even
        with a snapshot. -->
  - [x] S9.3.g Keep preview read-only and revalidate base/proposal/authorization
        when applying through a separate audited job. Evidence: no-side-effect
        and stale-preview tests.
        <!-- evidence: `does not touch a single input` deep-freezes the catalog,
        proposal, sources and ownership, runs the preview and compares the JSON
        before and after — a frozen input mutated under ESM strict mode throws,
        so the test fails loudly rather than by diff. `authorizeApply` does not
        read the plan's conclusions: it RECOMPUTES the plan from the catalog,
        proposal and inventories as they are now and compares hashes, so a moved
        base, an edited proposal, a re-read inventory and an altered plan are
        each refused BY NAME with the hash comparison as the backstop. Staleness
        is measured from the OLDEST contributing observation rather than from
        when the plan was assembled, because a plan built a second ago out of a
        four-hour-old index is four hours old. The strongest rule is
        `preview-insufficient`: a preview that could not see part of the system
        authorizes NOTHING, which is precisely the gate `?? 0` exists to slip
        past. The job carries the plan hash, the proposal hash, the base content
        hash, the authorizing actor, the revalidation instant and the audit
        reference. -->
      <!-- Registering the entry point was the usual four edits — the
      `KIT_OWNED_ENTRY_POINTS` row, `package.json` exports, the
      `tsconfig.base.json` alias, then regenerate — and the generator NAMES the
      two you forget (`unaliased-approved-entry-point`,
      `unexported-approved-entry-point`) rather than making you find them.
      142 entry points, no drift, no violations. -->
      <!-- NOT closed by this work and deliberately left for a host: the module
      takes the dependent inventories as input and does not go and read them.
      That is the correct seam — the stores, indexes, route tables and clocks
      are the host's — but it means a deployment gets `kind-unscanned` for every
      class it has no source for until somebody writes those sources, and
      `completeness: 'insufficient'` is the honest answer until they do. -->

- [x] S9.4 Add localized label and description support without localizing stable
      IDs. Evidence: locale fallback tests.
      <!-- S9.4 marked after writing and reading `libs/oshun/workbench-kit/src/
      localized-labels.ts` (+ spec, 179 tests, 100% of statements, branches,
      functions and lines) and after FIXING the shipped localization workflow
      the item is about. THE VEIN: A LOCALE-DEPENDENT JUDGEMENT WRITTEN WITH A
      LOCALE-INDEPENDENT PRIMITIVE ANSWERS THE SAME THING EVERYWHERE, AND THE
      PLACE IT ANSWERS WRONG IS ALWAYS THE LOCALE THE FEATURE WAS ADDED FOR. THE
      GREP IS NOT A WORD: find every comparison the localization path makes and
      ask which of `startsWith` on a tag, `\W`, `.length`, `toLowerCase`,
      `localeCompare` and a hand-written script range it is built out of, because
      each of those is a decision about language wearing the clothes of a string
      operation. Three measured instances shipped in `libs/oshun/studio-authoring/
      src/localization-workflow/`, all fixed in this commit rather than
      described. (1) `fuzzyMatchTranslationMemory` scored candidates with a
      Jaccard similarity over `text.toLowerCase().split(/\W+/)`, which is ASCII,
      so for ja/zh/ko/ar/he/th/hi both token sets came back empty and its own
      guard `if (aTokens.size === 0 && bTokens.size === 0) return 1` reported a
      PERFECT MATCH BETWEEN TWO UNRELATED SENTENCES — measured at exactly 1 for
      the Japanese, Arabic and Hebrew pairs and 0 for the English control, so the
      one language the translation memory worked for was the one nobody
      translates into; it then returned the entry's stored `score` instead of the
      similarity it computed. (2) The RTL check was
      `targetLocale.startsWith('ar-') || startsWith('he-')`, and
      `libs/oshun/i18n`'s `OSHUN_LAUNCH_LOCALES` ships `'ar'` and `'he'` WITHOUT
      A REGION — so the two right-to-left locales the product launches with were
      the two it could not see, along with fa/ur/ps/sd/ug/dv/yi/ckb; listing tags
      would not have fixed it, because DIRECTION IS A PROPERTY OF THE SCRIPT
      (`ku` is Latin and `ckb` is Arabic). (3) `detectHonorificGap` used
      `/[さま|さん|くん|ちゃん|殿|様]/u`, which is a CHARACTER CLASS and not an
      alternation, so it matched any text containing さ, ま or ん — and its own
      fixture `田中が到着しました` contains ま, so the assertion in a test named
      "flags ja-JP target missing -san / -sama" had only ever been satisfied by
      the generic `info` fallback at the bottom of the function. Two more sat
      beside them: `detectStaleTranslations` answered `{stale:false}` when the
      source hash was ABSENT from the index it was handed, so a verified match
      and a failed lookup were one answer; and `surfaceCustomerStalenessBanners`
      — THE ONLY CUSTOMER-VISIBLE STRING THE LOCALIZATION SYSTEM ITSELF PRODUCES
      — was a hardcoded English sentence labelled with a locale read from a map
      named `localeByDomain` indexed by `segmentId` and defaulted to `'en-US'`: a
      fallback persisted as a translation, committed by the localizer.
      `prioritizeLocaleQueue` also ignored the `nowUnixSeconds` it was handed, so
      overdue work never rose. -->
  - [x] S9.4.a Keep stable registry/taxonomy ID, code, alias target,
        parent/path, version, and interchange value locale-independent.
        Evidence: contract tests.
        <!-- S9.4.a: LOCALIZED TEXT IS A SIDECAR KEYED BY THE STABLE ID AND NEVER
        A FIELD ON THE ENTITY — S9.2 fixes a published version's bytes forever,
        so a French typo correction must not change a content hash, invalidate an
        alias resolution or need a version whose diff a reviewer reads.
        `LOCALE_INDEPENDENT_FIELDS` names the seven; `admitStableIdentity`
        refuses anything but lowercase ASCII (NFC and NFD are two rows to `===`)
        and refuses a `path` that does not END at `parentId`, because a path and a
        parent pointer are two spellings of one fact. `identityInvariantFaults`
        is the contract test itself: the same entry rendered in eight locales
        including `ar`, `he` and `ja-JP` must carry one fingerprint, and the spec
        also feeds it a Turkish-folded `code` and a collator-reordered alias list
        to prove it can fail. Ordering of ids is `compareCodepoints`, never
        `localeCompare`, which consults the RUNTIME's default locale. -->
  - [x] S9.4.b Store localized label/short/long description/help/keywords with
        canonical locale, source locale, status, author/translator, revision,
        provenance, and updated time. Evidence: localized value schema.
        <!-- S9.4.b: `LocalizedTextValue` / `LocalizedKeywordsValue` carry every
        field the clause names, and `admitLocalizedValue` reports every fault at
        once rather than the first. The refusals worth naming are the ones a
        SCREEN causes rather than a programmer: a machine provenance must name
        its engine (an unattributed machine translation cannot be re-run or
        recalled when the engine is found wrong) and a human one must not; a
        human translation must name its translator; an authored value is its own
        source and may not carry one; and a `human-translated`,
        `machine-translated` or post-edited value whose source locale IS its own
        locale is refused, because that row is one the coverage report counts and
        the translator never saw. Keywords are a list and never a sentence: they
        are joined against a query, and a surface that displayed them would be
        showing a reader the terms somebody guessed they might type. -->
  - [x] S9.4.c Define exact locale, language, configured fallback chain,
        source/default, and missing behavior without silently persisting a
        fallback as translation. Evidence: fallback tests.
        <!-- S9.4.c: `parseLocale` canonicalises through
        `Intl.getCanonicalLocales`, so `en-us`, `EN-US` and the 1989 spelling
        `iw` are not three rows, and the `und` refusal is checked on the
        canonical STRING because `new Intl.Locale('und').language` is
        `undefined` and a check made after construction reports "no primary
        language subtag" about a tag whose primary language subtag is right
        there. `admitLocaleCatalog` refuses chain cycles at configuration time —
        a loop found at request time is a hang on a page load — and
        `buildFallbackChain` walks exact, configured, RFC 4647 truncation,
        source, default, listing a locale ONCE at its first position. A FALLBACK
        IS RENDERED AND NEVER PERSISTED: `resolved-fallback` is a provenance the
        read path produces and `admitLocalizedValue` refuses BY NAME, and a
        target byte-identical to its source is refused unless declared a source
        copy, because a brand name legitimately does not change and a fallback
        somebody pressed save on looks exactly the same. -->
  - [x] S9.4.d Distinguish absent, draft, machine-suggested, human-reviewed,
        approved, stale, rejected, obsolete, and inaccessible values. Evidence:
        state fixtures.
        <!-- S9.4.d: nine states, and TWO OF THEM CANNOT BE STORED — `absent` and
        `inaccessible` are answers about a LOOKUP, and a stored row saying "there
        is no row" is a contradiction that also hides itself from every coverage
        count; `admitLocalizedValue` refuses them as `resolution-only-status`.
        They stay in one enumeration because a caller must tell them apart:
        `absent` means translate this, `inaccessible` means somebody already did
        and this actor may not read it, and collapsing them either leaks the
        existence of embargoed copy or commissions a second translation of
        something already paid for. `DISPLAY_TABLE` crosses status with audience
        and has THREE dispositions, because two are not enough: a stale approved
        translation beats no label and pretending it is current is what the
        notice prevents. `machine-suggested` reaches an end user under no
        configuration. Staleness is three-valued for the same reason the shipped
        detector was wrong — `unverifiable` keeps its stored status and carries a
        notice, so an index outage does not read as a translation regression. -->
  - [x] S9.4.e Validate script/direction, length/markup/placeholders/links,
        terminology, uniqueness where required, and unsupported locale.
        Evidence: validation tests.
        <!-- S9.4.e: seventeen fault kinds, and the measurement is the point.
        Length is GRAPHEMES (`'a👨‍👩‍👧b'.length` is 10 and its grapheme count is
        3, so a budget in code units is a different budget in every script).
        Script conformance is a measured RATIO over letters using
        `Script_Extensions` rather than `Script` — U+30FC, the Japanese prolonged
        sound mark, is `Script=Common`, so a check built on `Script` calls an
        ordinary `ポートレート` mixed-script — and `UNICODE_SCRIPTS_OF_ISO15924`
        maps the composite codes CLDR returns, because `/\p{Script=Jpan}/u` is a
        `SyntaxError` and a validator built on them fails to LOAD rather than
        failing open. Placeholders and link targets are compared as sets in BOTH
        directions (a dropped `{count}` and an invented `{userName}` are
        different bugs; a translated `href` is an open redirect that arrived
        through a spreadsheet), the placeholder pattern uses a LOOKAHEAD so
        `{count, plural, one {#} other {#}}` is not missed, and U+202A–U+202E
        embedding/override controls are refused while the isolates are permitted,
        because an override makes the label a person reads differ from the one
        that sorts and exports. Uniqueness is per (scope, field, locale) under
        one declared fold that uses `toLowerCase`, never `toLocaleLowerCase`. -->
  - [x] S9.4.f Search/sort/display by requested locale while retaining stable ID
        for API/filter/URL/export and exposing fallback/source state. Evidence:
        end-to-end locale tests.
        <!-- S9.4.f: `resolveLocalizedText` NEVER returns a bare string — a
        function returning `string` has already discarded whether the caller is
        looking at a French label, an English one or the key, and the caller
        renders all three identically; every read answers with the resolved
        locale, the chain step, the effective status and the notices. Direction
        comes from the locale the TEXT came from, not the one requested, so an
        English fallback is not laid out right-to-left. `renderEntryList` orders
        by an `Intl.Collator` for the requested locale and BREAKS TIES ON THE
        STABLE ID, because a collator calls many pairs equal and a list whose
        equal-label rows come back differently each call is one where a
        pagination cursor skips a row; the collation locale is read off the
        constructed collator's `resolvedOptions()` rather than from
        `supportedLocalesOf`, which hands the tag straight back. An unlabelled
        row sorts by its id rather than as an empty string at the top of the
        list. `searchLocalizedEntries` matches a stable id exactly, outranking
        every text match and unaffected by locale. And `buildInterchangeRow`
        TAKES NO LOCALE PARAMETER AT ALL — the spec asserts it is byte-identical
        for `ar` and `ja-JP` — and carries only approved labels, because an
        export is read by a system that cannot see a status. -->
- [x] S9.5 Add editorial calendar entities for tentative and committed slots.
      Evidence: committed capacity cannot be double-booked.
      <!-- S9.5 marked after writing and reading `libs/oshun/workbench-kit/src/
      editorial-calendar.ts` (+ spec, 103 tests, 100% of statements, branches,
      functions and lines) and `tools/double-booking-race.mjs` (run against the
      live PostgreSQL 16.14), and after FIXING both shipped calendars.
      THE VEIN: FIND THE STRING OR THE TYPE NAMING THE FAULT THE MODULE EXISTS TO
      PREVENT AND THEN FIND THE WRITE THAT COULD HAVE REFUSED IT; WHERE THE FAULT
      IS A NOUN IN A REPORT AND NOT A REFUSAL IN A COMMAND, THE SYSTEM DETECTS
      WHAT IT WAS BUILT TO PREVENT.
      `libs/aphrodite/talent/src/calendar/availability-calendar.ts` knows exactly
      what a double booking is — `CalendarConflict` with
      `conflictType: 'double-booking'`, found by pairwise `slotsOverlap` over
      slots that are ALREADY `BOOKED` — and the only path into that status was
      `updateSlotStatus(slotId, status)`, whose entire body was
      `const updated = { ...slot, status }; this.slots.set(slotId, updated)`. It
      consulted `slotsOverlap`, exported from the same file, not at all; it
      consulted the `BookingBlock` prep/recovery window its own `blockForBooking`
      stored, not at all, because nothing read `bookingBlocks` but a getter; it
      consulted the blackout dates not at all.
      `libs/aphrodite/venue-management/src/scheduling/venue-scheduler.ts` is the
      same shape with a sharper edge, because the check it needed is CALLED
      TWICE, three lines apart, for the two things that are not the booking:
      `createSlot` walks every blackout asking `timesOverlap`, then every
      maintenance window asking `timesOverlap`, and never once asks it about
      another SLOT — so two overlapping `AVAILABLE` slots at one venue were
      created and booked without complaint — while `venueCapacities`, the only
      number saying how much of the venue there is, is read in exactly one place,
      `matchPerformersToSlots`, to SCORE a suggestion. And `holdSlot(slotId,
      notes?)` set a status and wrote a note: no holder, no granted time, no
      expiry, no clock, with `bookSlot` accepting a `HELD` slot from any caller —
      a hold that never expires and that anybody may book over is a label, and
      its test asserted the label.
      All three fixed in this commit: `bookSlot` is now a guarded write in both
      files (returning a named refusal in the talent engine, throwing in the
      venue scheduler), `updateSlotStatus` REFUSES `BOOKED` and points at it,
      holds carry a holder and an expiry swept by `expireHolds(now)`, and
      `detectConflicts` is documented as what it can now honestly be: the
      detector for conflicts that ARRIVED from an import or a raised
      `minRestHours`, because a detector runs over a snapshot and a refusal has
      to be part of the write. -->
  - [x] S9.5.a Define calendar/resource/slot IDs, owner/scope, title-safe ref,
        start/end/time zone, status, priority, target/type, capacity demand,
        dependency, revision, and audit. Evidence: slot schema.
        <!-- S9.5.a: `CalendarSlot` carries all seventeen, and `admitSlot`
        reports every fault at once. Two checks earn their place. AN INTERVAL IS
        HALF-OPEN — `[start, end)` — so back-to-back slots do not conflict and
        nobody subtracts a second to work around it, and a zero-length interval
        is refused because it occupies nothing and therefore passes every
        exclusion check ever written. And THE OCCUPIED INTERVAL MUST CONTAIN THE
        BOOKED ONE: setup and teardown extend it outwards, so a swapped pair
        makes the exclusion narrower than what the calendar displays, which is a
        double booking that looks correct on every screen. `titleSafeRef` is an
        opaque handle and never the title, because the title of an unannounced
        production is what an embargo exists to protect and a field that
        SOMETIMES holds it leaks it through the one endpoint nobody thought
        about. The time zone is checked against `Intl.DateTimeFormat` rather than
        a pattern, because `Continent/City` is a shape and `Africa/Atlantis`
        matches it. -->
  - [x] S9.5.b Define proposed/held/tentative/committed/in-progress/completed/
        cancelled/expired/conflicted transitions and authorization. Evidence:
        slot state tests.
        <!-- S9.5.b: `TRANSITION_TABLE` and `TRANSITION_ROLES` are data.
        `commit` may be reached from proposed, held and tentative and NOT from
        `conflicted` — the world already moved under something, and committing on
        top of it is how the conflict acquires a second occupant. NO HUMAN ROLE
        MAY `expire`: the clock performs it, because an expiry a person performs
        is a cancellation nobody has to sign, and `authorizeTransition` refuses
        both directions of that (`clock-only-transition`). The refusals are
        separate kinds because they are separate remedies — `cross-tenant` is not
        a permission to grant but a different calendar and short-circuits alone,
        `role-may-not-perform` means ask somebody else, `not-the-holder` means
        the hold is somebody's, `wrong-source-state` means the world moved — and
        a single boolean makes all four "no" and the third unfixable by whoever
        hit it. Property writes are authorized SEPARATELY, because a scheduler
        may commit a slot and still not be allowed to change its priority. -->
  - [x] S9.5.c Define tentative hold duration/expiry, renewal, overbooking
        visibility, ranking, confirmation deadline, and no guaranteed-capacity
        claim. Evidence: hold clock tests.
        <!-- S9.5.c: A HOLD IS A LEASE OR IT IS A LABEL — holder, granted
        instant, expiry, bounded renewals, confirmation deadline that the lease
        must outlive. Expiry is a SWEEP against a clock and never a read-side
        check, because a hold that is only expired when somebody looks is holding
        the interval in every query that does not look; and an OVERDUE
        confirmation is reported separately and releases nothing, because
        automatic release means an editor who was in a meeting loses a studio.
        Renewal is measured from NOW rather than added to the old expiry, so a
        script cannot hold an interval indefinitely by renewing early, and the
        bound exists because AN UNBOUNDED RENEWAL IS A COMMITMENT MADE IN
        INSTALMENTS THAT NOBODY SIGNED. A HOLD NEVER CLAIMS CAPACITY: holds are
        granted while the resource is fully committed — that IS overbooking
        visibility — `maxConcurrentHolds` bounds the QUEUE and not the capacity,
        and `describeHoldPosition` returns `guaranteesCapacity: false` as a
        LITERAL TYPE so no surface rendering it can describe it as anything
        else. -->
  - [x] S9.5.d Define committed reservation transaction, capacity unit/
        availability, exclusivity/share rules, expected revision, and
        idempotency. Evidence: reservation contract.
        <!-- S9.5.d: AN AVAILABILITY CHECK IS NOT A RESERVATION.
        `evaluateReservation` decides against a snapshot and, on acceptance,
        returns a `ReservationCommand` carrying the exclusion key, both expected
        revisions and the idempotency key — because a detector can run over a
        snapshot and a refusal cannot; it has to be part of the write. The
        exclusion key is tenant and resource and NOT the interval: an interval in
        the key makes every booking unique and the constraint decorative.
        CAPACITY IS A QUANTITY WITH A UNIT AND TWO UNITS DO NOT ADD — refused,
        never converted, the factor being a domain fact this contract does not
        have. EXCLUSIVE AND SHARED ARE DIFFERENT ARITHMETIC AND NOT DIFFERENT
        AMOUNTS: modelling exclusivity as "demand equals capacity" makes an
        exclusive booking silently shareable the moment somebody buys a second
        encoder. And capacity is drawn down AT AN INSTANT, so the check is the
        PEAK concurrent demand over the overlap and never the sum over the
        window, which refuses bookings the resource could have taken. Idempotency
        is about the request: same key and digest replays, same key and a
        different digest is refused rather than becoming a second booking. -->
  - [x] S9.5.e Enforce exclusion/capacity constraints under concurrent commit,
        resize, move, cancel, resource change, and recurrence materialization.
        Evidence: real-DB double-booking tests.
        <!-- S9.5.e: `libs/oshun/workbench-kit/tools/double-booking-race.mjs`,
        target `double-booking-race`, run against the live PostgreSQL 16.14 on
        this box. It FAILS rather than skips with no database. THE CLAIM: four
        schedulers that all read "free" — the situation `evaluateReservation`
        answering `ok` to four callers at once MEANS — produce exactly ONE
        committed booking under `EXCLUDE USING gist (exclusion_key WITH =,
        occupied WITH &&) WHERE (state is capacity-occupying)`, measured. CONTROL
        1 drops the constraint, changes nothing else, and the same code leaves
        FOUR overlapping committed bookings, because A READ DOES NOT CONFLICT
        WITH ANOTHER TRANSACTION'S UNCOMMITTED WRITE — which is the shipped
        read-then-write shape and the reason the contract hands back a command
        rather than an answer. Five more controls: a RESIZE and a MOVE into an
        occupied interval (refused by the same constraint, accepted without it —
        the exclusion covers UPDATE, which an insert-time check does not); a
        CANCEL carrying a stale revision matching zero rows where the unguarded
        form cancels a booking whose interval it never saw; SHARED capacity,
        which no exclusion constraint expresses because a sum over concurrent
        rows is not a pairwise predicate, reaching peak 2 under a `FOR UPDATE` on
        the resource row and peak 3 without it; and a RECURRENCE whose fourth
        occurrence collides, leaving 0 rows materialised in one transaction and 3
        orphans when each occurrence autocommits. A rendezvous makes the
        interleaving decided rather than hoped for — without it control 1 was
        flaky in the direction that matters. And the deadlock the constraint
        sometimes throws instead of queueing (40P01) is classified as a REFUSAL
        and named, because nothing was booked and the caller must retry, which is
        what the idempotency key on the command is for. -->
  - [x] S9.5.f Reconcile resource/target/project dependencies, conflicts,
        notifications, publication/release state, and calendar projections.
        Evidence: integration tests.
        <!-- S9.5.f: `reconcileCalendar` reports six kinds — capacity lowered,
        resource withdrawn, blackout introduced, dependency cancelled, dependency
        moved past its dependant, publication window missed — each with the
        transition it IMPLIES and the roles to notify, and it PERFORMS NONE OF
        THEM. THE WORLD CHANGING UNDER A COMMITMENT PRODUCES `conflicted`, WHICH
        STILL OCCUPIES CAPACITY: a resource withdrawn does not cancel the
        reservations it breaks, because a production silently losing a booking is
        the failure this whole module is downstream of, and a state that stopped
        occupying would hand the interval to the next requester while the
        production that was already booked still believes it has it. Capacity is
        not compared across mismatched units here either. -->
  - [x] S9.5.g Apply tenant/object/property authorization and protect embargoed/
        confidential titles in free-busy views. Evidence: disclosure tests.
        <!-- S9.5.g: FREE-BUSY CANNOT CARRY A TITLE AND THE TYPE IS WHAT
        GUARANTEES IT — `FreeBusyInterval` has no field a title fits in, and a
        redaction applied at render time is one the next endpoint forgets; the
        endpoint that forgets it is always the one added for a partner
        integration. Another TENANT's slots are omitted ENTIRELY rather than
        shown as opaque busy blocks, because the pattern of when a shared studio
        is busy is itself information and a competitor reads a shoot schedule out
        of the shape of it. Within the tenant the disclosure class decides:
        `public` shows the ref, `internal` shows it to participants, schedulers
        and owners, `opaque` shows a busy interval and not even the resource to
        anybody who is not a participant — which is what an embargoed title
        needs, the ref being the handle a partner looks the title up by. And
        `mergeFreeBusy` merges ADJACENT blocks as well as overlapping ones, on
        purpose: three back-to-back half-hour reviews and one ninety-minute shoot
        must read the same, and leaving a zero-length gap between them is the
        same disclosure the merge exists to remove. -->
- [x] S9.6 Add recurrence, time-zone, daylight-saving, embargo, blackout, and
      dependency semantics. Evidence: calendar edge-case suite.
      <!-- S9.6 marked after writing and reading `libs/oshun/workbench-kit/src/
      calendar-recurrence.ts` (+ spec, 106 tests, 100% of statements, branches,
      functions and lines) and after FIXING all FOUR shipped local-time
      converters. THE VEIN: A LOCAL TIME IS NOT AN INSTANT, AND A FUNCTION FROM
      (DATE, TIME, ZONE) TO A SINGLE INSTANT WITH NO FAILURE MODE IS ANSWERING
      QUESTIONS THAT HAVE NO ANSWER AND QUESTIONS THAT HAVE TWO. THE GREP IS NOT
      A WORD, IT IS A RETURN TYPE: find every function whose result is one
      instant and whose input is a wall clock, and ask what it returns for the
      two hours a year the answer is not one instant. Those are the two hours a
      year when every alarm in the system is wrong, and they are the hours nobody
      writes a fixture for.
      MEASURED against tzdata 2026a. `libs/oshun/domain-tara/src/
      ritual-scheduling.ts` guessed the instant by reading the local time as UTC,
      formatting the guess back and subtracting the difference TWICE, with no
      convergence check and no failure mode — and it was wrong in BOTH
      DIRECTIONS. Asked for `2026-03-08 02:30` in `America/New_York`, a local
      time the clock SKIPS, it answered `06:30Z`, which reads 01:30 local: an
      hour early, silently. Asked for `2026-10-04 02:15` in
      `Australia/Lord_Howe`, whose spring transition is THIRTY MINUTES, it
      answered an instant reading 02:45: half an hour late, the opposite
      direction, for the same reason. Asked for `2026-11-01 01:30` in
      `America/New_York`, a local time that happens TWICE, it picked one of the
      two by accident of its initial guess and the caller could not request the
      other.
      The same shape shipped three more times, each subtly different, which is
      what makes it a vein rather than a bug.
      `libs/oshun/domain-veritas/src/retraction-ux/notification.ts` ran the
      identical loop FOUR times instead of two — more iterations do not make a
      gap have an answer.
      `apps/veritas/video/src/services/podcast/shows.ts` did ONE offset lookup
      and subtracted, which is wrong exactly on the day the offset changes, which
      is the day a weekly production schedule is computed across: measured, its
      answer for `2026-03-08 03:30` in `America/New_York` read 04:30.
      And `libs/arete/ai-coach/src/weekly-review.ts` took
      `toUtcFromLocal(localDateTime, timezoneOffsetMinutes)` — AN OFFSET IS NOT A
      ZONE. A stored offset is wrong for half the year in every zone that
      observes daylight saving, and the function had no way to find out, because
      the fact that would have told it was discarded at the boundary; its
      parameter is now an IANA zone and its result carries which one.
      All four now use the two-candidate method and report `unique`,
      `ambiguity-resolved` or `gap-shifted`. -->
  - [x] S9.6.a Define supported recurrence rule/version, start, count/until,
        intervals, weekdays/month rules, inclusions/exclusions, exception edits,
        and series/occurrence identity. Evidence: recurrence contract.
        <!-- S9.6.a: `RecurrenceRule` carries the version, frequency, interval,
        start, count-or-until, weekdays, month days, an explicit month-overflow
        policy, RDATEs and EXDATEs. `admitRecurrenceRule` REFUSES AN UNBOUNDED
        SERIES: it is legal in iCalendar and every consumer of one has to invent
        its own horizon, and no two of them invent the same one — a caller that
        genuinely wants forever says so with an `until` somebody had to type. It
        refuses a series bounded twice, and an unsupported rule VERSION rather
        than expanding it to the wrong dates. `monthOverflow` is on the rule
        because neither answer is right for every series: `skip` is iCalendar's
        and it is the one that surprises people — monthly-on-the-31st has SEVEN
        occurrences in 2026, not twelve — while `clamp` moves to the last day.
        AN OCCURRENCE'S IDENTITY IS ITS ORIGINAL START AND NEVER ITS CURRENT ONE:
        an exception keyed on where the occurrence IS cannot be found after it
        moves, so the move is applied again on the next expansion, and again, and
        the rehearsal walks forward an hour a day until somebody notices. The
        spec asserts the expansion is idempotent under a move, which is what that
        rule buys. -->
  - [x] S9.6.b Store source local date/time and IANA zone plus resolved instant/
        offset and tzdata version without relying on server locale. Evidence:
        temporal schema tests.
        <!-- S9.6.b: `ZonedDateTime` is `{localDate, localTime, zone}` — the
        SOURCE, what a person entered, and the only form that survives a tzdata
        update. `ResolvedInstant` is derived and carries the offset, the
        resolution kind and the TZDATA VERSION. That version is an INPUT: this
        module never reads `process.versions.tz`, because it is a contract, it
        runs in a browser, and a version read on the server is a claim about the
        wrong machine. `staleResolutions` re-resolves the source against the data
        in force now and reports the DRIFT — a zone whose rules changed did not
        change any stored instant, and the difference between the instant we
        stored and the instant the wall time now means is exactly the meeting
        somebody misses. `partsInZone` uses `hourCycle: 'h23'` rather than
        `hour12: false`, because the latter yields hour 24 for midnight in
        several locales and a 24 silently becomes the next day when fed back into
        `Date.UTC`; and the BC era is converted astronomically (`1 - year`, since
        there is no year zero) rather than by a sign flip that would be off by
        one for every date before the common era. -->
  - [x] S9.6.c Define nonexistent/ambiguous DST time choice, repeated/skipped
        occurrence, zone rule change, leap/day boundary, and all-day behavior.
        Evidence: DST edge fixtures.
        <!-- S9.6.c: `possibleInstantsFor` returns ZERO, ONE or TWO instants, and
        the size is the answer. The method: take the offsets a day either side,
        form one candidate per offset, and KEEP ONLY THE CANDIDATES THAT FORMAT
        BACK to the wall clock asked for — the step a fixed-point loop cannot
        take, because it has converged to something and has nothing to compare
        the something against. Fixtures: 0 for the New York hour gap AND for the
        thirty-minute Lord Howe gap; 2 for the New York repeated hour, for the
        thirty-minute Lord Howe one, and for `2018-11-04 00:30` in
        `America/Havana`, a zone that repeats MIDNIGHT. A NON-EXISTENT CALENDAR
        DATE IS A DIFFERENT FAULT FROM A NON-EXISTENT LOCAL TIME — `2026-02-29`
        is a typo and `2026-03-08 02:30` is a policy question, and answering "no
        instants" to both makes the wrong person responsible. AN ALL-DAY EVENT IS
        A DATE RANGE AND NEVER A MIDNIGHT INSTANT, because `Asia/Beirut` skipped
        midnight on 2023-03-26 and `America/Havana` has repeated it: the
        projection walks forward in quarter-hour steps (a transition can be
        thirty minutes, so an hourly probe steps over the gap it should report
        walking out of) and reports the wall time it actually used — 01:00 for
        Beirut. -->
  - [x] S9.6.d Define embargo start/end/zone, allowed prepublication actors,
        early release, extension/lift, clock source, target/channel/territory,
        and fail-closed state. Evidence: embargo tests.
        <!-- S9.6.d: AN EMBARGO FAILS CLOSED. No clock, an unresolvable lift
        time, a request from a territory nobody classified — all `indeterminate`
        with `mayPublish: false`, because the failure mode of the other choice is
        publishing an unannounced title and it is not recoverable. The lift
        resolves with `onAmbiguous: 'later'` and `onNonexistent: 'shift-forward'`
        and neither is arbitrary: on an ambiguous lift the embargo holds through
        BOTH readings of the repeated hour, and on a nonexistent one it holds
        until after the gap — AN EMBARGO RESOLVES TOWARDS STAYING IN FORCE. An
        early release is an explicit, ATTRIBUTED act checked before the clock, so
        a lift time that resolved oddly can never produce one; an extension moves
        the effective lift and keeps the original on the record. -->
  - [x] S9.6.e Define resource/tenant/global blackout type, interval/recurrence,
        capacity effect, priority, override authority, and conflict. Evidence:
        blackout tests.
        <!-- S9.6.e: A BLACKOUT SCOPE IS A LATTICE AND THE NARROWER SCOPE CANNOT
        WIDEN IT. An override is authorised against the scope that IMPOSED the
        blackout, so a resource owner declaring an override on their own resource
        cannot book through a platform maintenance window — and the refused
        override is REPORTED rather than silently ignored, because a booking
        refused for a reason the person believes they already handled is worse
        than one refused plainly. Blocking blackouts come back broadest scope
        first, `reduce-capacity` takes the LOWEST remaining capacity of every
        applicable reduction, and a blackout recurs on the same rule engine as
        everything else. The expansion window is WIDENED to the left by the
        blackout's duration: an occurrence that starts before the window and runs
        into it covers the window just as much as one starting inside it, and an
        expansion over the bare window never returns it — a maintenance shift
        that began an hour ago, invisible to the booking made now. -->
  - [x] S9.6.f Define finish/start, release/gate, asset/source, resource,
        approval, and cross-domain dependencies with lag/lead and unavailable
        state. Evidence: dependency scheduling tests.
        <!-- S9.6.f: four dependency types, seven subjects, lag and lead as one
        signed minute count. THE EARLIEST START DERIVED FROM AN UNSCHEDULED
        PREDECESSOR IS `null` AND NOT ZERO: a planner that reads unknown as zero
        takes the maximum over a set containing zero, gets the other constraints,
        and schedules a chain whose first link nobody has planned — today, in
        front of everything. An UNAVAILABLE predecessor (a withdrawn asset, a
        revoked approval) is unschedulable rather than late, because it does not
        become available by waiting. A violated constraint still reports the
        bound it violated, so the fix is visible. -->
  - [x] S9.6.g Materialize/query bounded windows deterministically and detect
        conflicts without infinite expansion or stale tzdata. Evidence:
        performance/property tests.
        <!-- S9.6.g: `expandSeries` takes a window AND a cap on both occurrences
        and rule STEPS — the step cap separately, because a rule whose every
        occurrence is excluded still has to stop and a cap counted on emitted
        occurrences would let it run forever. It reports `stoppedBecause` and
        `truncated`, because A SILENTLY CAPPED LIST READS AS A COMPLETE ONE, and
        that is how a conflict check passes over occurrences it never saw. THE
        RULE WALKS THE CALENDAR, NOT THE CLOCK: adding 24 hours to an instant is
        not tomorrow at the same time, it is the next day at a different time
        twice a year, so each occurrence's wall clock is resolved on its own and
        the spec pins the offset changing from -300 to -240 across the
        transition while the local time stays 09:00. COUNT COUNTS WHAT THE RULE
        PRODUCED, before the window and before the exclusions — counting after
        exclusions would REFILL them, so a series somebody shortened by one would
        end a day later than it did before, the opposite of what they asked for.
        Determinism is asserted directly (two expansions agree, and the order is
        the exported `compareOccurrences`), and staleness is `staleResolutions`
        from S9.6.b. -->
- [x] S9.7 Add drag and non-drag scheduling with server conflict checks.
      Evidence: concurrent reservation test.
      <!-- S9.7 marked after writing and reading `libs/oshun/workbench-kit/src/
      scheduling-board.ts` (+ spec, 193 tests, 100% of statements, branches,
      functions and lines), `apps/oshun/web/src/components/workbench/
      SchedulingBoard.tsx` (+ module CSS, + 25 jsdom tests) and
      `apps/oshun/web/tools/scheduling-board-browser.mjs` (54 controls in a real
      Chromium), and after fixing the shipped scheduler the item is about.

      THE VEIN: FIND THE GESTURE HANDLER THAT COMPUTES THE NEW VALUE, THEN FIND
      THE REQUEST IT MAKES. WHERE THE HANDLER'S OWN CALLBACK IS THE WRITE, THE
      DROP WAS THE DECISION AND THE SERVER WAS NEVER ASKED. The grep is `onDrop`
      / `onDragEnd` / `endDrag`, and the second grep — in the same file — is
      `revision`, `idempot`, `fetch`, `await`. Four hits and zero hits is the
      shape, and `libs/yemaya/ui/src/components/SchedulingEngine/
      DragDropSchedule.tsx` is exactly it: `endDrag` computed new dates and
      called `onTaskMove?.(id, start, end)`, with no request, no expected
      revision, no idempotency key and no question asked of anything that knows
      what else is booked. Its one validator, `validateDrop`, asked whether the
      GESTURE was enabled and then `return true`, so it was a deny-list of four
      cases over an open world.

      Reading the rest of that file with the question "what does this gesture
      actually produce" found six more, which is what makes it a vein rather
      than a bug. A RESIZE WAS COMPUTED AS A MOVE, because `calculateNewDates`
      never read `dragData.operation` — it returned the drop date plus the
      original duration for every one of them, so `onTaskResize` was handed a
      task of UNCHANGED duration at a new start, and the one thing a resize
      exists to do was the one thing that path could not do. THE POINTER WAS NOT
      AN INPUT: that function took `_pixelsPerDay` and never read it, its one
      caller passed two arguments, and `calculateDateOffset` — the only function
      that turns pixels into time, and the only one with a snapping test — was
      called by nothing, so how far somebody dragged had no effect on the result
      at all. EVERY SUB-DAY BOOKING BECAME A DAY, because the duration was
      `Math.max(1, Math.ceil(ms / 86400000))` DAYS re-applied through `setDate`:
      a sixty-minute review dragged once was a one-day booking of the room, and
      the test pinned the whole-day case by re-deriving the duration with the
      same `Math.ceil`, so it passed straight over the rounding. `setDate` WALKS
      THE LOCAL CALENDAR, so the same arithmetic yielded 23 or 25 hours across a
      transition — S9.6's vein one surface up, with the duration preserved in
      neither wall clock nor elapsed time because nobody had chosen. THE
      PROVIDER WAS DECORATIVE AND THE DROP WAS A NO-OP: `DraggableTask`,
      `DropZone`, `DragPreview` and `SortableTaskList` each called
      `useDragDrop()` rather than `useDragDropContext()`, so each got its own
      `useState` and every drop zone's `dragData` was permanently null —
      `isValidDropTarget` answered false for every target and rendered EVERY
      zone in the invalid style during a drag, `DragPreview` returned null, and
      `onDrop`'s `setTimeout(() => endDrag(), 0)` ran an `endDrag` closed over
      the pre-drop state that hit its own early return having called
      `onDragEnd(null)`. And `dataTransfer.getData()` RETURNS THE EMPTY STRING
      DURING `dragover` in every browser, so the validation inside `onDragOver`
      never ran and `dropEffect` was never set. There was also no keyboard path
      of any kind.

      All eight are fixed in place, and the module is pointed at as canonical
      (`scope:yemaya` may not depend on `scope:oshun`, so it cannot compose it
      — same boundary S9.6 hit for `scope:arete`). The drop is now a PROPOSAL:
      `onProposeMove` returns a decision and the callbacks run only after one is
      accepted; with nothing wired to decide, the drop is REFUSED with
      `NO_DECIDER_REASON` rather than performed. Four of that file's own tests
      asserted the defect and are rewritten.

      FIFTEEN DECISIONS, and the three the browser settled are worth the space.
      A GESTURE IS A PROPOSAL, and `committed` is the literal type `false` while
      the local read is `authority: 'local-snapshot'`, because the client's
      vocabulary has no success word — a local evaluation is `refused` or
      `no-local-objection`, never `accepted`. A DURATION IS PRESERVED IN ONE OF
      TWO INVARIANTS AND THE CALLER MUST SAY WHICH: measured on tzdata 2026a, a
      three-hour booking moved onto `2026-03-08` in `America/New_York` ends at
      04:00 under `wall-clock` and 05:00 under `elapsed`, and a system with only
      one is wrong for half its bookings. POSITION IS ARITHMETIC IN THE BOARD'S
      ZONE AND DURATION IS ARITHMETIC IN THE SLOT'S, because the person pointed
      at a grid drawn in one zone and the booking belongs to a resource living
      in another. SNAPPING BELONGS TO THE POINTER PARSER AND NOT TO THE
      PLACEMENT, so a keyboard command means exactly what it says and both paths
      reach one `proposePlacement` through one door — which makes non-drag
      parity a theorem rather than two tests that agree today; and it is
      measured from LOCAL MIDNIGHT, since a fifteen-minute grid computed on
      epoch milliseconds lands off the hour in every half-hour zone. A SNAP CAN
      LAND IN A GAP AND THE ONLY HONEST POLICY FOR A GESTURE IS `reject`. A
      MOVE IS A RE-COMMITMENT and is evaluated from the state it will return to,
      while `conflicted` and `in-progress` stay refused for S9.5's reasons. A
      MOVE BETWEEN RESOURCES IS TWO CAPACITY CHANGES, so both revisions travel.
      A CONFLICT IS PROJECTED THROUGH THE READER'S DISCLOSURE CLASS AND THE
      COUNT IS PART OF THE PROJECTION. AN UNKNOWN OUTCOME IS RESOLVED BY
      REPLAYING THE SAME KEY, NEVER BY READING, and may not be rendered as a
      failure. AND UNDO IS A COMPENSATION AND SOMETIMES A REQUEST.

      THE BROWSER FOUND A REAL DEFECT IN THE KIT, and it is the sharpest thing
      here: `mintIdempotencyKey` carried the INTENT KIND, and a 15px drag and
      one ArrowRight land on the identical interval on the identical lane — so
      they minted `k=move-to` and `k=move-by`, two keys, and a double-tap or a
      slow drag followed by a nudge was two bookings of one interval with
      idempotency protecting neither. THE KEY NAMES THE RESULT BEING ASKED FOR
      AND NEVER THE GESTURE THAT ASKED; it is keyed on the placement (booked AND
      occupied, since a changeover change is a different write) plus the
      expected revisions, which is also what makes the NEXT request distinct
      without a counter. Nothing in jsdom could have found it: the two paths
      have to reach the same pixel for the keys to be comparable at all.

      Both dist-patch directions verified: silencing
      `gesture-without-a-keyboard-command` and silencing the move arm of
      `move-changed-the-duration` in the BUILT `dist/.../scheduling-board.js`
      each failed a control; restoring returned 54/54.

      TRAPS. The gate `packed-consumer-smoke` was RED on twelve problems before
      this change and is red on six after: five declared subpaths did not import
      because the dist `package.json` was stale (a manual `tsc --outDir` bypass
      does not copy it — fixed by copying it), and `Object.freeze` AT MODULE
      SCOPE is work a bundler may drop under `"sideEffects": false`. This module
      writes its constant tables as literals with `readonly` types instead, so
      it is off that list; `calendar-recurrence`, `editorial-calendar`,
      `localized-labels`, `registry-lifecycle`, `registry-merge` and
      `taxonomy-impact` are still on it and several of them build `Set`s and
      `Map`s at module scope, which needs restructuring rather than unwrapping.
      `production-scan` is GREEN again (it was red on a name collision through
      2026-08-10). A v8 two-element `Array.sort` calls its comparator ONCE, so
      the `||` tie-break in ten inline comparators was unreachable — exported as
      `compareFindings` and tested directly, which is also the right shape for a
      surface merging findings from several finders. And the board never moves a
      booking itself, so repeated key presses do NOT accumulate: every press is
      computed from the same unchanged record, which is the claim under test and
      also the reason the browser fixture has to place its New York booking at
      01:45 to reach 02:00 in one step. -->
  - [x] S9.7.a Load resource/calendar capacity, tentative/committed slots,
        dependencies, blackouts, embargo, permissions, version, and current time
        from server. Evidence: scheduling view tests.
        <!-- S9.7.a: THE LOAD-BEARING FIELD IS THE ORIGIN OF THE CLAIM, NOT THE
        NUMBER (S7.3's shape, applied to a clock and to a permission).
        `SchedulingView` carries `clockOrigin`, of which exactly two of four are
        in `AUTHORITATIVE_CLOCK_ORIGINS`, and `permissions.origin`, of which
        `derived-from-payload-shape` is a fault — which fields an endpoint chose
        to project is not a statement about what this person may do. `viewClock`
        advances the server's reading by the time the client has observed since
        receipt and NEVER runs backwards past it, because a machine that resumed
        from sleep is wrong by hours in the direction that shows a lapsed hold
        as live. `ViewCoverage` is per class of thing, because a board drawn
        over an interval wider than what was fetched renders the remainder as
        FREE, and free is the one thing an unanswered interval is not.
        `loadedStates` makes the same point about the other axis: a board that
        draws only commitments shows held space as available and sends every
        requester into a refusal. An absent embargo on a protected slot is a
        fault rather than a permission (S9.6.d). And `gesture-only-capability`
        enforces S9.7.c at the point the permissions ARRIVE rather than at the
        point somebody notices they cannot Tab to anything. -->
  - [x] S9.7.b Implement pointer/touch drag/resize preview with snapping, time
        zone/DST, target resource, duration, dependency, and impact; do not
        commit optimistically. Evidence: drag tests.
        <!-- S9.7.b: the preview's `committed` is the literal type `false` and
        the impact's `authority` is the literal `'local-snapshot'`, so no
        surface can widen either; the React component renders both into the DOM
        and the browser tool reads them back. `previewGesture` is
        `proposePlacement(gestureToIntent(...))` — the snap lives in the parser
        and the placement never rounds. A resize moves exactly one edge
        STRUCTURALLY (the other is copied from the original, not recomputed),
        and `gestureAuditFaults` is the finder that catches the other
        arrangement over a recorded before/after — handed what
        `calculateNewDates` produces it reports `resize-changed-both-edges`,
        `move-changed-the-duration` and `setup-teardown-lost`. Setup and
        teardown keep their original lengths through a move. A snapped edge that
        names no instant is REFUSED, never shifted, and the browser drives that
        case for real on 2026-03-08 in New York and spends no request on it. The
        real drag is `page.mouse.down/move/up` over a captured pointer, and the
        interval the command carries is recomputed by the BUILT kit from the
        pixel distance rather than copied into the assertion. -->
  - [x] S9.7.c Provide keyboard move/resize/target controls and explicit edit
        dialog with exact date/time/zone/duration/resource as non-drag parity.
        Evidence: accessible alternatives.
        <!-- S9.7.c: parity is STRUCTURAL — one `proposePlacement`, reached by
        the pointer through `gestureToIntent` and by the keyboard through an
        exact intent — and the spec pins it by serializing two placements made
        under different modalities and comparing them character for character.
        `nonDragParityFaults` checks over EDGES rather than names, because a
        command list that can nudge a start and an end but never move the whole
        booking has the same gap as one missing a key and comparing labels would
        not find it. `zone` is on `EDIT_DIALOG_FIELDS` because a date and a time
        without one are not an instant, and a dialog that omits it silently
        means "the board's zone", which is not where most bookings live. A field
        the principal may not write renders as a DIFFERENT ELEMENT rather than a
        disabled input (S7.8), asserted in jsdom by tag name. The browser walks
        Tab through every booking and every resize handle and out the far side.
        -->
  - [x] S9.7.d Send expected calendar/resource/slot revisions and idempotency
        key to an atomic server capacity/conflict check and reservation.
        Evidence: command contract.
        <!-- S9.7.d: `buildSchedulingCommand` composes S9.5's
        `evaluateReservation` rather than restating it, and uses the result in
        ONE direction — to refuse. A local pass is `no-local-objection`, never
        `accepted`, because the snapshot is old by the time it is read and the
        world can only have got more booked since. A move between lanes carries
        BOTH resource revisions, because the source frees capacity and the
        target consumes it and one expected revision covers one of them. The key
        is minted per REQUEST and contains the expected revisions, which is what
        makes a retry free and the next request distinct without a counter — and
        a real browser proved it must not contain the intent kind. `attempt` is
        deliberately outside the key; `retryCommand` advances it and
        `attemptTranscriptFaults` audits an observed sequence for a retry that
        minted a new key, a board that moved before an answer, and a read issued
        after a timeout. -->
  - [x] S9.7.e Return conflicting slots/resources/dependencies/blackouts/
        embargo/capacity with protected safe detail and alternatives where
        permitted. Evidence: conflict fixtures.
        <!-- S9.7.e: THE COUNT IS PART OF THE PROJECTION. "Three conflicts" over
        an opaque calendar measures how busy somebody else is, and two such
        readings difference back to a single row — so an opaque conflict set is
        ONE statement with no count and no ref, an internal one is a count with
        no ref, and only a wholly public one names `titleSafeRef`s. A
        conflicting slot the board never loaded is treated as opaque rather than
        as absent, and one opaque member makes a mixed set opaque. Every
        `ReservationFaultKind` maps to a conflict source and a reader-facing
        noun built here rather than written by a surface. An alternative is an
        OFFER, so `suggestAlternatives` searches only lanes the reader may BOOK
        rather than merely see, inside the window the board fetched, clear of
        the blackouts it just drew; `alternativeFaults` audits offers whoever
        produced them, and the browser drives the see-only case through the
        built kit as a negative control. -->
  - [x] S9.7.f Handle concurrent accept/reject, stale display, tentative expiry,
        permission change, partial bulk, offline, retry, and unknown outcome.
        Evidence: race/fault tests.
        <!-- S9.7.f: `OUTCOME_PRECEDENCE` puts a refusal nobody can fix by
        editing first, then the ones a re-read changes, then the ones an edit
        changes — S7.2 reached the same order for route outcomes for the same
        reason, and `strongestRefusal` is one function so a single item and a
        batch agree. `unknown` is the one every scheduler gets wrong: the
        reservation may exist, so `mayReportFailure` is false, the next step is
        `replay-the-same-key`, and there is no branch anywhere that reads to
        find out. The browser drives a thrown transport, checks the retry
        carries the same key with the next attempt number, and counts ZERO
        network requests in between. `bulkSummary` is BUILT rather than checked
        (S7.14): "1 of 3 moved, 2 did not" names both numbers, and an unknown
        item is spoken as "may or may not have". An offline entry carries its
        expected revisions and an expiry derived from the server's own staleness
        budget — not for safety, which the revisions already give, but because a
        queue that fires two hundred refusals when a laptop wakes up has taught
        nobody anything. -->
  - [x] S9.7.g Restore focus/selection and announce accepted/refused/conflict
        result with undo/compensating behavior where supported. Evidence:
        accessible journey.
        <!-- S9.7.g: focus is restored in an EFFECT keyed on the submission
        rather than in the handler that produced it, because the element the id
        names is re-rendered by the same commit and `focus()` on a node not yet
        in the document moves focus to the body and reports success (S7.12); the
        browser asserts `document.activeElement` is the live booking and not the
        body, after a dismissal and after an answer. `restorationFaults` takes
        the LIVE element and slot ids as arguments so the same finder works over
        a browser transcript and over component state, and it does not blame a
        refusal for dropping a slot that left the board. THE ANNOUNCEMENT NAMES
        THE RESULTING TIME rather than saying "moved" — a reader who cannot see
        the board learns nothing from "moved", and that is the whole reason the
        live region exists; a refusal is assertive because the reader is about
        to act on a board that did not change. UNDO IS A COMPENSATION AND
        SOMETIMES A REQUEST: giving something back always works and taking
        something back does not, so undoing a move is `re-request-the-original`
        with `guaranteed: false` and a label that says somebody may have taken
        it, rather than an inverse the system cannot perform. Work that has
        started is not undoable at all. -->

      <!-- S9.7 FRONTIER NOTE. The next unchecked parent is S9.8 (capacity
      resources and reservation units as domain plugins), whose evidence clause
      is GPU, reviewer, DCC host, editorial and distribution examples sharing
      one reservation engine without shared code. -->

- [x] S9.8 Add capacity resources and reservation units as domain plugins.
      Evidence: GPU, reviewer, DCC host, editorial, and distribution examples.
      <!-- S9.8: shipped as `libs/oshun/workbench-kit/src/capacity-plugins.ts`
      (+ spec, 183 tests, 100% of statements/branches/functions/lines) and
      `libs/oshun/workbench-kit/tools/co-allocation-race.mjs` (nx target
      `co-allocation-race`, real PostgreSQL). 147 kit entry points.

      THE VEIN, STATED SO IT TRANSFERS: FIND THE NUMBER THE REQUEST DECLARES AND
      THE LIMIT THE RESOURCE DECLARES, THEN FIND THE SUBTRACTION BETWEEN THEM.
      WHERE THE LIMIT IS APPLIED TO THE RECORD INSTEAD OF TO THE DECISION -- A
      CLAMP, A SCORE, A REPORT -- THE RESOURCE IS OVER-COMMITTED IN EXACTLY THAT
      UNIT, AND THE DETECTOR THAT WOULD HAVE CAUGHT IT IS OFTEN UNREACHABLE BY
      CONSTRUCTION. The grep is mechanical: take the requirement type field by
      field, and for each field find the member of the LOAD type that spends it.
      A field with no member is a unit the scheduler cannot run out of.

      `libs/metis/multimedia/src/video/gpu-worker-pool.ts` is the sharpest
      instance, because the clamp and the detector are eleven lines apart. A job
      carries `estimatedMemoryMB` and the pool carries `memoryLimitMB`;
      `processNext` assigned any job to any idle worker without asking about
      memory at all, and then stored
      `Math.min(this.config.memoryLimitMB, Math.max(0, Math.round(value)))`. The
      limit was applied to the number being STORED, so `healthCheck`'s
      `worker.memoryUsageMB > this.config.memoryLimitMB` -- the only comparison
      of the two in the file -- COULD NEVER BE TRUE for any value that reached
      it. A 1024 MB composition on a 256 MB pool was admitted, recorded as 256
      and reported healthy, and the file's own test pinned it:
      `it('clamps an over-budget estimate to the memory limit')`. Four more
      followed from one root cause, that the pool never retained the reservation
      it granted: `processNext` returned the in-flight job and kept nothing, so
      `stopWorker` searched the queue and the completed list, found the running
      job in neither and DROPPED it; `completeJob` and `restartWorker` rebuilt a
      replacement with `compositionId: ''`, a fresh `createdAt` and a priority of
      1, so what came back was not the job that ran and a restarted job lost its
      place in the queue; and `getJob` could not find a rendering job at all.

      `libs/oshun/render-farm` is the same shape in the field a scheduler is
      most likely to declare and least likely to spend.
      `RenderResourceRequirements` carries `licenses?: Record<string, number>`
      and `RenderWorkerLoad` has no member for seats, so `capabilitiesSatisfy`
      asked about the renderer, the cores, the memory, the storage, the features
      and the GPU and never about a licence: a task declaring `{ arnold: 1 }`
      was dispatched whatever the site's licence server had left. The ledger
      exists one file over, and `advanced.ts::selectPlacement` consulted it with
      `licenses.available(feature) < seats` and never called `acquire` -- S9.5's
      vein one layer up, an availability check that is not a reservation. Its
      `gpuCompatible` never subtracted `worker.load.gpuCount` although the
      sibling `gpuSatisfies` did, so placement put work on machines whose cards
      were all busy. Its ranking added a region flag worth 100, the card's VRAM
      in gibibytes and a task count times twenty into one `score`, so a bigger
      card outvoted the requester's own region and did so even when the request
      needed no GPU. `evaluateLimit` projected `concurrentRunningTasks` as the
      CURRENT value while `maxQueuedJobs` two branches below projected
      `current + 1`, so the one limit that exists to bound concurrency could
      never be exceeded by the submission it was evaluating. And a fractional
      GPU count reached `normalizePositiveInteger`, where `Math.trunc(0.5)` is 0
      and the guard threw "Expected a positive integer" from whichever load
      calculation ran first.

      All eleven are fixed in place with tests, and every fix was
      mutation-tested: reverting the admission check, the licence acquisition,
      the concurrency projection, the rider draw-down and the staleness
      degradation each turned a test red.

      THE DESIGN, IN THIRTEEN DECISIONS. A RESOURCE HAS SEVERAL CAPACITY
      DIMENSIONS AND EXACTLY ONE OF THEM BEARS THE BOOKING: the bearing
      dimension is the calendar row and carries the exclusion key, the expected
      revisions, the hold and the idempotency key, and every other dimension is
      a RIDER on that row -- VRAM, licence seats, focus minutes, egress. Riders
      are refusals rather than warnings, and they are exactly what the shipped
      schedulers forget, because the bearing one is the only dimension with a
      state machine to remind them. Sharing is a property of the ROW and never
      of a rider, which a synthetic sixth plugin found during the build.
      A DIMENSION THE RESOURCE DOES NOT DECLARE IS A REFUSAL AND NEVER A ZERO.
      THE ID SAYS WHICH POOL, THE UNIT SAYS WHAT THE NUMBER MEANS, AND BOTH
      TRAVEL, because the factor between gib and gb is 1.073741824 and the
      factor between a claim and a focus-minute does not exist. A LEVEL IS DRAWN
      DOWN AT AN INSTANT AND A RATE IS DRAWN DOWN OVER A WINDOW: levels reuse
      S9.5's peak-concurrent sweep, rates use the exact maximum of a sliding
      integral over a piecewise-constant density, and a rate does not convert
      across periods either, since a daily budget can be spent before lunch.
      A FRACTION IS EXPRESSIBLE OR IT IS REFUSED AND NEVER ROUNDED. HEALTH IS
      OBSERVED RATHER THAN DECLARED AND A DEAD RESOURCE LOOKS EXACTLY LIKE AN
      IDLE ONE. AN INTERVAL THE SNAPSHOT DOES NOT COVER IS UNKNOWN, NOT FREE. A
      LIFECYCLE STATE SAYS WHAT MAY BE BOOKED NEXT AND NEVER WHAT IS ALREADY
      BOOKED. A QUOTA IS CHARGED TO THE REQUESTER AND CAPACITY TO THE RESOURCE,
      so they are different refusals with different remedies, and every fault
      carries a remediation class because a refusal without one is a dead end
      whatever its prose says. MONEY IS AN INTEGER IN MINOR UNITS AND A CURRENCY.
      PREEMPTION IS A PLAN AND NOT AN ACT. A RESERVATION THAT SPANS RESOURCES IS
      ALL-OR-NOTHING AND ORDERED. AND THE ENGINE HAS NO BRANCH ON A PLUGIN. -->
  - [x] S9.8.a Define plugin-owned resource type/ID/version, tenant/scope,
        capabilities/constraints, calendar/availability, capacity units,
        location/zone, cost, health, and lifecycle. Evidence: resource contract.
        <!-- S9.8.a: `PluginResource` carries all ten, and the two that decide
        everything else are the ones a declaration is most likely to get wrong.
        `admitPluginResource` refuses a resource with NO bearing dimension (no
        calendar row, so nothing carries its exclusion key or its revision) and
        one with TWO (two rows for one booking, and the second is the one nobody
        cancels). Capabilities are TAGGED values rather than strings, because the
        three comparisons a scheduler makes are three different orderings:
        `550.54.14` and `550.9` order one way as versions, another as text and a
        third as floats, and only one of those admits a host that can run the
        job. A number carries its unit and gib-against-gb is
        `attribute-unit-mismatch` rather than a 7.4 per cent error. An attribute
        the resource does not DECLARE is unsatisfied, never compatible, because
        absence is the state a fleet acquires the moment somebody adds a field
        to the inventory. `observedHealth` degrades a stale `healthy` to
        `unknown` against the clock, and a reading from the future is unknown
        too -- a host that stops answering has no running jobs, no memory in use
        and no errors, so every metric says idle. `LIFECYCLE_ADMITS_NEW_WORK` is
        a total table, and `draining` is the row worth stating twice: it accepts
        nothing new and keeps everything it has, and folding it into `retired`
        cancels the work draining exists to let finish. -->
  - [x] S9.8.b Define reservation request units, amount/rate, interval,
        exclusivity/share, setup/teardown, priority, preemption, quota, cost,
        and result. Evidence: reservation contract.
        <!-- S9.8.b: `PluginReservationRequest` names all ten and the interval
        it occupies is the booked one extended OUTWARDS by setup and teardown
        (S9.5 decision 7), which is also what cost is charged over: a changeover
        is time the resource cannot sell to anybody else, and a farm billing
        only the booked interval gives it away. `peakWindowedDemand` is the rate
        arithmetic and it is exact rather than sampled -- the integral of a
        piecewise-constant density over a sliding window is piecewise LINEAR in
        the window's start, with breakpoints only at the density's own and at
        those shifted back by one period, so the maximum is attained at one of
        those or at an end of the range and the function enumerates exactly that
        set. A quota is the same arithmetic charged to the REQUESTER instead of
        the resource, which is why it is a different fault with a different
        remedy: waiting fixes a full resource and never fixes an exhausted
        allocation. `estimateReservationCost` returns one total PER CURRENCY and
        there is no field a cross-currency sum could be put in, and every line is
        an integer of minor units, because money held as a float acquires a
        rounding error per line and reconciles against nothing. -->
  - [x] S9.8.c Define availability/capacity calculation, tentative hold,
        commitment, release, resize, move, expiry, failure, and reconciliation
        interfaces. Evidence: plugin lifecycle tests.
        <!-- S9.8.c: `calculateAvailability` answers `open`, `closed` or
        `unknown` rather than a number, because the failure it exists to stop is
        a surface drawing free space over an interval nobody answered for: a
        window past the snapshot's coverage is unknown, a stale health reading is
        unknown, and only `open` may be drawn as space. `planRelease` returns
        capacity from NOW rather than from the booked start, since a job
        cancelled halfway through has already consumed the first half, and a
        release at or after the end frees nothing and says so instead of
        reporting a zero-length interval that every overlap check passes.
        `planResize` moves exactly one edge STRUCTURALLY -- the other is copied
        -- and produces a REQUEST rather than a mutation, because on a rate
        dimension the same amount in a shorter interval is a HIGHER draw, so
        even a shrink has to be asked. `planMove` carries both revisions and
        refuses a target that cannot account for a rider rather than dropping
        it. `planFailure` splits three facts everybody folds together: a run
        that failed is over and its capacity returns; a resource that went
        unreachable has said nothing about the work, so the capacity stays
        claimed and somebody reconciles; and an unknown outcome may not be
        reported as a failure at all, because the reservation may exist and the
        next step is to replay the same key rather than to read and guess
        (S9.7.f). `reconcileResource` reports eight kinds of drift and performs
        none of them. -->
  - [x] S9.8.d Implement GPU example with model/VRAM/compute/region/driver,
        fractional/exclusive capacity, quota, health, and cost. Evidence: GPU
        fixtures.
        <!-- S9.8.d: three pools, because a card is three things at once -- a
        DEVICE somebody may hold exclusively, seven COMPUTE SLICES and eighty
        gibibytes of VRAM. A scheduler with only the first oversubscribes memory
        the moment two jobs share a card, which is what `RenderWorkerLoad` (a
        `gpuCount` and nothing else) does. FRACTIONAL GPU IS NOT AN ARBITRARY
        FRACTION: `MIG_PROFILES` is the A100-80GB table, seven compute slices and
        eight ten-gibibyte memory slices, and the spec checks the arithmetic
        invariant rather than the names (no profile's slices times its instance
        count exceeds 7, none's memory exceeds 80). Asking for 0.5 of a card is
        not a smaller request, it is one the device cannot express, and a MIG
        instance takes a WHOLE device from the device pool plus part of that
        card's slices and memory -- charging it a fraction of a device would let
        two instances of two profiles be placed as though they were on different
        cards. `CUDA_MINIMUM_DRIVER` makes "newer driver" a comparison that can
        be made, and the spec pins that `550.54.14 > 550.9` fails under both text
        and float comparison. -->
  - [x] S9.8.e Implement reviewer example with qualification/role/working hours/
        load/COI, count/effort units, delegation, and privacy. Evidence:
        reviewer fixtures.
        <!-- S9.8.e: TWO pools, because a queue that counts only claims treats a
        four-hour appeal and a five-minute triage as the same load --
        `libs/contracts/concordia`'s `maxConcurrentClaims` is the first pool and
        there is no second one. `reviewer.effort-minute` is a RATE over a day,
        because attention is spent and does not come back when the item closes,
        and the spec drives the case a claim count cannot see: a second
        four-hour appeal that fits three concurrent claims and does not fit a
        300-minute day. `qualificationsAt` is a function of the CLOCK, because an
        expired certification is not a qualification and a resource whose
        attributes were built once at registration is a reviewer who stays
        qualified forever. `withinWorkingHours` walks the interval in the
        REVIEWER's zone rather than sampling its ends, and the spec drives a
        16:30-18:30 booking against a window that closes at 17:00. A LAPSED
        CONFLICT IS STILL A CONFLICT INSIDE THE COOLING-OFF PERIOD, so filtering
        on `endedAtMs === null` clears somebody who left the company last week.
        DELEGATION MOVES THE CLAIM AND NEVER THE QUALIFICATION: the delegate's
        own qualifications and own conflicts are re-checked, because the
        alternative is a decision signed by somebody who was not allowed to make
        it, discovered at audit. And WHY a reviewer is unavailable is projected
        through the reader's class: "at capacity" is ordinary, "conflict of
        interest" says this person has a financial or family relationship with
        the party under review, and that is employment data wearing a scheduling
        word. -->
  - [x] S9.8.f Implement DCC host example with application/version/plugin/
        license/GPU/session/exclusive slot, liveness, and maintenance. Evidence:
        DCC fixtures.
        <!-- S9.8.f: THE LICENCE IS NOT A PROPERTY OF THE HOST. A floating seat
        lives on the licence server, is shared by every host in the building, and
        is therefore its own resource with its own revision -- modelling it as a
        field on the worker is exactly what lets two hosts hand out one seat, and
        it is why `coAllocate` exists. `dcc.gpu-device` is deliberately not
        `gpu.device`: the same card counted in two pools is a card that can be
        handed out twice. The session is exclusive and the host offers one,
        because an artist opening Maya on a box somebody else is using is not a
        capacity question. `sessionLiveness` never releases on silence, in any of
        its three verdicts: an artist on a flaky VPN, a host whose agent crashed
        while Maya kept running, and a machine that was unplugged look identical
        from here, and two of the three still have somebody's unsaved work in
        them. Maintenance is a blackout with a window rather than a deletion, and
        a window declared over existing commitments is a reconciliation finding.
        -->
  - [x] S9.8.g Implement editorial and distribution examples with slot/
        throughput/channel/territory/embargo/deadline/blackout semantics.
        Evidence: domain fixtures.
        <!-- S9.8.g: the editorial desk separates what it can HOLD from what it
        can PUT OUT -- six pieces in production and four a day through subbing
        and legal are different numbers, and a model with only the first
        schedules a week's output for Tuesday. A DEADLINE IS A PROPERTY OF THE
        REQUEST AND NOT A REFUSAL BY THE RESOURCE: the desk has not run out of
        anything, so reporting it as a capacity fault sends the requester to look
        for another desk, and `deadlineFinding` is separate from the decision
        with a `change-the-request` remedy. Distribution needs three pools, and
        egress shows why granularity matters -- 0.1 Mbps steps, and an offer that
        is not a whole number of them is capacity the channel advertises and
        cannot hand out. `distributionGate` composes S9.6's `decideEmbargo`
        rather than restating it, so the fail-closed behaviour survives: an
        unclassified territory and a lift time that will not resolve are both
        indeterminate, and indeterminate is never a yes. The blackout is checked
        SECOND and independently, because a released title still may not go out
        during a territory blackout and folding the two answers together loses
        which one refused. -->
  - [x] S9.8.h Prove plugins share reservation engine without shared code
        assuming one unit or silently converting incompatible capacities.
        Evidence: cross-resource isolation tests.
        <!-- S9.8.h: proved four ways. STRUCTURALLY, every filter in the module
        is by `(resourceId, dimensionId)`, and the spec asserts that a GPU
        decision is byte-identical whether or not a reviewer's effort and a
        desk's throughput are sitting in the same draw array.
        `pluginRegistryFaults` catches the three ways two plugins can collide,
        and the subtlest is `unit-used-with-two-meanings`: two plugins may both
        count in `minute`, but one metering it as a rate over a day and another
        as a level makes the same word mean "how many at once" in one place and
        "how many per day" in another, and the first person to write a shared
        report will add them. `templateConformanceFaults` keeps a resource from
        renaming its own units out of comparability with the rest of its type.
        And the proof that the ENGINE has no branch on a plugin is a SIXTH
        plugin that exists only in the spec -- a ceramics kiln with chambers and
        fuel therms -- decided end to end by the same functions, and refused by
        them for the same reasons and in its own unit. The fifth way is the one
        no in-process suite can do: `tools/co-allocation-race.mjs` races four
        contenders for one DCC session AND one licence seat against a real
        PostgreSQL 16.14, and its five controls each reproduce a shipped fault
        before the claim is measured against them -- THE RIDER IS NOT THE ROW
        (with only the bearing exclusion, four sessions commit against a pool of
        one seat and the seat pool is never touched), A CLAMP IS NOT A REFUSAL (a
        demand of 8 against an offer of 1 is admitted and stored as exactly 1, so
        the health check that compares them can never fire), A PARTIAL
        ACQUISITION IS WORSE THAN A REFUSAL (per-leg transactions leave three
        contenders holding hosts they cannot use), ORDER DECIDES DEADLOCK, and
        UNITS DO NOT ADD (a seat-hour draw against a seat pool is refused by the
        storage layer, because a schema that stores a bare number is one that
        adds them). Both dist-patch directions verified: moving the DCC bearing
        flag in the BUILT `dist/.../capacity-plugins.js` turned it red on two
        findings, and restoring it returned it to green. -->

      <!-- S9.9 FRONTIER NOTE. The next unchecked parent is S9.10 (prohibit
      direct production-catalog writes), whose evidence clause is that all
      changes pass through reviewed versioned bundles -- which is S9.9's bundle
      made mandatory, so the first question is which write paths exist that do
      not go through one. -->

- [x] S9.9 Add collection, relation, feature, rank, eligibility, and audience
      curation primitives. Evidence: deterministic curation bundle.
      <!-- S9.9: shipped as `libs/oshun/workbench-kit/src/curation-bundles.ts`
      (+ spec, 192 tests, 100% of statements/branches/functions) and
      `libs/oshun/workbench-kit/tools/curation-determinism.mjs` (nx target
      `curation-determinism`, child processes under five locale/zone
      combinations). 148 kit entry points.

      THE VEIN, STATED SO IT TRANSFERS: A CURATED ORDER IS A DECISION ABOUT
      MEMBERSHIP AND NOT DECORATION. WHEREVER A SORT'S COMPARATOR CAN RETURN
      ZERO FOR TWO DISTINCT ITEMS AND A LIMIT IS APPLIED DOWNSTREAM OF IT, THE
      SET OF THINGS PEOPLE SEE IS DECIDED BY THE ORDER THE ROWS HAPPENED TO
      ARRIVE IN. The grep is mechanical and it is two greps. First: every
      `.sort(` whose comparator has no total tie break, then look downstream for
      `.slice(`, `limit`, `[0]` or `break`. Second: every ordering primitive
      that reads ambient host state rather than its input -- `localeCompare`
      with no locale argument, iteration over a `Map` or `Set`, and `+=` over
      floats.

      `libs/oshun/customer-curation/src/collections/smart-collections.ts` is the
      sharpest instance, because it is LIVE -- `apps/oshun/bff` depends on it --
      and because the failure is not a reordering. `evaluateSmartCollection`
      sorts `b.createdAtUnixSeconds - a.createdAtUnixSeconds` and then
      `sorted.slice(0, rules.limit)`, so three artifacts created in the same
      second with a limit of two return `[a, b]` from one input permutation and
      `[c, b]` from another. Not a different order -- a different SET, with no
      way for anybody to say why `c` is not on the shelf. Measured against the
      shipped function before it was touched. Its `alphabetical` arm is
      `a.title.localeCompare(b.title)`, which orders `["ärlig","i","I","z"]`
      under en-US, `["ärlig","I","i","z"]` under tr-TR and
      `["i","I","z","ärlig"]` under sv-SE -- three orders from one input, and
      `LC_ALL` is not a field in any bundle. And `smartRulesEqual`, whose stated
      job is "to dedupe smart collections within an owner's library and to
      short-circuit re-evaluation when nothing changed", canonicalises with
      `JSON.stringify(pred)`, which preserves key INSERTION order: two identical
      rule sets built by two code paths compared unequal, so the short circuit
      never fired and the re-evaluation ran every time.

      `libs/oshun/search-discovery` has the same shape three more times.
      `candidates/generators.ts::findObjectsMatchingInterests` walks the catalog
      as `for (const [objectId, object] of objectsById)` and `break`s at twenty,
      so which twenty objects a cohort's interests match is decided by the order
      the catalog `Map` was built in. `ranker/features.ts::scoreRankerCandidate`
      accumulates `total += value * weight` over `input.features`, a `Map`, so
      the same eight readings inserted in reverse produce composites differing
      by 2.22e-16 -- which is not nothing, because it is the sign of the
      comparator that ranks them. The same function reads
      `weights.weights.get(featureClass) ?? 0` per PRESENT feature, so a feature
      class nobody measured contributes exactly what a feature class measured at
      zero contributes: both score 0.435 on the same candidate, and a collector
      that has gone down is indistinguishable from a signal that came back
      empty. `applyCoherenceConstraints` then applies greedy `maxPerSource` and
      `maxPerDomain` caps -- and when `enforceTaraCenteredHome` is false it
      applies them to `input.ranked` WITHOUT SORTING IT AT ALL, so the caps keep
      whichever items came first; the sorted branch had no tie break either.
      Every one of its ten exclusion paths was a bare `continue`, so an item
      dropped for a takedown, for rights, for consent or for residency left the
      same trace as an item that was never a candidate, which is none.
      `enforceAntiMonoculture` stops at one remaining item and returns a set
      that still violates the ratio it was asked for, reported as success.

      Three more from asking what each input MEANS rather than how it is
      ordered. `catalog/object-classes.ts::isObjectFresh` is
      `nowUnixSeconds - reviewedAt <= f.windowSeconds`, a ONE-SIDED window that
      every negative age satisfies, so an object published in 2033 is fresh in
      2023 and gets fresher the further ahead it is dated.
      `aggregation/decay.ts::decayWeight` clamps age with
      `Math.max(0, now - capturedAt)`, so a signal timestamped ten years in the
      future keeps weight 1.0 and never decays -- the one field a client
      supplies is the field that turns the decay off. And `timeOfDayBucket`
      computes `Math.floor((unixSeconds % 86400) / 3600)`, which is the UTC
      hour: eight in the evening in Los Angeles is 04:00Z and buckets as
      `night`, under a dimension that exists to say when in somebody's day a
      thing happened and beneath a reason string that reads "because it's
      evening".

      `objectIsEntitled` is the only one that is a leak rather than a wobble.
      `creator-only` was decided as `viewer.entitlement === 'creator-only' ||
      viewer.entitlement === 'admin-only'` -- a comparison of TIERS -- so any
      viewer holding the creator tier was entitled to every other creator's
      private objects. It was written that way because it could not be written
      any other way: `SearchableObject` carried `personaBinding` and `tenantId`
      and had no field naming the creator at all, so the rule named a
      RELATIONSHIP the schema could not express and a tier comparison was what
      was left. A TIER IS NOT AN IDENTITY, and the fix is a field.

      All eleven are fixed in place with tests, and every fix was
      mutation-tested: reverting the tie break, the code-point sort, the
      canonical JSON, the canonical feature order, the coherence ordering, the
      two-sided freshness window, the identity comparison, the future-capture
      refusal and the zone-aware bucket each turned a test red.

      FOURTEEN DECISIONS. A COLLECTION IS ENUMERATED OR DERIVED AND THE TWO HAVE
      DIFFERENT IDENTITY, because a derived one's members are a FUNCTION of a
      corpus and a version naming only the rules denotes a different member set
      every time the corpus moves. AN ORDER KEY LIST IS PART OF THE MEMBERSHIP
      DECISION AND MUST BE TOTAL BEFORE A LIMIT MAY BE APPLIED, tested the way
      `indexes.ts::sortIsTotal` tests a database query: the sorted columns plus
      the fixed ones must contain a declared unique key. TEXT IS COMPARED BY
      CODE POINT, OR BY A COLLATION THE BUNDLE NAMES AND HASHES -- and the quiet
      offender is JavaScript's own `<`, which orders UTF-16 CODE UNITS and so
      puts an emoji-led title before U+FFFD where UTF-8 byte order and
      `COLLATE "C"` put it after, which is a page ordered in the process
      disagreeing with the same page ordered in the index. A SIGNAL CARRIES ITS
      UNIT, SCALE, SOURCE, SOURCE VERSION AND OBSERVATION TIME, AND POLICY NEVER
      TRAVELS WITH IT: the signal type has no weight field and the term type has
      no value field, because a weight applied at capture is a number nobody can
      re-weight and nobody can audit. AN ABSENT SIGNAL IS NOT A ZERO -- a term
      refuses the item or takes a DECLARED default that is written into its
      reasons, and there is no third behaviour, because contributing nothing
      silently is a demotion applied to whichever items a broken collector
      happened to cover. THE COMPOSITE IS EXACT INTEGER ARITHMETIC SUMMED IN THE
      POLICY'S DECLARED TERM ORDER, in basis points over readings held as
      integers at a declared decimal scale, and a policy whose worst case could
      leave the exact range is refused. EVERY EXCLUSION CARRIES ITS REASONS, ALL
      OF THEM, because "why isn't my thing on the shelf" is the only question
      anybody ever asks and fixing the first of four rejections and being
      refused again is how a queue stops being used. AN INPUT THE SNAPSHOT DOES
      NOT COVER IS INDETERMINATE, AND INDETERMINATE IS NEVER ELIGIBLE. A TIER IS
      NOT AN IDENTITY. A SEGMENT BELOW ITS COHORT FLOOR IS REFUSED AT EVALUATION
      AND NOT SUPPRESSED AT REPORT, because a suppressed count still answers the
      question when the person asking chose the predicate -- and DIFFERENCING
      makes the floor a property of every PAIR rather than of one segment, since
      a permitted segment of 40 and a permitted nested one of 38 identify two
      people by subtraction. A SEGMENT MAY NOT BE DEFINED ON A SPECIAL-CATEGORY
      ATTRIBUTE NOR ON A DECLARED PROXY FOR ONE, because a wellness affinity
      inferred from which health articles somebody read is health data with a
      different name and a derivation does not launder the category. A RELATION
      IS DIRECTIONAL AND ITS CONSTRAINTS BELONG TO ITS TYPE, so acyclicity is
      walked per type -- a mixed walk finds cycles that are cycles in neither
      graph. A FEATURE PLACEMENT IS A CLAIM ON A SLOT OVER AN INTERVAL, two
      overlapping exclusive claims are decided by declared priority and a
      priority tie is a fault rather than a coin toss, and a slot that resolves
      to nothing says WHICH of the four reasons it was, because an empty shelf
      and a shelf whose only candidate was refused for rights look identical to
      a reader and must not look identical to an operator. AND THE HASH COVERS
      THE INPUTS, THE POLICY AND THE AMBIENT DECLARATION RATHER THAN THE OUTPUT,
      because an output hash agrees exactly when two builds made the same
      mistake. -->
  - [x] S9.9.a Define versioned collection identity/type/owner/scope/title refs/
        lifecycle and ordered or rule-driven membership. Evidence: collection
        schema.
        <!-- S9.9.a: `CurationCollection` carries all seven, and the field that
        decides the rest is `membership`. AN ENUMERATED COLLECTION'S MEMBERS ARE
        ITS CONTENT and change only by an edit that moves a revision; A DERIVED
        COLLECTION'S MEMBERS ARE A FUNCTION OF A CORPUS AT AN INSTANT, so
        `corpusVersion` is part of its identity and `collectionFaults` refuses a
        derived membership without one -- a rule collection versioned by its
        rules alone is a bundle whose members change underneath a frozen id,
        which is exactly what a reader who pins a version is trying to prevent.
        The title is a REF into the localized-label registry rather than a
        string, because S9.4 already decided that a label is localized and an id
        is not, and a collection carrying a raw title is one whose name cannot
        be translated without changing the collection. The lifecycle is asserted
        EQUAL to `registry-lifecycle.ts`'s six states in the spec rather than
        imported, so the two cannot drift; `SERVABLE_LIFECYCLE_STATES` is
        deliberately a strict subset of that module's `PUBLISHED_STATES`, which
        also contains `retired`, because a retired ENTRY must keep resolving for
        the history that named it and a retired SHELF must stop being drawn.
        A duplicate ordinal is a fault rather than a tolerance, because a tie in
        an enumerated collection is the arrival-order membership this module
        exists to refuse, and a blank tenant is separated from a null one
        because null says unscoped and "" says somebody built an id from an
        empty field. -->
  - [x] S9.9.b Define typed directional relation identity, source/target,
        role/strength/order, validity period, provenance, and constraints.
        Evidence: relation schema.
        <!-- S9.9.b: `CurationRelation` names all seven and the constraints are
        properties of the TYPE rather than of the graph. Acyclicity is walked
        per relation type, and the spec drives the case that makes it matter: an
        `a -> b` prerequisite and a `b -> a` related-to form a cycle in the
        union and in neither graph, so a single walk over all edges reports a
        cycle nobody has, and a system that then refuses the edit refuses a
        correct one. Only edges IN FORCE at the instant are walked, because a
        prerequisite that lapsed last year does not make a cycle today, and the
        same restriction governs functionality and out-degree. An empty
        half-open validity interval is its own fault, because a relation valid
        from 200 to 200 holds at no instant and is visible in the store and in
        no result -- the shape somebody stares at for an afternoon. STRENGTH IS
        A READING AND MAY BE `unjudged`, for the same reason a rank signal may
        be absent: an editor who has not judged how strong a link is has not
        judged it to be zero. The cycle walk is iterative rather than recursive
        (a citation graph is as deep as its corpus, and a stack overflow in a
        validator reads as a crash rather than as a finding) and visits nodes in
        code-point order, so a graph with several cycles reports the same one on
        every run. -->
  - [x] S9.9.c Define feature placement/slot, target, creative/metadata ref,
        start/end, priority, exclusivity, fallback, and target/channel context.
        Evidence: feature schema.
        <!-- S9.9.c: a placement is a CLAIM ON A SLOT OVER AN INTERVAL, which is
        S9.5's reservation composed rather than restated. Two exclusive claims
        that overlap are a conflict decided by declared priority, and an equal
        priority is a separate fault rather than a coin toss -- the shipped
        alternative is whichever row the store returned first. The overlap check
        is per CONTEXT-INTERSECTING pair, so one placement for GB and one for GH
        on the same slot at the same time are not reported: they can never be
        shown to the same reader, and a conflict report full of those stops
        being read. `peakOverlap` sweeps endpoints rather than sampling, and
        sorts ends BEFORE starts at an equal instant, because an interval is
        half-open and a sweep that opened first would report a phantom overlap
        on every back-to-back campaign. `resolveSlot` answers `placed`,
        `fallback` or `empty` and populates `reasons` on ALL THREE: an empty
        shelf and a shelf whose only candidate was refused for rights look
        identical to a reader, and the first is a scheduling gap while the
        second is a legal question. Its four empty-reasons are distinct --
        nothing targets the slot, everything is outside its interval, everything
        excludes this channel/territory/locale, or a named placement was refused
        with its dimension -- and the winner is chosen by priority, then later
        start, then id in code-point order, which is what makes the choice
        total. -->
  - [x] S9.9.d Define raw rank signals/units/source/version/time separately from
        rank policy/weights/tie-break/diversity and final position. Evidence:
        deterministic ranking tests.
        <!-- S9.9.d: the separation is structural and the spec asserts it as
        structure: `RankSignal` has no `weight` key and `RankTerm` has no
        `value` key. A pipeline that stored `0.35 * dwell` has thrown away the
        dwell, and no later policy can recover it. Readings are INTEGERS at a
        declared decimal scale and weights are integer basis points, so the
        composite is summed exactly and in the POLICY's declared term order --
        the caller's map order cannot reach it, which is the whole of
        `scoreRankerCandidate`'s 2.22e-16. A rescale that would round is refused
        rather than performed, because a rank that rounds its inputs is a rank
        two pipelines can disagree about, and both directions are driven: 3000
        at 1e-3 becomes 3 exactly and 3500 does not become 3.5. `MAX_TERM_MAGNITUDE`
        is a billion, which is where the two constraints meet -- it leaves room
        for ninety full-weight terms before the worst case reaches 2^53-1, and
        the first version had it at a trillion, where the module refused its own
        example policy. The spec caught that, which is what a spec is for.
        `policy-tie-break-not-total` is the finding that matters: two items with
        equal composites keep the order they arrived in, and a limit downstream
        turns that into membership. Items refused for a missing required signal,
        cut by the limit, or demoted by a diversity cap are all CARRIED in the
        result with their reasons and their contributions, next to the ones that
        ranked. -->
  - [x] S9.9.e Define eligibility rules for lifecycle/gates/freshness/rights/
        consent/embargo/audience/locale/territory/channel/capacity and exact
        refusal reasons. Evidence: eligibility truth tables.
        <!-- S9.9.e: eleven dimensions, each a separate refusal with a separate
        remediation class, because a reader whose item was refused for rights
        and one whose item was refused for freshness have nothing to do with
        each other and a single boolean sends both to the same page.
        `ELIGIBILITY_COMBINATION` is the truth table as data, and its asymmetry
        is the decision: `ineligible` absorbs `indeterminate` and
        `indeterminate` absorbs `eligible`, so a known refusal stays a fact when
        something else times out and an unknown never becomes a yes. A DIMENSION
        NOBODY READ IS `indeterminate` RATHER THAN ABSENT -- a pipeline that
        forgot to check rights has not established that rights are fine -- and
        the spec drives exactly that by removing one dimension from an otherwise
        all-eligible set. Every reason is kept rather than the first, because a
        queue that answers one rejection at a time over a review cycle that
        takes a day is a queue people stop putting things into. `freshnessAt`
        has THREE states because an item that is not yet current is neither
        fresh nor stale, and merging it into either loses the difference between
        an embargoed piece and an expired one; the shipped one-sided window is
        the reason. And `evaluateRelationshipScope` is the `creator-only` fix:
        it resolves a REF against the party the item names, and an item that
        records no holder is `indeterminate` rather than eligible -- which is
        precisely the state that made the shipped rule compare tiers. -->
  - [x] S9.9.f Define audience segments by stable governed refs, allowed
        attributes/purpose/consent, version, minimum cohort/privacy, and
        noninference rules. Evidence: audience security tests.
        <!-- S9.9.f: a segment is defined over GOVERNED ATTRIBUTE REFS, and the
        registry's classification is what decides whether it may be targeted on.
        `special-category-derived` is the class that matters: an attribute
        INFERRED from a special category is still that category, so a "wellness
        affinity" computed from which health articles somebody read is health
        data with a different name, and a registry that classes it as ordinary
        launders it. Proxy detection is structural rather than statistical,
        because the registry already knows what each attribute was derived from.
        A SEGMENT BELOW ITS FLOOR IS REFUSED AT EVALUATION AND NOT SUPPRESSED AT
        REPORT: suppressing the number still answers the question when the
        person asking chose the predicate, since "fewer than five" for
        `country = GH AND role = admin AND salaryBand = 9` has already said
        there is at least one and the asker supplied every term. The floor is
        also absolute rather than self-declared, because a minimum a segment
        sets for itself can be set to one. And `differencingFaults` is the
        disclosure a per-segment check cannot see: two segments of 40 and 38
        where the second is contained in the first each pass their own floor,
        and the two people in the difference are identified by subtraction --
        so the floor is a property of every PAIR, and the intersection case is
        the same statement for overlapping rather than nested segments. -->
  - [x] S9.9.g Compose/validate curation with no duplicates/cycles/invalid refs,
        deterministic ordering, preview/diff, review, and expected revision.
        Evidence: composition tests.
        <!-- S9.9.g: `composeCuration` validates every part TOGETHER rather than
        in sequence, because the faults that matter are between the parts -- a
        placement pointing at a slot that does not exist, a collection member
        whose ref does not resolve, a policy whose tie break is not total over
        the items it is about to order. A ref carries the VERSION it was
        resolved at, and `ref-resolves-to-a-different-version` is separate from
        `ref-does-not-resolve` because the remedies differ: the first is a
        bundle that needs rebuilding against the corpus it ran on and the second
        is a typo or a deletion. `diffCuration` reports `member-moved` as its
        own kind rather than as a removal plus an addition, because a reviewer
        approving a reorder and one approving a swap are approving different
        things and a diff that renders both as `-a +a` asks them to spot it; and
        `ambient-changed` exists so that a bundle whose members moved because
        somebody's container changed time zone says so instead of presenting it
        as an editorial decision. `requiredReview` takes the STRICTEST any
        single change requires -- an eligibility change is `rights-and-owner`
        because it is the one that can put something in front of somebody not
        permitted to see it -- and an ambient change raises nothing at all,
        because asking an owner to approve a container move hides the container
        move. -->
  - [x] S9.9.h Build a versioned bundle whose identical inputs produce identical
        members/ranks/eligibility/reasons/hash. Evidence: reproducibility test.
        <!-- S9.9.h: THE HASH COVERS THE INPUTS, THE POLICY AND THE AMBIENT
        DECLARATION -- NOT THE OUTPUT. Hashing the output makes two bundles
        agree exactly when nothing interesting is true: a build that dropped
        every member because a collector was down hashes identically to another
        that did the same, and the pair reads as a reproduction. The spec pins
        the consequence: hand-editing a bundle's members and verdicts does NOT
        move the hash, and it is the per-axis findings that catch it and name
        which axis moved. Every ambient input the computation could read is
        declared in `AmbientDeclaration` and hashed with it, and a build whose
        declaration is incomplete is REFUSED rather than defaulted, because a
        bundle that reads `Intl.DateTimeFormat().resolvedOptions().timeZone` is
        a property of the container it ran in and container images change
        without anybody deciding to change a ranking. `canonicalize` is composed
        from `revisions.ts` rather than written again, which is why building
        returns a `ParseResult`: a bundle carrying a `NaN` would otherwise hash
        the same as one carrying `Infinity`, both having serialized to null.

        THE PROOF IS `tools/curation-determinism.mjs`, and it is the half no
        in-process suite can settle: a single process has one locale, one time
        zone, one ICU build and one heap layout, so running a build twice in it
        proves the function is not calling `Math.random` and cannot prove it is
        not calling the container. The tool builds the SAME bundle in child
        processes under `LC_ALL` of en_US, tr_TR, sv_SE, C and unset and `TZ`
        fourteen hours apart across the date line, times four input
        permutations, and requires one members list, one excluded list, one
        verdict set, one input digest and one hash across all twenty -- after
        first checking that the children really did resolve to four different
        locales, so the claim is not one container measured twenty times. Six
        controls each reproduce a shipped defect before the claim is measured
        against them: A LOCALE SORT IS A CONTAINER SORT (three orders from one
        input), A LIMIT OVER A TIED ORDER CHOOSES MEMBERS (four different member
        SETS from four permutations), FLOAT ADDITION IS NOT ASSOCIATIVE
        (0.6800000000000002 against 0.6799999999999999), A MAP WALK WITH A BREAK
        IS AN ARRIVAL-ORDER FILTER, A CLOCK READ IS AN UNDECLARED INPUT, and
        UTF-16 ORDER IS NOT UTF-8 ORDER. A control that stops reproducing its
        defect is reported as a BROKEN CONTROL rather than as a pass.

        AND IT EARNED ITS PLACE ON ITS FIRST RUN. It reported four hashes where
        the members, the excluded list and the verdicts were all identical: the
        enumerated membership was being canonicalised in whatever array order
        the caller built it, although the ordinal is the content and the array
        order is presentation. A real defect in this module, invisible to 189
        in-process tests, found by the one thing that varies what a process
        cannot vary about itself. Both halves are now pinned in the spec -- the
        same membership in two orders digests the same, and a membership whose
        ORDINALS moved digests differently, because a reorder is an editorial
        change that must not ship under a pinned version. -->
- [x] S9.10 Prohibit direct production-catalog writes. Evidence: all changes
      pass through reviewed versioned bundles.
  - [x] S9.10.a Inventory database/admin/API/job/import/script/provider paths
        capable of changing production catalog, search, feature, rank,
        collection, or audience state. Evidence: write-path register.
  - [x] S9.10.b Define draft proposal/bundle, semantic diff/impact, validation/
        gates, required reviews/approvals, schedule/embargo, publish, verify,
        and rollback lifecycle. Evidence: catalog workflow contract.
  - [x] S9.10.c Restrict production catalog credentials and write functions to
        the publisher identity and deny humans/hosts/domain services direct
        access. Evidence: authorization tests.
  - [x] S9.10.d Bind publisher execution to immutable approved bundle/hash,
        target/environment, expected production version, idempotency, and
        step-up. Evidence: command tests.
  - [x] S9.10.e Prevent alternate database, search index, cache, compatibility,
        migration, admin, or provider API writes from bypassing the workflow.
        Evidence: bypass tests.
  - [x] S9.10.f Read back and verify production catalog, preserve immutable
        release/audit, and reconcile partial/unknown/rollback outcomes.
        Evidence: end-to-end publish tests.
  - [x] S9.10.g Add CI/runtime detection for unregistered catalog mutation code/
        credentials. Evidence: deliberate bypass failure.
        <!-- S9.10: shipped as `libs/oshun/workbench-kit/src/catalog-write-paths.ts`
        (+ spec, 109 tests, 100% of statements, branches and functions) and
        `libs/oshun/workbench-kit/tools/catalog-bypass-scan.mjs` with its
        register `tools/catalog-write-paths.json` (nx target
        `catalog-bypass-scan`). 149 kit entry points.

        This item is the word ALL in S9.9's evidence clause, and ALL is a claim
        about a closed world: false the moment one door exists that the bundle
        does not lead through, and invisible to the bundle, because that door is
        precisely the thing that never produces one.

        THE VEIN, STATED SO IT TRANSFERS: AN APPROVAL THAT TRAVELS ON THE THING
        BEING APPROVED IS NOT AN APPROVAL, AND A CHECK THAT QUANTIFIES OVER THE
        LIST THE WRITER PASSED CANNOT REFUSE WHAT THE WRITER LEFT OUT. The grep
        is mechanical and it is three greps. In every admission, advance or gate
        function: (1) which fields of the verdict are read off the SUBJECT --
        `edge.reviewState`, `gate.satisfied`, `gate.required`; (2) which are
        taken from a CALLER-SUPPLIED BOOLEAN -- `reviewerIsExpert`, `isAdmin`;
        (3) which JOIN THEIR EVIDENCE BY IDENTIFIER where the evidence is about
        content -- `preview.requestId === rollout.requestId`. All three are
        shipped here, and every claim below was measured against the live
        function before it was touched.

        `search-discovery/src/concept-graph/schema.ts::admitConceptEdge` is the
        admission control for the concept graph and its high-stakes rule reads
        `edge.reviewState !== 'approved' || edge.approvedByExpertId === null` --
        both fields of the edge the caller is submitting. A caller who writes
        `reviewState: 'approved', approvedByExpertId: 'me'` is admitted and no
        reviewer ever existed: the gate asks the thing being admitted whether it
        was approved. Worse in the other direction: for the nine kinds that are
        NOT prerequisite or contradicts there was no review rule at all, so an
        edge whose own state read `rejected` -- a reviewer looked at it and said
        no -- entered the live graph, and `listNodeNeighbors`, in the module
        whose sibling `queries.ts` filters all eight of its traversals through
        `approvedEdges` with a comment saying why, reported it as a fact about
        the concept.

        `concept-graph/curation.ts::reviewConceptEdgeProposal` took
        `reviewerIsExpert: boolean` as an INPUT, so the expertise gating every
        high-stakes edge in the system was whatever the caller passed, and it
        let `reviewerId` equal the proposal's `proposerId`: alice proposed,
        alice approved, alice certified alice, and the edge went into the graph
        with a provenance record naming alice as its reviewer.
        `withdrawConceptEdgeProposal` took no actor at all -- its own doc says
        "proposer cancels their own proposal" -- so anybody could withdraw
        anybody's, and the record it wrote set `reviewedAtUnixSeconds` while
        leaving `reviewerId` null: a decision by nobody, at a time.
        `listPendingProposals` applied its age filter only when BOTH
        `olderThanSeconds` and `nowUnixSeconds` were supplied, so
        `{ olderThanSeconds: 604800 }` returned every pending proposal -- the
        widest possible answer to a request to narrow -- and
        `summariseCurationQueue` folded negative ages into a maximum starting at
        zero, so a queue of future-stamped work reported no old work.

        `studio-authoring/src/editorial-lifecycle/lifecycle.ts::tryEditorialAdvance`
        was one line: `input.gates.filter((g) => g.required && !g.satisfied)`.
        `input.gates` is the caller's array and both booleans are the caller's,
        so the function had no idea which gates exist and `approved ->
        published` with `gates: []` succeeded. The producer of the right list
        was one file away -- `checklists.ts::resolveEditorialChecklist` resolves
        the required gates per content type and state and its own doc says the
        result "can be handed straight to `tryEditorialAdvance`" -- and nothing
        checked that it had been, while `apps/oshun/bff/.../editorial-publisher.ts`
        takes `gatesFor` from the deployment and two of its four call sites
        passed `gates: []` outright. The kit had already decided the general
        form in S2.9, where `UNAVAILABLE_BEHAVIOURS` has no `pass` member "not
        as a rule -- as a type"; a decision that stays inside the kit is a
        decision the library that needed it never got. The same fix found the
        gate pointing backwards: the non-publish arm of the checklist returned
        the authoring-evaluation gate for EVERY state, so a takedown was gated
        on a check about the quality of the writing.

        `studio-authoring/src/taxonomy-curation/curation.ts::tryAdvanceOntologyRollout`
        is the third grep. A previous pass fixed the missing preview and wrote
        "a stage name is not a gate" above the fix; the join it added is
        `preview.requestId !== rollout.requestId`. The preview is a computation
        over `request.targetIds`, the request is a mutable record, and the join
        is on its id. Measured: a preview whose `affectedTagIds` is
        `["tag:obscure"]` admitted a request whose targets were
        `["tag:every-artifact-we-have"]`, reporting `complete: true` and a blast
        radius of one tag. The same function rolled out from `staged` with no
        preview, no approval, no reviewer, a year-old preview, and a request
        whose own `contestStatus` read `contested`.

        And two silent-drop writes in the index. `catalog/index-engine.ts::indexObject`
        answered `SearchIndexEngine` for both outcomes, so an object of another
        class was dropped and the caller got back a value indistinguishable from
        a completed write; `indexObjectInRegistry` did the same for a class the
        manifest has no engine for, which is a deployment fault. Its embedding
        arm read `engine.embedding && payload.embedding ? indexed :
        engine.embedding`, so re-indexing an object whose embedding was
        withdrawn KEPT THE PREVIOUS VECTOR: the lexical document said one thing
        and the vector said what the document used to say, and a semantic query
        still returned the object on the strength of text that had been edited
        or taken down.

        Sixteen in-place fixes across the three libraries and the BFF, and all
        thirty-two mutations of them were caught: reverting the approval record,
        the self-review refusal, the roster resolution, the two-sided
        qualification window, the withdrawal actor, the future-dated queue
        count, the rejected-edge neighbour filter, the four-field provenance
        predicate, the requirement loop, the deployment-added gate, the
        ungated removal, the canonical-form binding, the preview window, the
        approval requirement, the contested refusal, the class-mismatch refusal
        and the stale-vector removal each turns a test red.

        TEN DECISIONS. A PROHIBITION IS A CLOSED WORLD AND A REGISTER IS ONLY A
        CLAIM ABOUT ONE, so `registerFaults` judges the entries it has and the
        register is worth what `reconcileRegister` says when it is set against
        an INDEPENDENT enumeration -- and a reconciliation against zero
        observations reports zero unregistered paths, which reads identically to
        compliance, so an empty or partial scan is itself a finding. AN OMITTED
        GATE IS AN UNMET GATE: `changeAdmissionFaults` iterates the CONTRACT's
        requirements and looks each up in what was presented, and never the
        reverse, because the two loops are one keystroke apart and only one of
        them can refuse an omission. AN APPROVAL BINDS TO A DIGEST AND NOT TO AN
        IDENTIFIER -- the id says WHICH change, the digest says WHICH BYTES, and
        evidence whose `subjectDigest` is null is refused rather than warned
        about. A CHANNEL IS CLOSED BY A MECHANISM OR IT IS OPEN, so
        `documented-convention` and `code-review` are in the vocabulary
        precisely so a register can SAY them and both report as open; the one
        self-serving closure, `writes-no-shared-state`, carries its criterion in
        the type's own comment. THE PUBLISHER IS AN IDENTITY AND NOT A ROLE
        PEOPLE CAN ASSUME, so `assumableBy` is read as well as `principal` and a
        break-glass grant must expire and must name where its use is recorded.
        THE EXPECTED PRODUCTION VERSION IS PART OF THE COMMAND, because
        publishing onto whatever is there now is a lost update with a receipt.
        AN INDEX, A CACHE AND A PROJECTION ARE PRODUCTION CATALOG STATE, and two
        writers into one derived class is its own finding even when both are
        legitimate, because each reads back exactly what it wrote. AN UNKNOWN
        OUTCOME IS NOT A FAILED ONE, so `reconcilePublication` answers with five
        outcomes and names `reconcile-before-any-retry` rather than retrying.
        THE VERIFICATION READS THE TARGET AND NOT THE RECEIPT: an observation
        sourced from the command's own reply is a fault AND is excluded from the
        read count, so a publication verified entirely against its own answer
        reconciles to `unknown`. AND EVERY REFUSAL NAMES THE ROUTE IT WOULD HAVE
        TAKEN INSTEAD, because a prohibition with nowhere to go is one people
        route around, and the route they find is the one nobody registered.

        `tools/catalog-bypass-scan.mjs` is the half no in-process suite can
        settle. It reads 1395 real source files under four roots, strips
        comments and string literals first -- every file that DESCRIBES the scan
        mentions every pattern it looks for, so a scanner that greps raw text
        reports its own documentation as a fleet of bypasses and is switched off
        within a day -- and finds every SQL DML against a catalog table, every
        search-index write, every taxonomy admission, every editorial advance
        and every catalog credential read, anchoring each on its enclosing
        exported symbol rather than on a line number that moves. It found
        fourteen real sites, every one of which is now classified in the
        register with an owner and a closure. SIX CONTROLS, each of which must
        reproduce its defect first: an unregistered SQL write, an unregistered
        index write, an unregistered credential, a MENTION that must produce
        nothing, an EMPTY SCAN that must not read as a clean one, and a stale
        register entry. A deliberate `indexObjectInRegistry` call added to
        `libs/oshun/customer-curation` fails it with the file, the symbol and
        the matched line, and removing one entry from the register fails it the
        other way; exit 1 both times, exit 0 clean.

        Scope stated: `schedule/embargo` in S9.10.b is the `scheduled` stage in
        the contract's stage list -- the embargo predicate itself is S9.6's
        `lifecycle.ts::isEmbargoed` and is not restated here -- and S9.10.f's
        "end-to-end publish tests" are exhaustive over the reconciliation of a
        read-back, since V1 has no live production catalog service to publish
        into; the live end-to-end equivalent is the BFF editorial publisher
        walking the real state machine under the checklist's required gate ids.
        -->
- [x] S9.11 Add search indexing events with replay and dead-letter handling.
      Evidence: full rebuild matches incremental index.
  - [x] S9.11.a Define versioned index event types for upsert/delete/tombstone/
        permission/taxonomy/localization/rights/gate/catalog/visibility changes.
        Evidence: event schemas.
  - [x] S9.11.b Emit authoritative event with tenant/object/revision/sequence,
        changed field classes, causation/correlation, and projection version via
        outbox. Evidence: atomicity tests.
  - [x] S9.11.c Consume idempotently with per-object/version ordering, late/
        duplicate/out-of-order/gap/deletion handling, and property redaction.
        Evidence: delivery tests.
  - [x] S9.11.d Build documents through versioned projection adapter and perform
        atomic replace/delete without partial searchable state. Evidence:
        indexing integration tests.
  - [x] S9.11.e Retry transient failures and dead-letter malformed/unsupported/
        deterministic failures with owner, safe cause, source event, and replay.
        Evidence: dead-letter tests.
  - [x] S9.11.f Implement checkpointed full rebuild from authoritative data/
        events into new index version with validation and atomic alias/cutover.
        Evidence: rebuild rehearsal.
  - [x] S9.11.g Compare incremental and rebuild docs/counts/hashes/queries/
        permissions and repair divergence. Evidence: parity report.
        <!-- S9.11: shipped as `libs/oshun/workbench-kit/src/search-index-events.ts`
        (+ spec, 98 tests, 23 of 23 mutations of the shipped decisions caught),
        entry point `./search-index-events` (150 approved, no drift), plus
        in-place fixes in `search-discovery`, `domain-tara`, `offline` and
        `apps/oshun/mobile`.

        The evidence clause -- "full rebuild matches incremental index" -- is the
        only evidence in the phase that cannot be produced by the thing it
        judges. An index is a DERIVED store, and every other check on it reads
        the derived store and asks the derived store whether it is right, which
        is blind to exactly the two failures that leave nothing in it to read:
        the object that never arrived, and the object that should have left and
        never did.

        THE VEIN, STATED SO IT TRANSFERS: A DERIVED STORE IS ONLY AS TRUE AS THE
        ENUMERATION THAT MAINTAINS IT, AND AN ENUMERATION OVER THE DERIVED STORE
        CANNOT SEE WHAT NEVER ARRIVED. The grep is one question asked of every
        maintenance loop: WHAT DOES IT ITERATE? A loop over the derived store can
        refresh and can evict and can never ADD. A loop over the source can add
        and can update and can never DELETE. Only a loop over the UNION is a
        reconciliation -- and the moment one side's membership is read off the
        other, the comparison agrees with itself for every input.

        Four instances, measured before anything was written, all the same loop.

        `search-discovery/src/catalog/index-engine.ts::freshenIndexEngine` walked
        `lexical.docs`, `embedding.entries` and `facets.indexedAtByObject` --
        three enumerations of the INDEX -- and refreshed or evicted whatever was
        past the class budget. An object present in `liveObjects` and absent from
        every sub-index was never looked at, so a document whose indexing event
        was lost was invisible to the pipeline whose NAME is freshness. Worse in
        the other direction: an object deleted from the catalog while its entry
        was still fresh was not stale, so it was not in the stale set, so it was
        not evicted -- and `defaultFreshnessTargetSecondsFor` answers
        `365 * 86400` for every class outside veritas, nyx, metis and arete, so a
        takedown that reached the catalog and not the index stayed searchable for
        up to a year while the report called the class clean. It now walks the
        union, and a snapshot must DECLARE whether it is the complete class,
        because a partial one may not evict anything.

        `domain-tara/src/favorites-sync.ts` called
        `getFavorites({ limit: 500 })` and handed the result over as the
        authoritative remote set. A PAGE IS NOT A SET: the five hundred and first
        favourite was "not remote", so it was counted as a remote deletion and
        dropped from the device. The same function called an item `unchanged`
        when its id appeared on both sides, so a meditation renamed,
        recategorised or re-timed reconciled as unchanged and the stale copy was
        the one kept. It now pages to a short page, degrades to `partial` rather
        than deleting when an account outruns the budget, dedupes rows an offset
        walk repeats, and counts `updated` apart from `unchanged`.

        `domain-tara/src/ritual-completion-events.ts` builds the only search
        document any V1 library produces from an event, and keyed it
        `${event.eventId}-search` where the event id ended in
        `Math.random().toString(36).slice(2, 8)`. A DOCUMENT IS KEYED BY THE
        OBJECT IT DESCRIBES AND NEVER BY THE EVENT THAT ANNOUNCED IT: with a
        random component in the key, one completion delivered twice was two home
        signals, two activity rows, two search documents and six
        recommendations, none carrying a field naming what they duplicated, and
        a rebuild -- which reads completions, not deliveries -- could never
        produce the same set. Every projection is now keyed on
        `taraRitualCompletionObjectId`, and the test that asserted
        `secondEvent.eventId).not.toBe(firstEvent.eventId)` was pinning the
        defect.

        `offline/src/queue.ts::process` was the dead letter that is not one. On
        the attempt reaching `policy.maxAttempts` it pushed an item result and
        `continue`d WITHOUT pushing the item onto `nextQueue` -- and `nextQueue`
        is what `saveQueue` persists -- so the operation was deleted from durable
        storage and survived only in the array returned to the caller. Its one
        caller, `apps/oshun/mobile/.../offlineQueueController.ts`, kept those in
        a field, in memory, on a mobile client: the process that died between the
        failure and the retry lost the user's queued write with no record
        anywhere that it had existed. And every failure was transient, so a
        payload the server would never accept was retried four times on a metered
        radio and then discarded. There is now a persisted dead-letter store, a
        caller-supplied failure classifier, replay under the ORIGINAL id (the
        previous `enqueue(type, payload)` minted a new one, so a retry was a
        second write), and per-object head-of-line ordering so a queued edit
        cannot be overtaken by the next edit to the same object.

        TEN DECISIONS. A DELETE AND A TOMBSTONE ARE DIFFERENT EVENTS AND THE
        REBUILD IS WHERE THE DIFFERENCE BITES -- a tombstone is the one fact that
        is true of the index and false of the authoritative store, because the
        row is usually still there, so a plan whose `tombstoneSource` is `none`
        republishes every takedown it ever applied, and the parity report then
        calls those documents `only-in-rebuild`, which is the shape of a MISSING
        document; repairing it the obvious way puts them back, which is why
        `repairPlan` consults the tombstone list before anything else. A REVISION
        ORDERS CONTENT AND A SEQUENCE ORDERS EVENTS, AND ONLY ONE OF THEM MOVES
        WHEN A PERMISSION CHANGES, so deduplicating on the revision drops every
        envelope-only change -- precisely the class whose staleness is a
        disclosure. THE STALENESS BUDGET IS A PROPERTY OF THE FIELD CLASS AND NOT
        OF THE PIPELINE, since one "index lag" number treats a corrected title
        and a revoked ACL alike and is always chosen for the title. A GAP IS NOT
        A REASON TO STOP AND IT IS NOT A REASON TO PROCEED, so it is held for a
        window and then answered with `request-replay` naming the missing range.
        THE DISPOSITION OF A FAILURE IS A PROPERTY OF THE FAILURE AND NOT OF THE
        ATTEMPT COUNT -- only `unknown` consults the count, exactly as S3.6's
        `decideDelivery` already decided -- and `hold-for-upgrade` is the third
        answer for an event emitted for a projection this worker does not have.
        A DEAD LETTER THAT DOES NOT CARRY ITS SOURCE EVENT IS A LOG LINE; it
        names an owner, and its cause is CHECKED against the values the redaction
        policy protects rather than trusted, because an operational store is
        where careful redaction goes to be undone. A DOCUMENT REPLACE IS ONE
        OPERATION OR THE INDEX IS QUERYABLE IN A STATE NOTHING PRODUCED, across
        sub-indexes too: a write that fills the lexical document and leaves the
        previous vector is the defect this repository already fixed once in
        `indexObject`, and it is a plan-level fault here so the next pipeline
        cannot reach it independently. AN ANALYZER CHANGE IS AN INDEX VERSION
        CHANGE -- nothing in a document changes when the tokenizer does, so the
        old documents are simply unfindable by queries the new analyzer parses,
        which no document comparison can see and only a QUERY probe can. A
        REBUILD PINS A WATERMARK BEFORE IT READS ANYTHING, or every change made
        while it ran is silently rolled back by the cutover, and it checkpoints
        against its own plan digest, because a checkpoint is an index into an
        ORDER and resuming a changed plan reports completion over a set it never
        covered. AND A PARITY REPORT WHOSE TWO SIDES SHARE AN ENUMERATION PROVES
        NOTHING, WHILE ONE OVER AN EMPTY CORPUS PROVES LESS: both produce zero
        divergences, which reads exactly like parity, so `conclusive` is a field
        and `rebuildMatchesIncremental` requires it.

        Scope stated: S9.11.d's "indexing integration tests" and S9.11.f's
        "rebuild rehearsal" are exercised against the real in-process index
        engine and the real offline queue over real storage, since V1 has no live
        Elasticsearch or OpenSearch deployment to cut an alias over; the kit
        owns the plan, the cutover refusal and the parity arithmetic as data, and
        `freshenIndexEngine`'s union loop is the live reconciliation those rules
        describe. Emission composes S3.6's `outbox.ts` -- the transaction-bound
        row, per-subject order and the `retryable | permanent | unknown`
        classification are not re-decided here, only specialised to what a search
        event has to carry.
        -->
        <!-- S9.11 marked after writing and reading
        `libs/oshun/workbench-kit/src/search-index-events.ts` end to end, running
        its 98 tests, mutating 23 of its decisions and confirming each turns a
        test red, and after running the four repaired libraries' own suites:
        search-discovery 166, domain-tara 318, offline 13, mobile jest 68. -->

- [x] S9.12 Add permission-aware search projection. Evidence: hidden titles and
      counts do not leak.
  - [x] S9.12.a Define searchable/displayable/facetable/sortable/suggestible/
        exportable fields per property authorization, audience, lifecycle,
        classification, and tenant scope. Evidence: projection policy.
  - [x] S9.12.b Project tenant/workspace/object ACL or authorization filter
        version plus protected-source/learner/biometric/unreleased redaction
        without raw credentials/roles. Evidence: index document tests.
  - [x] S9.12.c Apply authorization before query, total/count, facet, group,
        suggestion/autocomplete, highlight/snippet, ranking, and pagination.
        Evidence: disclosure negative tests.
  - [x] S9.12.d Revalidate current object/property authorization at result use
        to cover stale index and emergency revocation. Evidence: stale ACL
        tests.
  - [x] S9.12.e Update/delete index immediately on permission/ownership/
        classification/embargo/rights/consent/lifecycle change. Evidence:
        invalidation latency tests.
  - [x] S9.12.f Prevent hidden existence inference through timing, score,
        spelling suggestion, related item, empty-versus-forbidden errors, cached
        result, and cursor. Evidence: side-channel tests.
  - [x] S9.12.g Test two tenants with colliding IDs/titles/content/taxonomy and
        ordinary/assigned/reviewer/auditor/support roles. Evidence: isolation
        matrix.
        <!-- S9.12: shipped as
        `libs/oshun/workbench-kit/src/search-permission-projection.ts` (+ spec,
        95 tests, 54 of 54 mutations of the shipped decisions caught), entry
        point `./search-permission-projection` (151 approved, no drift), plus
        in-place repairs in `libs/contracts`, `apps/oshun/bff`,
        `search-discovery` and `domain-tara`.

        The evidence clause is "hidden titles and counts do not leak", and the
        second noun does the work. Filtering the ROWS is true of every
        implementation that has this defect, because a hidden row is always
        withheld; what escapes is the total, the page fill, the facet count, the
        suggestion, the score of the rows that remain, and the id in the cursor.

        THE VEIN, STATED SO IT TRANSFERS: A SEARCH SURFACE IS A SECOND READ PATH
        TO EVERY ROW IT COVERS, AND ITS AUTHORIZATION IS ASKED ABOUT THE QUERY
        RATHER THAN ABOUT THE ROW. The direct route asks "may you read THIS
        object". The search route asks "may you use THIS facet", "may you see
        THIS domain", "may you reach THIS index" -- questions about the request,
        answered before a row is loaded -- and then reads whatever the collector
        reads. Two greps find it everywhere: which noun the permission check
        takes as its SUBJECT, and which values in the response were computed from
        rows the check never saw.

        Four instances, measured against the running code before anything was
        written.

        `apps/oshun/bff/src/admin/admin-search.ts::runAdminUniversalSearch` gates
        on `facetsVisibleTo(scopes)`, and the `libs/contracts` table unlocked the
        `cases` facet for any ONE of `admin:workspace:support`, `:privacy`,
        `:rights` or `:incident` while `collectCaseHits` read all four
        workspaces. THE FACET IS A UNION OF SOURCES AND THE REQUIREMENT WAS AN
        ANY-OF OVER THAT UNION, so any-of collapsed to all-of. Measured against
        the booted BFF with a token carrying only `admin:workspace:incident`:
        `GET /v1/admin/workspaces/privacy` answered
        `403 admin_scope_missing` and
        `GET /v1/admin/search?q=privacy&facets=cases` answered `200` with three
        privacy requests and a rights request. `artifacts` collapsed the same
        way -- a `:persona`-only operator was served `policy-privacy-dsar` from a
        workspace whose own route refuses them. And the needle was scored against
        `item.subjectId`, which no hit renders: `q=user-privacy-02` returned one
        export request and never printed the id, so the query box answered a
        question about a data subject that the answer was careful not to state.
        The requirement now lives on the SOURCE, the facet's requirement is
        DERIVED from its sources, and a partially served facet reports
        `skippedSources` rather than looking like an empty workspace.

        `search-discovery/src/catalog/index-engine.ts` ran
        `searchLexicalIndex(index, query, topK)` to `hits.slice(0, topK)` and
        offered `filterVisibleHits` as the visibility step AFTERWARDS. Two
        defects in one line: the caller who asked for twenty got three, and the
        seventeen missing rows were the count of the matches that viewer may not
        see. The scores were worse -- BM25 reads `docs.size`,
        `documentFrequency` and `averageLength`, three statistics over the WHOLE
        corpus, so the rank of a document a viewer can see moved when a document
        they cannot see was added, which is a cross-tenant existence oracle
        anybody who can write a document can operate. `aggregateFacetCounts`
        counted over whatever scope it was handed and `filterByFacets` with no
        filters handed back every indexed object. All three now take a required
        `AuthorizedObjectIds`, the statistics are recomputed over it, and
        `revalidateHits` separates a hit whose object LEFT THE CATALOG (the index
        is behind) from one whose object refuses this viewer (the query was
        answered over too wide a set) -- the previous `if (!object) return false`
        reported neither.

        `search-discovery/src/candidates/generators.ts` builds the related rails
        and every one of them cut its top-K before entitlement, which
        `applyCoherenceConstraints` applied afterwards. A hidden object consumed
        the slot and the rail came back short: four slots rendering two tells the
        reader that two objects exist which are more similar to what they are
        reading than anything they may open, and which ones is often obvious from
        the seed. Measured: with the two nearest neighbours hidden and
        `maxPerSeed: 2`, the rail returned NOTHING.

        `domain-tara/src/ritual-completion-events.ts` builds the only search
        document any V1 library produces, and it carried a title, a summary, a
        body, tokens and a route -- a member's practice record -- and no owner,
        no tenant, no audience and no filter version. A DOCUMENT WITH NO SUBJECT
        CANNOT BE FILTERED TO ONE, however carefully the query is written, and
        the one place the member appeared was inside `documentId`, which is the
        field that travels furthest. It now carries `subjectId`, `audience:
        'subject-only'` and `authorizationFilterVersion`; the key is left alone,
        because S9.11 keyed every projection on the completion deliberately.

        TEN DECISIONS. THE UNIT OF AUTHORIZATION IS THE ROW AND A FACET IS NOT A
        ROW -- a facet, a tab, a domain filter and an index name are groupings
        the UI chose, and the moment one spans two sources with different owners
        an any-of requirement is read by the collector as all-of. A FIELD'S
        PROJECTION IS SIX PERMISSIONS AND THE MATCH IS ONE OF THEM: searching
        discloses EXISTENCE, faceting the value AND a count, sorting ORDER,
        suggesting the value verbatim -- so a searchable-and-not-displayable
        field is a match oracle, licensed only by a named human acceptance
        carrying its reason, and never for a class where presence IS the
        disclosure. THE DOCUMENT HOLDS THE UNION AND THE ANSWER HOLDS THE
        INTERSECTION, which is why the authorization filter is a required field
        of the document rather than a lookup beside it. THE AUTHORIZED CORPUS IS
        A TYPE AND NOT A STEP -- every stage after authorization takes
        `AuthorizedCorpus`, which `authorizeCorpus` is the only function able to
        produce, so a stage cannot read the wider set because it holds no
        reference to it; a stage ordering written down as a list of names is a
        comment, and this is the same ordering held by the compiler. THE SCORE IS
        A CORPUS STATISTIC SO THE CORPUS HAS TO BE THE VIEWER'S. TRUNCATION AFTER
        AUTHORIZATION, NEVER BEFORE. AN EMPTY ANSWER AND A FORBIDDEN ANSWER ARE
        THE SAME ANSWER, so there is no forbidden code and no field able to carry
        one -- S4.3's conceal-outranks-deny at the level of a result set. A
        CURSOR IS BOUND TO THE VISIBILITY IT WAS MINTED UNDER, since S4.9 already
        binds tenant, actor and query and none of those move when an ACL does --
        and the digest covers the INPUTS to visibility and never the outcome, or
        indexing an ordinary document would invalidate every outstanding token
        and the digest would itself be a value moving with the corpus.
        REVALIDATION IS A BACKSTOP AND NOT A CONTROL: it shrinks the rows, cannot
        repair the aggregates without the corpus, says so in the report, treats
        could-not-tell as a drop that is NOT evidence of a stale index, and
        discards whole any answer older than the revocation epoch. AND A
        NARROWING IS A DELETE WHILE A WIDENING IS AN UPDATE, AND ONLY ONE OF THEM
        HAS A BUDGET -- a reshape is a narrowing, because taking the safe half
        first costs nothing.

        Scope stated: S9.12.e's "invalidation latency tests" are exercised
        against observations of a pipeline rather than against a live indexing
        cluster, since V1 has no Elasticsearch or OpenSearch deployment to
        measure a queue on; the kit owns the action, the budget per change class
        and the breach arithmetic as data, and the four repaired libraries are
        where the decisions are applied. S9.12.b's redaction composes S3.12's
        `STRATEGY_CAPABILITIES` and S3.13's `PRESENCE_DISCLOSING_CLASSES` rather
        than re-deciding which operations a protected field can support.
        -->
        <!-- S9.12 marked after writing and reading
        `libs/oshun/workbench-kit/src/search-permission-projection.ts` end to
        end, running its 95 tests, mutating 54 of its decisions and confirming
        each turns a test red, and after running the repaired libraries' own
        suites: workbench-kit 10264, search-discovery 179, domain-tara 321,
        contracts admin-universal-search 22, BFF admin-search route 15 plus the
        OpenAPI coverage contract. -->
- [x] S9.13 Add localization jobs, translation memory refs, glossary refs,
      review, and parity gates. Evidence: locale bundle round-trip.
  - [x] S9.13.a Define localization job source revision/locale, target locale/
        script/region, field/unit list, provider/method, references, policy,
        budget, and lifecycle. Evidence: job schema.
  - [x] S9.13.b Segment structured content without localizing IDs/code/URLs/
        placeholders/standards and preserve stable source anchors/order/context.
        Evidence: segmentation tests.
  - [x] S9.13.c Reference authorized versioned translation memory matches with
        source/target locale, segment/hash, score, rights/confidentiality, and
        provenance. Evidence: TM tests.
  - [x] S9.13.d Reference versioned glossary terms, morphology/context,
        required/ forbidden translations, case, locale scope, and precedence.
        Evidence: glossary tests.
  - [x] S9.13.e Store generated/human translation as proposal with provider/
        model/template/run/cost or translator identity and no auto-approval.
        Evidence: proposal lifecycle tests.
  - [x] S9.13.f Implement linguistic/domain/legal/safety/accessibility review,
        suggestions, comments, required roles/quorum, and approval per locale.
        Evidence: review workflow tests.
  - [x] S9.13.g Gate completeness, placeholder/markup/reference parity,
        terminology, numbers/units/dates, links, rights, layout/overflow,
        accessibility, and source freshness. Evidence: parity gate tests.
  - [x] S9.13.h Build/export/import locale bundle with source/target revisions,
        stable IDs, statuses, refs, provenance, hashes, and loss report; require
        round-trip identity. Evidence: bundle tests.
        <!-- S9.13: shipped as
        `libs/oshun/workbench-kit/src/localization-jobs.ts` + `.spec.ts` (153
        tests), entry point `./localization-jobs` (152 approved, no drift), plus
        in-place repairs in
        `libs/oshun/studio-authoring/src/localization-workflow/`.

        THE VEIN: A TRANSLATION IS A REWRITE PERFORMED BY SOMEBODY WHO CANNOT
        READ THE PARTS THAT MUST NOT CHANGE. Every localization judgement is
        about a SPAN, and it is one of two exactly opposite judgements -- this
        span must survive byte for byte, or this span must be REBUILT to the
        target language's own grammar -- and every check in this repository was
        asked about the WHOLE STRING, which can express neither. Three greps:
        which checks take the entire sourceText/targetText as their subject;
        which check that guards VERBATIM survival normalises first (case-folds,
        trims, de-duplicates), because that one has erased the property it was
        asked to certify; and which check requires the target to MATCH THE
        SOURCE'S STRUCTURE, for the spans where matching the source is the
        defect.

        Six instances, all measured before anything was written.
        `studio-authoring/.../glossary-editor.ts::enforceGlossaryInSegment` was
        three defects in five lines, all the first grep: it decided a
        do-not-translate term survived with
        `targetText.toLowerCase().includes(term.toLowerCase())`, so `OSHUN`
        against a German target reading `oshun ist bereit` answered `[]`; it
        matched as a substring with no boundary, so the term `Tara` reported
        `do-not-translate-violated` against the source `A tarantula appeared`;
        and it was count-blind, so `OSHUN, OSHUN, and OSHUN again` translated as
        `OSHUN und nichts weiter` also answered `[]`.
        `translation.ts::evaluateLocaleQA` measured expansion over the whole
        segment: `Save` to `Speichern unter` correctly warned at 3.75x, and
        appending the same 96-character URL to BOTH sides made the identical
        defect produce no finding at all. `locale-formats.ts` scanned the whole
        target, so `Bestellnummer 1,234,567 wurde storniert` was reported as a
        number-format leak whose repair rewrites an order number, and
        `https://example.com/docs/3/4/2026` as a retained US date whose repair
        breaks a URL. `translation.ts` also had no identity where a reference
        belongs: `lookupGlossary` took no scope, so a lineage-scoped term and a
        global one were told apart by ARRAY ORDER (measured: the same query
        answered `exercice` and `pratique contemplative` depending on which was
        pushed first); `TranslationMemoryEntry` had four fields, so a match
        returned another customer's sentence at similarity 1 with nothing saying
        whose it was; and a segment built `status: 'approved'` with
        `reviewedByCreatorId: null` scored `launchable: true, coverageRatio: 1`.
        `workbench-kit/src/localized-labels.ts` (S9.4) compares
        `new Set(extractPlaceholders(source))` against the target's set, and a
        set is not parity: the `#` inside a plural branch is invisible, so
        `{count, plural, one {# item selected} other {# items selected}}` and a
        German translation with no `#` at all both extract to exactly
        `["count"]`; the Set destroys multiplicity, so two `{city}` against one
        compare equal; and it strips `{{`/`}}` as the escape, which is Rust's
        and .NET's, while ICU quotes with an apostrophe. The third grep is asked
        nowhere: `Intl.PluralRules` gives the eight `libs/oshun/i18n` launch
        locales two categories for en-US and de-DE, three for fr-FR, pt-BR and
        he, one for ja-JP and SIX for ar, so parity with the source is exactly
        the rule that keeps an Arabic string wrong for 2, 3, 11 and 100 items --
        and the only parameterised customer strings the product produces are
        undeclared, `privacy/src/receipts-feed/receipts-feed.ts` emitting 23 copy
        keys across 18 parameterised messages with 26 parameter names of which
        the catalogue holds ZERO, while the catalogue's own 28 keys carry no
        substitution at all.

        TWELVE DECISIONS. A SEGMENT IS A SEQUENCE OF SPANS AND EACH SPAN HAS A
        DISPOSITION (verbatim, rebuilt, translated). PARITY IS A SEQUENCE OF
        KEYS WITH THEIR MULTIPLICITIES, NEVER A SET, with `#` counted per BRANCH
        and order compared only for the kinds consumed positionally, since
        demanding a named argument keep its position would forbid translating
        into Japanese. THE TARGET'S PLURAL CATEGORIES COME FROM THE TARGET, and
        an exact `=1` selector never stands in for the `one` category, which in
        Russian also matches 21. A VERBATIM CHECK NEVER NORMALISES, so a
        do-not-translate term is `exact` by construction and a term declaring
        otherwise is refused. A TERM OCCURS AT A PLACE, counted on
        `Intl.Segmenter` word boundaries so it works for Japanese, and never
        inside a protected span. PRECEDENCE IS A TOTAL ORDER OVER SIX SCOPES AND
        A TIE IS A REFUSAL. A MEMORY MATCH IS SOMEBODY ELSE'S SENTENCE AND
        ARRIVES WITH ITS OWNER ATTACHED, `unknown` rights being a refusal rather
        than a permission and the locale pair compared on the resolved SCRIPT so
        `zh-Hans` does not serve `zh-Hant`. A PROPOSAL CANNOT BE BORN APPROVED:
        the status is not an input and no transition reaches `approved` except
        from `in-review`. REVIEW IS PER LOCALE AND PER DIMENSION, and a
        credential for a LANGUAGE is not a credential for a LOCALE unless the
        policy says so in a field. A GATE ANSWERS PASS, FAIL OR UNEVALUABLE AND
        SILENCE IS NEVER A PASS. EXPANSION IS MEASURED OVER THE SKELETON. AND A
        LOSSY EXPORT AND A DEFAULTING IMPORT ARE ONE BUG SEEN TWICE -- the
        importer defaults nothing, an absent field is a fault, and a bundle with
        a non-empty loss report may be read and may not be imported back over
        what it came from.

        Scope stated: S9.13.a's "field/unit list" is the segmented unit list
        rather than a second field vocabulary, because S9.4's `LOCALIZED_FIELDS`
        already names the fields and S9.13.b's segmentation is what turns them
        into units. S9.13.c's "segment/hash" is the SKELETON digest, so a match
        implies the same protected spans and can actually be spliced; the raw
        text digest would agree for two texts whose URLs differ. S9.13.d's
        "morphology" is the declared surface-form list rather than a stemmer,
        because a stemmer that guesses is wrong in the language the term was
        added for. S9.13.g's "rights" gate re-reads the memory entry statuses at
        gate time and reports an unresolvable reference as a BLOCK rather than a
        pass; propagating a withdrawal to the derived work is S9.14's
        invalidation and is not claimed here. The four repaired
        `studio-authoring` functions duplicate the minimum span primitive the
        `layer:shared-logic` boundary forces (that package may not import
        `layer:workbench-kit`), the same way S9.4's RTL script set is duplicated
        there.
        -->
        <!-- S9.13 marked after writing and reading
        `libs/oshun/workbench-kit/src/localization-jobs.ts` end to end, running
        its 153 tests, mutating 128 of its decisions and confirming each turns a
        test red, and after running the repaired library's own suite
        (studio-authoring 192, of which 10 pinned the defects above and were
        rewritten, plus 11 new ones) and the whole workbench-kit suite (10418).
        -->
- [x] S9.14 Add cross-locale dependency invalidation. Evidence: source changes
      mark translated dependents stale.
  - [x] S9.14.a Record dependency from each translated field/segment/bundle/
        review/gate/release to exact source field/anchor/revision/hash and
        translation inputs. Evidence: dependency schema.
  - [x] S9.14.b Classify source changes as content, structure, identifier,
        placeholder/markup, link, media, rights/consent, glossary/TM, policy,
        metadata, or nonsemantic. Evidence: change classifier tests.
  - [x] S9.14.c Determine affected locales/segments/references/layout/parity
        gates/catalog/search/releases transitively without invalidating
        unrelated translations. Evidence: blast-radius tests.
  - [x] S9.14.d Mark dependencies stale transactionally with source old/new
        revision, change reason/type, time, and event. Evidence: staleness
        tests.
  - [x] S9.14.e Queue idempotent retranslation/review/parity/reindex/rebundle or
        require human action according to policy and cost. Evidence: workflow
        routing tests.
  - [x] S9.14.f Block publication/use where stale translated content violates
        target policy and surface exact affected locales/segments. Evidence:
        eligibility tests.
  - [x] S9.14.g Reconcile missed invalidation by comparing stored dependency
        versions to authoritative sources. Evidence: repair tests.
        <!-- S9.14: shipped as
        `libs/oshun/workbench-kit/src/cross-locale-invalidation.ts` (entry point
        `./cross-locale-invalidation`, contract runtime, 153 approved entry
        points and no drift), with in-place repairs to
        `libs/oshun/workbench-kit/src/localization-jobs.ts` and to four shipped
        functions in `libs/oshun/studio-authoring`.

        THE VEIN. A DERIVED WORK IS A FUNCTION OF EVERY INPUT THAT WENT INTO IT,
        AND EVERY INVALIDATOR IN THIS REPOSITORY WATCHES THE ONE INPUT THAT
        SHARES ITS NAME. A translation is made from a source span, and it is
        also made from the glossary decisions that constrained its wording, the
        memory entries its sentences were assembled from, the rights those
        entries carry, the policy that judged it, the model version that
        produced it and the asset its alternative text describes. Staleness is
        computed against the source text; the other six are recorded on the
        derived work in full and read by nothing, so a withdrawn sentence stays
        published and certified. And the one input that IS watched is watched as
        a WHOLE, through a single digest, so the answer is the same boolean for
        every dependent: the two failures are opposite and simultaneous, a
        trailing space costing eight locales a full retranslation while a rights
        withdrawal costs nothing. Three greps find it. What is the SUBJECT of
        the staleness comparison -- is it one scalar standing for an entire
        upstream? List the inputs the derived record NAMES, then grep each name
        inside the staleness and gate code; the gap between the two lists is the
        set of ways the artifact can go wrong without anybody hearing. And which
        invalidator can be asked AGAIN -- every one of them is a push at the
        moment of a change, so a miss is permanent and invisible.

        SEVEN MEASURED INSTANCES, all run before anything was written.
        `studio-authoring/src/localization-workflow/translation.ts::detectStaleTranslations`
        over eight locale rows of one unit, with the source moved by a single
        trailing space, answers `stale count: 8 of 8`, every reason
        `source-text-changed-since-translation`, and its verdict has four fields
        -- `segmentId`, `state`, `stale`, `reason` -- of which none can say WHAT
        changed, so a corrected typo and a rewritten section cost the same eight
        retranslations. The same function over a unit whose memory entry was
        withdrawn that morning answers `{state:'fresh', stale:false,
        reason:null}` and `buildLocaleLaunchScorecard` downstream answers
        `launchable:true, coverageRatio:1`; `TranslationSegment` has nine fields
        and names no input except a digest of the source text, so there is no
        field on the row a withdrawal could ever have been matched against.
        `editorial-lifecycle/staleness.ts::detectStaleArtifacts` reads
        `sourceLastChangedAtUnixSeconds !== undefined`, so an artifact whose
        drift signal was never wired reports exactly like one whose source has
        not moved: measured with two published articles reviewed ten seconds ago
        -- one with no signal, one whose source changed in the review's own
        second, since the comparison is `>` -- the sweep returns `[]`, and
        `StaleArtifact` carries a reason named `source-drift` and no field
        naming which source. `asset-metadata/metadata.ts::buildAssetReplacementCascade`
        returns four fields whose invalidation is `input.asset.usedByArtifactIds`,
        with no old or new revision, no reason, no classification and no locale,
        so replacing a photograph with a re-encode of the same photograph and
        replacing it with a different photograph produce byte-identical
        cascades. `asset-metadata/approval-queue.ts::surfaceBrokenLinks` guards
        its rights alarm with `asset.usedByArtifactIds.length > 0`: measured, an
        asset whose licence expired four months ago raises one signal when a
        usage row happens to be present and ZERO when the list is empty -- and
        the list is empty for every asset in the product, because
        `usedByArtifactIds: []` in `asset-metadata/bulk-upload.ts` is the only
        assignment to that field in the repository, so a condition that reads as
        a filter is an off switch on the one class of change that cannot be
        deferred. `workbench-kit/localization-jobs.ts::runLocalizationGates`
        does re-read memory entry status at gate time and compares `status`
        only, while the `entryVersion` carried on the `MemoryMatchRef` beside it
        is compared to nothing anywhere in the module, so an entry that was
        CORRECTED -- same id, new version, still `active` -- passes the rights
        gate. And `glossaryRefs` is written by `admitTranslationProposal`,
        carried through `buildLocaleBundle`, validated by `importLocaleBundle`,
        and appears ZERO times inside `runLocalizationGates`.

        ELEVEN DECISIONS. A DEPENDENCY NAMES A PART OF A NAMED REVISION OF A
        NAMED OBJECT AND EVERY INPUT CLASS IS A DEPENDENCY -- seven input kinds,
        each carrying an identity and a VERSION, because a reference with no
        version can never be shown to have moved. AN ANCHOR THAT MEANS A
        POSITION IS NOT AN ANCHOR: `paragraph-2`, `3`, `node-1` are refused at
        admission, since inserting a unit above one of them rebinds it to
        somebody else's sentence without moving any digest it owns. A CHANGE IS
        A SET OF FACTS ABOUT PARTS, NEVER A LABEL ON A WHOLE, each fact naming
        its anchor, and one edit routinely produces several of different
        classes. `nonsemantic` IS A CLAIM ABOUT THE RENDERED RESULT UNDER A
        DECLARED NORMALIZATION AND THE ROLE DECIDES WHICH NORMALIZATIONS APPLY,
        so a whitespace fold is invisible in a paragraph and CHANGES THE
        MEASUREMENT of a capped `button-label`, `unicode-nfc` changes what a
        `keyword` matches, and a change touching a protected span is never
        nonsemantic however the prose compares. SILENCE IS NEVER A PASS, SO
        THERE IS A TWELFTH CLASS: `unclassified` is what a digest disagreement
        nothing else explains becomes, and it routes to a human and blocks under
        every tolerance, because a change nobody could describe is not one
        anybody can price. A BLAST RADIUS IS COMPUTED FROM THE PART AND CROSSES
        A LOCALE BOUNDARY ONLY WHERE THE TARGET SAYS IT DOES -- a fact reaches a
        dependency when the dependency's input covers the fact's anchor, and the
        same argument-reference edit is a parity re-check for `ja-JP`, which
        owes one plural branch, and a retranslation for `ar`, which owes six.
        THE ACTION IS PART OF THE FINDING AND THE CLASSES DO NOT SHARE ONE:
        retranslate, resegment, rebind, re-splice, re-check parity, re-check
        terminology, replace, re-gate, reindex, rebundle, re-review the
        alternative text, triage. AN IGNORED CHANGE STILL MOVES THE WATERMARK,
        with the reason recorded beside it, because a reconciler that keeps
        rediscovering an edit somebody already decided to ignore is a reconciler
        that gets switched off. AN IDEMPOTENCY KEY NAMES THE RESULT AND NEVER
        THE EVENT: automatic execution is a permission the policy grants per
        action and per locale, a locale with no policy has granted nothing, an
        unpriceable action is a human decision rather than a cheap one, and a
        rights change is never queued at all. AN ELIGIBILITY ANSWER NAMES THE
        UNITS, per locale, in three verdicts, with the blocked ones carrying
        exact unit ids rather than a count and `undecidable` reserved for a unit
        in scope that no dependency covers. AND A PUSH INVALIDATOR'S MISSES ARE
        INVISIBLE, SO THE ONLY HONEST ONE CAN BE ASKED AGAIN --
        `reconcileDependencies` compares stored input versions against the
        authoritative ones and reports a missed invalidation, an unresolvable
        input, a mark queued against a revision already superseded, an orphan
        mark and a consumed input no dependency names, and reports the COVERAGE
        of the snapshot it was given, because a sweep over half the inputs that
        finds nothing has found nothing about the other half.

        REPAIRS IN PLACE. `detectStaleTranslations` now takes the recorded
        non-source inputs and an input index and answers over both dimensions,
        with every finding naming its input and the two versions;
        `TranslationSegment.inputs` is `null` when nobody recorded them, which
        is `unverifiable` rather than `fresh`, and the index's `coverage` field
        is what tells an input that is GONE from one the index could not answer
        about. `detectStaleArtifacts` takes a list of sources rather than an
        optional timestamp, reports `drift-unverifiable` for an unwired
        artifact, compares with `>=` so an edit in the review's own second is
        flagged, and names the drifted sources. `buildAssetReplacementCascade`
        classifies the replacement against the perceptual hash
        (`identical-bytes` / `re-encode` / `different-media` / `unclassifiable`,
        the last because guessing cheap would waive an accessibility re-review
        nobody performed), carries both versions and a reason, and takes its
        dependents from an enumeration it was GIVEN with the coverage that
        enumeration can honestly claim. `surfaceBrokenLinks` no longer gates on
        the producerless usage list: an expired licence raises its signal and
        stays quiet only when a COMPLETE enumeration says the asset is unused.
        And the rights gate compares `entryVersion`, blocking when an active
        entry's version cannot be read and warning when it has moved.

        Scope stated: S9.14.b's `placeholder/markup` class is named
        `argument-markup`, keeping S9.13's vocabulary -- an argument reference
        is what the syntax holds -- and because the repository's own stub scan
        greps the other word. S9.14.c's `catalog/search` propagation is the
        `reindex` action on the `metadata` class rather than a second index
        model, because S9.11 already owns the index event and its projection.
        S9.14.e's cost is a per-action unit price against a per-locale budget
        rather than a scheduler, since the queue is the host's. S9.14.g's repair
        marks only the MISSED invalidations: an unresolvable input and an
        unrecorded dependency both need somebody to decide what the dependent
        should have pointed at, and inventing a mark for either would be the
        invalidator asserting a fact it does not have. The `TranslationInputRef`
        kinds duplicated in `studio-authoring` are the minimum the
        `layer:shared-logic` boundary forces (that package may not import
        `layer:workbench-kit`), the same way S9.4's RTL script set and S9.13's
        span primitive are duplicated there.

        Pre-existing and untouched: `generate-workbench-kit-api-reference.mjs`
        was already red at HEAD with 86 `undocumented-module` violations, one
        for every S9.x kit module since S9.4, and this module is the 87th of
        that same class; `check-conventions-ratchet.mjs` was already red at HEAD
        on `test-dot-test-suffix` for six committed `*.test.ts(x)` files, none
        of them touched here.
        -->
        <!-- S9.14 marked after writing and reading
        `libs/oshun/workbench-kit/src/cross-locale-invalidation.ts` end to end,
        running its 133 tests, mutating 138 of its decisions and confirming each
        one turns a test red, and after mutating the five repaired shipped
        functions 41 more times with the same result -- 179 mutations, 179
        caught. Suites run: workbench-kit 10554 across 149 files (of which
        localization-jobs 155, two of them new), studio-authoring 211 (of which
        16 new, six rewritten where they pinned the defects above, and the rest
        given the two fields the records grew).
        Gates run by hand: the stub-indicator scan over every changed file
        (zero hits), eslint, prettier, `tsc --noEmit` on both `tsconfig.lib.json`
        and `tsconfig.spec.json` in both libraries proved with a deliberate-error
        canary, and `generate-workbench-kit-export-map.mjs --check` (no drift,
        153 entry points, no violations).
        -->
- [x] S9.15 Add accessible calendar, catalog, and taxonomy UI primitives.
      Evidence: keyboard-only full journeys.
  - [x] S9.15.a Build calendar agenda/list/grid views with semantic date/time/
        zone/resource/slot/status/capacity/conflict data and synchronized
        selection. Evidence: calendar parity tests.
  - [x] S9.15.b Provide keyboard create/navigate/select/move/resize/recurrence/
        exception/resource/commit/cancel and exact form alternatives to drag.
        Evidence: keyboard calendar journey.
  - [x] S9.15.c Build catalog table/list/collection/feature/rank/eligibility/
        audience views with preview/diff/review/publish state. Evidence: catalog
        semantic tests.
  - [x] S9.15.d Provide keyboard curation, ordering, targeting, eligibility
        inspection, preview, review, and bundle publication journey. Evidence:
        keyboard catalog journey.
  - [x] S9.15.e Build taxonomy tree/list/search/detail/impact/diff/lifecycle/
        translation views with nonvisual hierarchy and counts. Evidence:
        taxonomy semantic tests.
  - [x] S9.15.f Provide keyboard add/rename/move/merge/split/alias/deprecate/
        migrate/review/publish journey with cycle and impact feedback. Evidence:
        keyboard taxonomy journey.
  - [x] S9.15.g Implement focus restoration, announcements, noncolor status,
        zoom/reflow, RTL/time-zone/locale, virtualization, conflict/offline/
        degraded states across all three. Evidence: cross-primitive a11y matrix.
        <!-- S9.15: shipped as `libs/oshun/workbench-kit/src/domain-views.ts`
        (entry point `./domain-views`, 154 approved, no drift),
        `apps/oshun/web/src/components/workbench/{CalendarViews,CatalogViews,
        TaxonomyViews}.tsx` + module CSS, and
        `apps/oshun/web/tools/domain-views-browser.mjs`.

        THE VEIN: A VIEW IS A SAMPLE AND EVERY SENTENCE IT WRITES IS ABOUT THE
        POPULATION. Three greps -- find the count and ask whose set it is over;
        find the CONTROL whose options are derived from the rows it happens to
        hold; find the field that NAMES the omission and grep it in the render.
        Eight measured instances across four shipped surfaces, all repaired in
        place. `StudioTaraWorkbenchCalendar.tsx` declared `ProgramEntry.clipped`
        and read it NOWHERE while its lane summary counted the truncated slot
        list and printed it as a number; declared `reservations` and read that
        nowhere either, so a whole lane of locked editorial commitments was
        fetched and never drawn by a component whose own comment said "every
        dated item"; bucketed days with `toISOString().slice(0, 10)`, which is
        the UTC day and is the reader's day in exactly one zone, and took its
        window from `getUTCFullYear()` on a LOCAL `new Date()`, so "This month"
        was the wrong month on the last day of one in a positive offset; and
        `reservedByCycle.get(cycle) ?? 0` printed "reserves 0 commitments" for a
        cycle the planner never answered about.
        `StudioTaraWorkbenchCatalog.tsx` built its category, teacher and series
        dropdowns from the rows the SERVER had already filtered, so choosing a
        category collapsed that control to one option and the door locked behind
        the reader, while a browser-side teacher filter could stay ON with no
        control anywhere showing it; and it printed `{filtered.length} of
        {state.total} items`, two denominators in one sentence.
        `StudioTaraWorkbenchCollectionEditor.tsx` wrote `position: index` to the
        server for a `curated` collection, drew the rank as `{index + 1}.` in a
        span, and offered NO way to change it -- not a keyboard way, no way.
        `StudioTaraWorkbenchCategories.tsx` opened its editor as a form appended
        below the whole list, moved focus nowhere and announced nothing.

        Decisions: EVERY COUNT IS AN S9.3 `ImpactCount` AND NEVER A NUMBER,
        rendered through `formatCount`. A VIEW'S POPULATION IS RETURNED WITH ITS
        ROWS, computed by the same pass that placed them, so the count cannot
        disagree with the drawing, and the seven `POPULATION_LIMITS` are derived
        from the refusals rather than declared. A SHORTFALL THE READER CAN UNDO
        IS AN OFFER AND ONE THEY CANNOT IS A DISCLOSURE. A FACET BUILT FROM THE
        RESULT AND APPLIED AT THE SERVER IS A ONE-WAY STREET, and a selected
        value absent from its own option list is a filter that is on and
        invisible. PARITY IS NOT "THE SAME ROWS APPEAR": every entry leaves a
        projection through one of two doors, and an entry every view refuses is a
        commitment no reader can reach. AN INSTANT IS NOT A ZERO-WIDTH INTERVAL
        -- the half-open overlap test compares a point against itself, so a due
        date sitting exactly on the window's start is the one item the range says
        it does not contain. A ROW IS NOT AN ENTRY. AN UNDATED ROW HAS NO DAY. A
        DAY IS READ IN A NAMED ZONE AND WALKED AS A CALENDAR DATE. A SELECTION
        THAT SURVIVES A VIEW SWITCH IS THE DANGEROUS HALF, so a member the target
        view cannot address is WITHHELD -- kept, counted in the action scope and
        disclosed -- and DROPPED only when the row is gone. A CATALOG ROW UNDER
        REVIEW IS TWO ROWS. AN ORDER OPERATION IN A FILTERED VIEW IS AMBIGUOUS,
        so a relative move is refused with the absolute alternative named.
        `aria-setsize` HAS ITS OWN UNKNOWN AND IT IS -1. A SEARCH VIEW DROPS THE
        HIERARCHY. A CYCLE REFUSAL NAMES THE ANCESTOR and a committed key-meaning
        change names its blast radius, both against S8.1's sixty-second ceiling.
        AND THE MATRIX IS ELEVEN CONDITIONS TIMES THREE FAMILIES, thirty-three
        cells, where the eight a browser can settle may not be answered by a
        declaration.

        THE BROWSER FOUND FOUR THINGS NOTHING ELSE COULD. A ROVING TABINDEX
        WITHOUT A COMPOSITE ROLE is a set of rows most of which are not in the
        tab order with nothing telling a reader to press an arrow -- measured as
        one reachable entry out of five, and fixed by making the panel a
        multi-select listbox whose reachability proof is the ARROW walk. A KEY
        THAT IS A SPACE CANNOT SURVIVE A SPACE-JOINED ATTRIBUTE, so `data-keys`
        is JSON. `inline-size: 100%` ON A PADDED, BORDERED ROW OVERFLOWS ITS
        PARENT BY EXACTLY THE PADDING PLUS THE BORDER -- 326 against 304 at 320px
        and 1286 against 1264 under `dir="rtl"`, the same 22px both times. And
        the field a reader sees and the value a form submits came from two
        places, so the absolute-position control submitted an empty string.

        Scope stated: the calendar family models a `CalendarEntry` rather than
        requiring S9.5's `CalendarSlot`, because an agenda shows program windows,
        dossier due dates and agent cycles beside bookings; `entryFromSlot` is
        the composition point so a slot's decisions are not made twice. S9.15.b's
        move and resize reach S9.7's `proposePlacement` through the host rather
        than being re-decided here. S8.1's `treeKeyboardFaults` already owns the
        generic tree keyboard pattern, so the taxonomy family adds only the
        operations S9.15.f names that a generic tree does not have. -->
        <!-- S9.15 marked after writing and reading
        `libs/oshun/workbench-kit/src/domain-views.ts` end to end, running its
        224 tests, and mutating 203 of its decisions -- 203 caught, nineteen of
        them only after the spec gaps the first pass exposed were closed and four
        equivalent mutants were re-aimed. The fourteen in-place repairs to the
        four shipped studio surfaces were mutated back to what shipped: 14 of 14
        caught. 217 mutations, 217 caught.
        Suites run: workbench-kit 10779 across 150 files (of which
        `domain-views` 224), and 55 jsdom tests across
        `DomainViews.spec.tsx`, `StudioTaraWorkbenchCurationRepairs.spec.tsx` and
        `StudioTaraWorkbenchCalendar.spec.tsx`.
        Real Chromium: `apps/oshun/web/tools/domain-views-browser.mjs`, 69
        controls -- three keyboard-only journeys plus the 33-cell matrix, every
        cell measured -- with both dist-patch directions verified (patching
        `commandFaults` in `dist` turned three verdicts red, patching
        `reachabilityFaults` turned another red, and restoring returned all 69).
        Gates run by hand: the stub-indicator scan over every changed file (zero
        hits beyond pre-existing `// stub:legitimate` input placeholders),
        eslint, prettier, `tsc --noEmit` on `tsconfig.lib.json`,
        `tsconfig.spec.json` and `apps/oshun/web/tsconfig.wbcheck.json` -- all
        three proved with a deliberate-error canary, and the four repaired studio
        components ADDED to `tsconfig.wbcheck.json`, which is the first time they
        have been typechecked at all -- and
        `generate-workbench-kit-export-map.mjs --check` (no drift, 154 entry
        points, no violations).
        Pre-existing and untouched: `generate-workbench-kit-api-reference.mjs`
        was already red at HEAD with 87 `undocumented-module` violations, one for
        every S9.x kit module since S9.4, and this module is the 88th of that
        same class; `tools/conventions/check-conventions-ratchet.mjs` was already
        red at HEAD on `test-dot-test-suffix` for six committed `*.test.ts(x)`
        files, none of them touched here, and every file added by this item is
        `.spec.ts(x)`. -->
- [x] S9.16 Add exportable calendar and catalog audit snapshots. Evidence:
      stable schema and authorization tests.
  - [x] S9.16.a Define versioned snapshot envelope with type/scope/query/time
        window/target, source versions, generated actor/time, schema, redaction
        policy, counts, pagination completeness, and hash. Evidence: envelope
        schema.
  - [x] S9.16.b Export calendar resources/capacity, slots/recurrence/exceptions,
        states, dependencies, blackouts/embargo, conflicts, actors, changes, and
        audit refs. Evidence: calendar snapshot fixtures.
  - [x] S9.16.c Export catalog/collection/relation/feature/rank/eligibility/
        audience, bundle/release, target verification, correction/withdrawal,
        actors, changes, and audit refs. Evidence: catalog snapshot fixtures.
  - [x] S9.16.d Apply tenant/object/property/protected/embargo/audience role and
        purpose authorization at query and serialization. Evidence: ordinary/
        auditor cross-tenant negative tests.
  - [x] S9.16.e Redact or pseudonymize content/identity according to snapshot
        purpose while preserving stable internal refs only for authorized
        exports. Evidence: canary data tests.
  - [x] S9.16.f Generate deterministically with stable order/format/time
        semantics and sign/checksum immutable output. Evidence: reproducibility
        tests.
  - [x] S9.16.g Verify import/read tooling, schema compatibility, complete page
        capture, retention/legal hold, and audit of export/download. Evidence:
        snapshot lifecycle tests.
        <!-- S9.16: shipped as `libs/oshun/workbench-kit/src/audit-snapshots.ts`
        (entry point `./audit-snapshots`, 155 approved, no drift), two committed
        goldens with pinned canonical digests under
        `libs/oshun/workbench-kit/schemas/audit-snapshot-{calendar,catalog}.golden.json`,
        and in-place repairs to `apps/oshun/bff/src/routes/admin-audit-log.ts`,
        `apps/oshun/bff/src/admin/admin-audit-events-store.ts`,
        `apps/oshun/bff/src/routes/tenant-console-reads.ts`,
        `libs/oshun/tenant-console/src/bulk-ops/bulk-export.ts`,
        `apps/oshun/bff/src/routes/tara-workbench.ts` and
        `apps/oshun/web/src/components/studio/StudioTaraWorkbenchCalendar.tsx`.

        THE VEIN: A VIEW IS READ BY SOMEBODY WHO CAN ASK AGAIN AND A SNAPSHOT IS
        READ BY SOMEBODY WHO CANNOT. S9.15's reader is present at the moment of
        the read and can widen the filter, press page two or notice the spinner;
        this reader is a regulator, an auditor or the same operator in eighteen
        months, and the bytes are the only witness they will ever have. So every
        question they will ask has to be answerable FROM THE DOCUMENT, including
        the two a live view never has to answer: is this all of it, and what was
        the question. Three greps -- find every LIMIT on the export path and ask
        what in the output discloses it; find every predicate that decided
        membership and ask whether the document repeats it; find every number,
        hash and instant and ask whether a reader can re-derive it from the
        bytes. Ten measured instances across six shipped surfaces.
        `admin-audit-log.ts`'s investigation export called
        `listAcrossOperators({ ..., limit: 500 })` and wrote
        `## Matched events (${events.length})` into the Markdown an investigator
        downloads, so the size of the SAMPLE was printed as the size of the
        population with no hasMore, no total, no second page and no parameter
        that would fetch one -- and the audit event the export itself writes
        recorded `matchedEventCount: matched.length`, which is 500 again. It
        truncated from the WRONG END, because the store sorts
        reverse-chronologically before slicing, so what fell off was the
        beginning of the incident. It resolved bookmarks with
        `.map(readEvent).filter(e => e !== null)` and rendered the bookmark
        section only `if (bookmarkedEvents.length > 0)`, so an investigation
        whose ten bookmarks had aged out of the ring buffer exported a document
        in which "there were none" and "there were ten and they are gone" were
        the same bytes. `recordInvestigationAuditEvent` put the DECLARED
        bookmark count in its base payload and the export's extraPayload
        overwrote it with the RESOLVED count, so one field name carried two
        populations depending on which action wrote the row -- and a shipped
        test asserted `bookmarkedEventCount: 0` for an investigation with one
        bookmark, locking that in. The Markdown carried the title, two dates and
        a list of events and NOT THE FILTER, so the one artefact that leaves the
        building could be neither reproduced nor challenged; it carried no
        generated instant while the JSON did, and neither carried a checksum.
        `listAcrossOperators` ordered by
        `b.occurredAt.localeCompare(a.occurredAt)` before slicing, which is both
        S6.5's locale-dependent comparator and S9.9's
        `limit-over-non-total-order` -- a limit over a tied order does not
        truncate a list, it CHOOSES between the tied rows by arrival order.
        `tenant-console-reads.ts` read `listAcrossOperators({ limit: 5000 })`,
        filtered the result to this tenant and sliced again: the limit clamps to
        500, so the filter ran over the 500 most recent events across EVERY
        operator, and a tenant with hundreds of events saw an empty cockpit
        that is indistinguishable from having none -- A FILTER APPLIED TO A PAGE
        IS A FILTER OVER THE WRONG POPULATION, so the tenant predicate is now a
        field on the store's query.
        `@oshun/tenant-console`'s `bulk-export.ts` sorted the records inside
        every exported file and the file list inside the manifest with
        `localeCompare`, and both feed a sha256, so the integrity manifest of a
        data export was a function of the exporting process's LANG; and its
        `verifyManifest` walked `manifest.files` checking each was present with
        the right bytes, so a file in the download and absent from the manifest
        verified clean, while `totalRecordCount` was never compared against the
        sum of the per-file counts -- the chunks were never required to TILE the
        population. `tara-workbench.ts`'s calendar feed enumerated agent cycles
        with `for (let i = 0; i < 366 && cursor <= toUnix; i += 1)` and said
        nothing when the cap bit, and `StudioTaraWorkbenchCalendar.tsx` printed
        that lane's size as an `exact` count.

        Decisions: A COUNT IS A TRIPLE AND NEVER A NUMBER -- matched as an S9.3
        `ImpactCount` because a truncated read honestly knows only `at-least`,
        emitted as what is in the bytes, withheld as the shape of the hole
        itemised by reason -- and `countAccounts` is the invariant tying them
        together. COMPLETENESS IS A PROOF AND NOT A FLAG: the envelope carries
        the PAGES with their row counts and their start and end cursors, and the
        claim is re-derived rather than trusted, so a page fetched from
        somewhere the previous page did not end is a torn read whose missing
        rows are in no page at all. THE WHOLE DOCUMENT IS READ AS OF ONE MOMENT
        OR IT IS NOT A SNAPSHOT, which is the other side of S4.9's admission
        that a keyset traversal without one may miss and repeat rows and of
        S1.5's note that offset paging is correct only over a frozen snapshot.
        AN OMITTED SECTION AND AN EMPTY SECTION ARE DIFFERENT FACTS, so absence
        is a value with a reason. A SNAPSHOT IS A CLOSED WORLD: an id it
        mentions and does not contain is a question its reader cannot go and
        ask, because the system has moved on. AUTHORIZATION IS ASKED TWICE AND
        THE SECOND TIME IS THE ONE PEOPLE SKIP, because a snapshot is
        DENORMALISED and every embedded actor, conflicting slot and audience
        definition is a second read the query's admission check never saw. THE
        TARGET IS NOT THE SCOPE -- the scope decides which rows are selected and
        the target decides how much of each may be shown, whether two exports
        may be joined, and who has to be named when the document turns up
        somewhere it should not be. A PSEUDONYM IS A JOIN KEY, so the salt is
        per-snapshot unless a named authority approved linkage; and one subject
        under two codenames splits a person while one codename over two subjects
        MERGES them, which is not a loss of resolution but a false statement
        about conduct. THE HASH COVERS THE DOCUMENT AND NOT A HAND-WRITTEN LIST
        OF ITS FIELDS. AND A HOLD OUTRANKS AN EXPIRY WHILE A DOWNLOAD IS ITS OWN
        EVENT: a retention schedule that wins over a legal hold destroys
        evidence on time, an operator who can release the hold on the evidence
        about them has a delete button with extra steps, and generating a
        document is a decision while handing over the bytes is the disclosure.

        Scope stated: the module is `contract`-class, so the stores, clocks,
        hash functions and readers it is decided against are the host's; S9.9's
        `orderFaults`/`orderIsTotal`/`compareCodePoints`, S8.12's embargo
        standing, exposure and audience ceilings, S9.5's `admitInterval` and
        S9.3's `ImpactCount` are composed rather than re-decided. The audience
        ceiling needed one named exception: `learner-record` tops out at
        `object-collaborators` and `auditors` ranks above it on a ladder that
        counts PEOPLE, so `regulatory-audit` and `legal-discovery` license that
        one band and nothing wider. -->

### S10 — Observability, reliability, privacy, and cost

- [x] S10.1 Define shared OpenTelemetry resource and span attributes. Evidence:
      schema lint rejects unbounded content attributes.
  - [x] S10.1.a Define resource attributes for service/library, version, domain/
        workbench, host/runtime, environment, region, deployment, worker/
        provider class, and tenant-safe scope. Evidence: resource schema.
  - [x] S10.1.b Define span naming and attributes for UI navigation/action,
        HTTP/ RPC, command/query, database, outbox/inbox/event, job,
        provider/model, blob/bundle, review/gate, publish/verify, and bridge.
        Evidence: span schema.
  - [x] S10.1.c Define status/error/retry/cancel/timeout/partial/unknown
        outcome, correlation, version, target, usage, and cost attributes with
        units. Evidence: semantic convention tests.
  - [x] S10.1.d Allowlist bounded values and explicitly prohibit raw IDs,
        content, prompts, URLs/paths, filenames, stack/error messages, PII,
        protected identity, biometric/learner data, and secrets. Evidence:
        attribute policy.
  - [x] S10.1.e Set cardinality and length budgets, unknown/other behavior, and
        schema version/deprecation ownership. Evidence: lint configuration.
  - [x] S10.1.f Add compile/runtime lint and canary-data tests that reject
        unregistered or unbounded attributes. Evidence: deliberate violation
        test.
        <!-- S10.1: shipped as `libs/oshun/workbench-kit/src/telemetry-attributes.ts`
        (entry point `@oshun/workbench-kit/telemetry-attributes`, contract-class,
        156 approved entry points, no drift) with a 193-test spec, plus repairs
        to `@oshun/tracing` and a 14-test regression spec
        (`libs/shared/tracing/src/attribute-hygiene.spec.ts`).

        THE VEIN: WHAT AN ATTRIBUTE COSTS AND WHAT IT DISCLOSES ARE BOTH DECIDED
        BY ITS VALUE, AND EVERY CHECK IN THIS REPOSITORY WAS WRITTEN ABOUT ITS
        NAME. A name is what the author chose; a value is what the world
        supplies. And the same value costs different amounts depending on where
        it lands: on a span attribute a distinct value is an ADDEND -- one field
        on one record, dropped when the span ages out -- while on a metric
        dimension it is a MULTIPLIER, a new time series against every other
        dimension, retained for the metric's whole retention and paid for at
        every collection interval whether or not anything happened; on a
        resource it multiplies every signal the process emits. The API is
        `setAttribute(key, value)` in all three places and the economics are
        three orders of magnitude apart, so "is this attribute bounded" is not a
        question about the attribute, it is a question about the PAIR. Telemetry
        is also the only output in this system with no boundary check -- a
        response has a schema, a write has a column type, S9.16's export has an
        envelope, and a span has the backend creating a column for whatever
        arrives -- so the producer is the only enforcement point that exists,
        and the producer is every file in the repository.

        Twelve measured instances across five shipped surfaces.
        `libs/shared/tracing/src/otlp-metrics.ts` opens with "Real OTLP/HTTP
        metric export for BOUNDED, metadata-only metric points" and then appends
        `'oshun.observed_at_unix_ms': new Date(point.observedAt).getTime()` to
        every point's dimension set -- a distinct value per observation, so
        every point exported creates its own time series, forever, added by the
        export path where no caller asked for it and no caller can turn it off.
        A histogram with a unique dimension per observation is a log with a
        worse query language and a longer retention. The same file's
        `validatePoint` is the repository's ONLY content prohibition and tests
        `/(conversation|transcript|...|prompt|source\.body)/iu` against
        `Object.keys(point.attributes)`, never the value, so `{ q: <the text the
        user typed> }` is admitted and `{ 'queue.message_count': 5 }` is refused
        -- unsound in one direction, incomplete in the other, and defeated by
        renaming in either. That prohibition is never applied to the RESOURCE,
        which is the set attached to EVERY point, and `...input.resourceAttributes`
        is spread LAST after `service.name`, so a caller can relabel its
        telemetry as another service's -- identity is the one thing a caller
        must not assert about itself. The same file keys its instrument cache on
        `${point.name}:${point.unit}`, so one metric recorded once as `ms` and
        once as `s` does not fail but FORKS into two instruments with one name
        whose values differ by a thousand; and it calls `meter.createHistogram`
        for every point because `OtlpMetricPoint` has no field in which any
        other instrument kind could be named -- the kind is not chosen wrongly,
        it is UNSTATABLE, which is the S8 family again.
        `libs/shared/tracing/src/middleware/hono.ts`, on the real BFF request
        path, sets `'http.url': c.req.url` -- the full URL INCLUDING THE QUERY
        STRING, so every search term, filter, reset link and signature ever
        passed as a query parameter is copied verbatim to a vendor that retains
        it on a schedule nobody here controls and indexes it for staff who were
        never granted the tenant. It sets `'net.peer.ip'` from
        `x-forwarded-for`, which is personal data in its own right and
        attacker-supplied besides, alongside `http.user_agent` from an
        eight-kilobyte header and `request.id` from a CLIENT-SUPPLIED
        `x-request-id`, so the length of that attribute is chosen by whoever is
        calling. Its `generateSpanName` falls back to
        `${c.req.method} ${url.pathname}`, putting a concrete path into the
        primary grouping key of every APM product -- one operation per object --
        at the exact place a template was in scope. That middleware also
        declares `recordRequestBody`, `recordResponseBody` and `maxBodySize`
        ("default: 10KB") and reads NONE of the three: the bodies were never
        recorded, which is the safe outcome arrived at by accident, and an
        operator who wrote `recordRequestBody: false` had turned off something
        that was never on while believing they had made a decision. Raw error
        text reached attributes in six places (`hono.ts` status message,
        `tracer.ts` twice including `exception.stacktrace`, `decorators.ts`
        twice, `middleware/index.ts` once), and an exception message is a string
        assembled at the failure site out of exactly the values that were in
        scope when things went wrong. And
        `libs/oshun/analytics/src/tracing-manifest.ts` says downstream SDKs
        "compile this manifest into their span builders", that
        `resolveSpanAttributes` is "used by tracers to validate emitted
        attribute sets at boot time" and that `isPii` is "used by sanitizers",
        while NOTHING outside the file imports any of them -- six hundred lines
        of convention with no reader is not a schema, it is a document that
        happens to compile; and its `type: 'string'` vocabulary has no field in
        which an enumeration could be written, so BOUNDED -- the one word in
        this item's evidence line -- is inexpressible in it even if somebody did
        read it.

        Decisions. ADMISSION IS ASKED ABOUT A PAIR AND NEVER ABOUT AN ATTRIBUTE,
        because `tenant.id` is correct on a span, ruinous as a dimension and
        false on a resource shared by many tenants. BOUNDED IS A STATEMENT ABOUT
        A SET, SO THE REGISTRY HAS TO BE ABLE TO WRITE THE SET DOWN: enum,
        boolean, quantity with a unit, bucketing with its boundaries, template
        with its parameters, pattern, opaque identifier, pseudonym or free text
        -- and only the ones with a computable cardinality may be a dimension.
        CARDINALITY IS A PRODUCT AND BUDGETS ARE SPENT BY THE METRIC, since four
        dimensions of five, eight, three and twelve are 1,440 series and each
        looked modest in review. A DISTINCT-VALUE COUNT IS AN S9.3
        `ImpactCount`, because coercing an unknown to a number is the arithmetic
        that keeps an estimate small right up until the bill arrives. A RESOURCE
        IS BOUNDED BY THE FLEET AND A DIMENSION BY ITS DOMAIN -- which is why
        `service.instance.id` is correct on one and ruinous on the other -- so
        the resource's real question is not "is this small" but "is this fixed
        for the lifetime of the process". FOLDING TO `_other_` IS A TRUNCATION
        AND AN UNDISCLOSED TRUNCATION IS A FALSE STATEMENT ABOUT THE POPULATION
        (S9.15, S9.16), so folds are COUNTED -- and the ledger that counts them
        is itself capped, because a ledger keeping every distinct folded value
        in order to count them grows exactly as fast as the cardinality it
        exists to prevent, which is why its distinct count is `at-least`. THE
        SPAN NAME IS AN ATTRIBUTE WITH NO KEY, which is how a concrete path
        survives in a library that has a prohibition list. AN IDENTIFIER IS A
        TOKEN AND A SENTENCE IS NOT A TOKEN, because an opaque-id slot accepts
        any string by construction and is otherwise the widest hole in the
        schema -- found by the regression tests, not by design. AND A
        PROHIBITION IS TESTED AGAINST VALUES WITH CANARIES, POSITIVE AND
        NEGATIVE, since a corpus of nothing but known-bad strings is passed
        perfectly by a detector that refuses everything, and such a detector is
        indistinguishable from a working one until it has refused enough
        legitimate traffic that somebody switches the lint off.

        Repairs shipped in `@oshun/tracing`: the per-observation timestamp
        dimension deleted; the content prohibition extended to VALUES and to the
        resource; identity sealed by spreading caller attributes first; the
        instrument cache keyed on the name alone with unit and kind
        disagreements refused as conflicts; `OtlpInstrumentKind` made statable;
        `http.url` and `net.peer.ip` removed entirely and replaced by the route
        SHAPE via a new `templatizePath`; user agent truncated and
        `x-request-id` accepted only as a bounded token; the three
        never-read body controls replaced by one that is read; and every raw
        error message and stack trace replaced by `errorClassName`. One shipped
        test asserted the defect (`status.message` `toBe('Test error')`) and now
        asserts the class.

        Verification: 193 kit tests, 88 tracing tests, 0 typecheck errors, no
        export-map drift. A 22-mutation sweep over the kit module caught 19; the
        three survivors were each proven EQUIVALENT rather than untested by
        mutating the redundant partner (removing the sealed-key rejection is
        caught; making the shared-regex state observable by breaking the scan
        loop early is caught; the FNV per-iteration `>>> 0` is equivalent by
        congruence mod 2^32, since every subsequent `^` and `<<` re-coerces).

        Scope stated: the module is `contract`-class, so the tracers, meters,
        clocks and exporters it is decided against are the host's; S9.3's
        `ImpactCount` and S9.9's `compareCodePoints` are composed rather than
        re-decided. The `@oshun/tracing` repairs are self-contained rather than
        importing the kit, because `libs/shared` is the lower layer and must not
        depend on `libs/oshun`. -->
        
- [x] S10.2 Instrument browser-to-worker trace propagation. Evidence: one
      synthetic journey has an unbroken trace.
  - [x] S10.2.a Create/continue W3C trace context at trusted browser/desktop
        ingress and validate sampling/baggage propagation without accepting
        spoofed internal identity. Evidence: ingress tests.
  - [x] S10.2.b Propagate through web navigation/action, BFF request,
        actor/auth/ authorization, domain service, database/blob/cache/search,
        and response. Evidence: synchronous trace tests.
  - [x] S10.2.c Persist trace/correlation in outbox/event/inbox/job
        parent-child/ attempt and restore context in worker. Evidence:
        asynchronous tests.
  - [x] S10.2.d Propagate allowed context to provider/model/tool, publisher,
        callback/webhook, DCC/engine/bridge, and read-back verification.
        Evidence: external boundary tests.
  - [x] S10.2.e Link audit, job, provider, artifact, bundle, release, and
        external refs as span attributes/events without high-cardinality
        metrics. Evidence: trace evidence tests.
  - [x] S10.2.f Preserve parent/link semantics across retry, fan-out/fan-in,
        queue delay, batch, cancellation, unknown outcome, and compensation.
        Evidence: topology tests.
  - [x] S10.2.g Run a synthetic browser-to-worker-to-publisher journey and
        assert no broken segment, wrong tenant context, or sensitive baggage.
        Evidence: captured trace.
        <!-- S10.2: shipped as `libs/oshun/workbench-kit/src/trace-continuity.ts`
        (entry point `@oshun/workbench-kit/trace-continuity`, contract-class)
        with a 131-test spec, plus repairs to `@oshun/tracing`,
        `@oshun/gpu-dispatcher`, `@oshun/bff-kit` and `@lilith/service-lib` and
        a 19-test regression spec
        (`libs/shared/tracing/src/propagation-continuity.spec.ts`).

        THE VEIN: A TRACE IS A CLAIM ABOUT CAUSATION AND IT IS MADE OF EDGES,
        NOT SPANS, AND EVERY CHECK IN THIS REPOSITORY IS ABOUT A SPAN. Every
        span can be perfectly formed -- correct name, bounded attributes, all of
        S10.1 satisfied -- and the trace still be false, because what makes two
        spans one trace is the EDGE between them, and an edge exists only where
        the context survived a medium that does not natively carry it: an HTTP
        header, a queue message attribute, a database column, a webhook body, a
        retry, a four-hour delay. In process the edge is free and invisible;
        across any of those it is a field somebody had to write and somebody
        else had to read. AND A MISSING EDGE RAISES NO ERROR -- it does not look
        like a failure, it looks like two well-formed traces. Nothing throws,
        nothing is refused, no dashboard turns red, and the only symptom is that
        during an incident the worker's work is not under the request that asked
        for it, discovered by the person who needed it at the moment they needed
        it. So the grep is not "is there a tracer"; it is: for every
        asynchronous hop, NAME THE FIELD THE CONTEXT RIDES IN, then show that
        field has both a writer and a reader.

        Ten measured instances. `EventContext.traceParent` (`outbox.ts`,
        `quarantine.ts`) is documented "W3C traceparent, or null when the
        mutation had no trace" and has two declarations, ZERO production writers
        and ZERO production readers; twelve of its fourteen occurrences are in
        specs and seven of those set it `null`, and `quarantine.spec.ts` puts
        `'00-trace-span-01'` in it -- which is not a traceparent, and nothing
        objected because nothing parses it. `@oshun/gpu-dispatcher`'s
        `tracePoll` carries the comment "Try to link to parent trace if
        available" and then writes `'trace.linked_trace_id'` as a string
        ATTRIBUTE, starting the span with neither parent nor link, so the poll
        is the root of a NEW trace and the connection exists only as text --
        THE EDGE WAS DESCRIBED RATHER THAN MADE -- while `createJobTraceLink`,
        twelve lines further down the same file, already built the correct
        structure and had no caller. The same library's
        `injectTraceContextToJob` returns the job input UNCHANGED when there is
        no active span, so the enqueue succeeds, the job runs, and the edge is
        absent with no marker anywhere.
        `LightweightTracer.extractContext(_carrier)` returned
        `context.active()` and never looked at its argument, while implementing
        `Tracer`, being exported from the package root, and being what the Hono
        middleware calls on the real BFF request path -- two implementations of
        one interface disagreeing about whether propagation happens, with the
        type system calling them substitutable; and its `injectContext` wrote
        `00-${traceId}-${spanId}-01`, hardcoding the sampled flag (a trace
        sampled OUT telling every downstream service it was sampled IN) and
        erasing the seven reserved bits, and never wrote `tracestate` or
        `baggage` at all. `@oshun/bff-kit`'s `residency-guard` wrote
        `traceId: extractTraceId(request)` where `extractTraceId` returned the
        WHOLE `traceparent` header into a field called `traceId`, so every audit
        row it wrote carried a superstring of the key everything else joins on
        -- the one record whose entire purpose is to be found later joined to no
        trace at all -- with `req_${Date.now()}` as the fallback.
        `@lilith/service-lib`'s `health-response` returned
        `ctx.spanId ?? traceHeader.slice(0, 16)` as `x-span-id`: a span id no
        span ever had, derived deterministically from the trace id so every
        request in that trace lacking one gets the SAME value, two lines below
        `randomBytes(16)` minting a trace id at RESPONSE time. `parseTraceState`
        enforced no member cap, no byte cap and no grammar while
        `updateTraceState` prepended and kept everything (one member per hop,
        forever); `parseBaggage` enforced none of W3C's 180/8192/4096 limits and
        called `decodeURIComponent` unguarded, so `baggage: %=1` threw URIError
        inside the propagation path. And `tracing-manifest.ts` declares a
        `propagation` channel list per span family -- exactly the right subject
        -- which nothing outside that file imports.

        DECISIONS. THE UNIT IS THE EDGE AND EVERY EDGE NAMES ITS CARRIER, so
        "we propagate context" cannot be written down without saying in what.
        ABSENCE IS ONLY EXPRESSIBLE AGAINST A DECLARATION -- a verifier over the
        edges it was given can report that they are malformed and can never
        report that one is missing, because absence has no representative in the
        data -- so the journey is DECLARED first and `verifyJourney` reports
        declared legs with no observed edge, which is the difference between
        "we found no broken edges" and "we found no edges". THE FIRST EDGE IS
        CHOSEN BY A STRANGER, SO IT IS CONTINUED AND DISTRUSTED AT THE SAME
        TIME: the trace id is honoured (S4.13's asymmetry, composed rather than
        re-decided), the SAMPLED FLAG is not, because it is not an identifier
        but a SPEND INSTRUCTION, and when refused the decision falls to
        consistent trace-id-ratio sampling so every service computes the same
        answer without communicating. A REFUSAL TO CONTINUE IS ITSELF AN EDGE --
        a trace already recorded under another tenant restarts AND LINKS, since
        continuing would join two tenants' journeys and in a trace UI a join is
        a disclosure. THE DEFERRED EDGE INVERTS PARENTHOOD: a job enqueued now
        and run in four hours is not a CHILD of the enqueue, because the parent
        ended hours ago and a child outliving its parent makes the parent's
        duration -- the number every latency dashboard is built on --
        meaningless; the answer is a LINK, and the same holds for retry
        (attempts link to the intent, they do not nest) and fan-in (a child has
        exactly one parent and a join has several). A FABRICATED JOIN KEY IS
        WORSE THAN A MISSING ONE, because a null is a visible hole and a minted
        one is a hole that reports itself as full. BAGGAGE IS THE ONLY CONTEXT
        THAT REACHES EVERY DOWNSTREAM SERVICE INCLUDING ONES WE DO NOT OWN, so
        its audience ceiling is per-key and enforced per-HOP (the tenant
        pseudonym is fine on the way to our worker and a disclosure on the way
        to a model provider, which one decision at ingress cannot express) --
        and WHO MAY SET A KEY IS A DIFFERENT QUESTION FROM HOW FAR IT MAY
        TRAVEL, which is where S10.2.a's "without accepting spoofed internal
        identity" lives. CAPS ARE PART OF THE FORMAT, not an optimisation.

        Repairs shipped: `LightweightTracer.extractContext` now extracts;
        `injectContext` writes the real flags and forwards tracestate; the
        lightweight span inherits its parent's sampling instead of asserting
        `traceFlags: 1`; tracestate and baggage are capped to the specification
        with the largest member shed first and a non-throwing decode;
        `tracePoll` passes a real span LINK and `createJobTraceLink` carries the
        parent's real flags instead of `1 // Sampled`; a missing context at
        enqueue is now WRITTEN DOWN (`trace.unpropagated`) instead of returning
        unchanged; the residency guard parses the trace id out of the
        traceparent, validates the `x-trace-id` fallback, drops the clock-minted
        one and fills in the span id it was passing as `null` from the header it
        was already reading; health responses ECHO OR OMIT a join key and never
        invent one. S10.1's `oshun.target.ref` was widened from `SPAN_ONLY` to
        `EVERY_RECORD`, which its own description had said since 1.0.0, and its
        metric budget now spends the `ImpactCount` FLOOR (a pre-existing
        typecheck error, invisible because the library's `tsconfig.json` has an
        empty `include` and compiles nothing). Four shipped tests asserted the
        fabricated ids and now assert the repair.

        Verification: 131 kit tests, 107 tracing tests, 248 gpu-dispatcher, 26
        lilith, 3 bff-kit; 0 typecheck errors across all five projects; no
        export-map drift (148/148 modules). A 37-mutation sweep caught 35; the
        two survivors were proven EQUIVALENT by mutating the redundant partner
        (`fan-in` is both `many` and `deferred` so either branch answers `link`
        -- flipping its timing IS caught; `updateTraceState`'s own-key filter is
        redundant with `parseTraceState`'s first-wins dedupe -- removing the
        dedupe IS caught). Two survivors were real gaps and were closed: a
        boundary test that derived its input from `CLOCK_SKEW_TOLERANCE_MS`
        passed for every value of it, and every malformed trace id the obvious
        tests reached happened to land on `false` anyway, so the sampler's
        validation could be deleted unnoticed until a canary whose digits
        would have sampled IN was added.

        Scope stated: the module is `contract`-class, so the tracers, clocks and
        exporters it is decided against are the host's; S4.13's
        `parseTraceparent` and journey graph (`correlation.ts`), S10.1's
        `admitAttribute`/`classifyValue` and S9.3's `ImpactCount` are composed
        rather than re-decided. The `libs/shared` repairs are self-contained
        rather than importing the kit, because `libs/shared` is the lower layer.
        The canonical audit schema still requires a non-empty `traceId`
        (`z.string().min(1)`, documented "always present for correlation"),
        which is the pressure that produced the invented one; widening it
        touches every audit producer and consumer and was left alone. -->
- [x] S10.3 Instrument stage, gate, review, job, publish, and curation metrics.
      Evidence: metric names and units pass semantic review.
  - [x] S10.3.a Define stage transition requests/refusals/completions/duration/
        current distribution with bounded domain/stage/action/outcome labels.
        Evidence: stage metrics.
  - [x] S10.3.b Define gate runs/duration/verdict/freshness age/invalidation/
        recomputation/waiver/unavailable metrics with evaluator/policy families.
        Evidence: gate metrics.
  - [x] S10.3.c Define review assignments/queue
        age/SLA/verdict/quorum/escalation/ delegation/COI/approval duration
        metrics without identity labels. Evidence: review metrics.
  - [x] S10.3.d Define job queue/execution/attempt/retry/cancel/dead-letter/
        resource/usage/cost/outcome metrics by bounded job/provider/resource
        class. Evidence: job metrics.
  - [x] S10.3.e Define publish plan/eligibility/refusal/execute/read-back/
        mismatch/compensation/target latency and outcome metrics. Evidence:
        publish metrics.
  - [x] S10.3.f Define curation proposal/review/bundle/publish, eligible/
        ineligible, collection/feature size/rank drift, catalog
        verify/correction metrics. Evidence: curation metrics.
  - [x] S10.3.g Verify instrument type, unit, boundaries, aggregation,
        cardinality, privacy, owner, dashboard, and alert use. Evidence:
        semantic review.
        <!-- S10.3 shipped 2026-08-13.

        THE VEIN: A METRIC IS A CLAIM ABOUT A POPULATION AND AN INSTRUMENT ONLY
        EVER SEES WHAT REACHED IT, AND EVERY CHECK IN THIS REPOSITORY IS ABOUT
        THE POINT THAT ARRIVED. There are four ways not to arrive, all of which
        still return a number, and this repository shipped all four.

        NO DENOMINATOR. `libs/shared/gpu-dispatcher/src/metrics.ts` declared
        `gpu_jobs_total{job_type, priority}`,
        `gpu_jobs_completed_total{job_type, endpoint}`,
        `gpu_jobs_failed_total{job_type, endpoint, error_type}` and
        `gpu_jobs_cancelled_total{job_type}` -- four names, four label sets, one
        question. `job_type` was the only key common to all four, so "what
        fraction of high-priority jobs succeeded" had no answer: the outcomes
        were never keyed on priority and no query could recover it.

        NO WRITER. `gpu_jobs_running` was declared in the interface, registered
        with prom-client, given a help string and two label names, and
        incremented by nothing. Its only test was
        `expect(metrics.jobsRunning).toBeDefined()` -- an assertion that the
        instrument exists and never that it moves -- while the collector beside
        it maintained the same quantity in memory and put it in the snapshot. A
        panel over an empty series draws a flat line, and flat reads as healthy.

        NO RESOLUTION. `QUEUE_WAIT_BUCKETS` ended at 300s while
        `GPU_DURATION_BUCKETS` beside it ended at 3600 -- and queue wait is the
        one of the two that grows without bound, because that is what a backlog
        IS. `histogram_quantile` answers a quantile falling in `+Inf` with the
        highest FINITE boundary, so p99 queue wait reported 300 and stopped
        moving: the series went FLAT during exactly the incident it exists to
        show. `libs/shared/metrics` made it worse by defaulting every histogram
        with no explicit buckets to `HISTOGRAM_BUCKETS.HTTP_DURATION`, 5ms to
        10s, a hypothesis about a different subject applied platform-wide.

        NO RETURN PATH. `recordJobCancellation` incremented a counter and
        touched no gauge, so `gpu_jobs_pending` rose by one per cancellation and
        never came back -- forever -- while the collector's in-memory
        `jobStats.pending--` got it right. Two representations of one quantity,
        disagreeing by exactly the cancellation count, and the wrong one was the
        one that left the process. Because the instrument is a level nobody
        rates it, so the drift had no slope to notice.

        Plus the value-side twin of S10.1: AN INSTRUMENT TYPE LICENSES
        ARITHMETIC AND THE VALUE HAS TO SUPPORT IT. `updateEndpointHealth`
        stored `healthy -> 1, degraded -> 0.5, unhealthy -> 0` and
        `updateCircuitBreakerState` stored `closed -> 0, half-open -> 0.5,
        open -> 1` -- enumerations wearing numbers, running in OPPOSITE
        directions in one file, so 1 meant "good" in one gauge and "bad" in the
        other. `avg` over endpoints returns 0.5 both when every endpoint is
        degraded and when half are perfect and half are dead.

        Three more in the substrate, `libs/shared/metrics/src/registry.ts`: the
        instrument cache was keyed on the NAME ALONE and was a get-or-create, so
        a second `histogram({name, buckets})` returned the first instrument and
        silently discarded the second declaration's buckets and label names
        (S10.1 fixed exactly this in the OTLP path with
        `assertNoInstrumentConflicts`; the Prometheus path, which the job and
        cache metrics actually use, did not have it); `PromGauge.get()` ended
        `?? 0`, so a health check querying a misspelled label read a permanently
        healthy zero; and `setDefaultLabels` assigned rather than merged, so a
        later caller adding `region` dropped the `service` set at construction
        and the process exported under no service at all.

        SHIPPED. `libs/oshun/workbench-kit/src/workflow-metrics.ts`
        (contract-class, entry point `@oshun/workbench-kit/workflow-metrics`):
        38 metric declarations across the six families (a-f), each with
        instrument, UCUM unit, value semantics, bounded label domains, explicit
        histogram boundaries with a declared operating range, owner and
        escalation, recording site, and named dashboard/alert/objective readers;
        seven lifecycle machines the levels and outcome partitions are checked
        against; and `reviewWorkflowMetricCatalog` (g), which reports across the
        nine axes S10.3.g names plus three the vein adds -- name, denominator,
        return-path. 20,069 series against a 100,000 budget. 157 tests.

        The checks that are algorithms rather than assertions: a threshold must
        be EXACTLY a boundary, because the fraction under T is
        `bucket{le=T}/count` and there is no such bucket otherwise; consecutive
        boundaries are capped at a ratio of 4, because a quantile inside a
        bucket is interpolated across it and the shipped 300 -> 3600 step is a
        twelvefold error bar; a denominator's labels must be a SUBSET of the
        numerator's, which is the gpu-dispatcher defect stated as a rule; a
        level's `leavesOn` must cover every transition out of the counted states,
        derived from the machine rather than declared, which is the cancellation
        leak stated as a rule; and k-anonymity by PIGEONHOLE -- `population /
        cells` below the floor means at least one cell is, whatever the
        distribution -- which is what makes S10.3.c's "without identity labels"
        achievable, since `{domain, gate, verdict, shift}` contains no
        identifier and still names one person when one person reviews that gate
        on that shift.

        REPAIRS. `libs/shared/metrics`: histogram buckets are now required with
        no fallback; one declaration ledger across all four instrument kinds
        that refuses a disagreeing re-declaration and rolls back on construction
        failure; boundaries validated strictly increasing (prom-client silently
        SORTS what it is given); `Gauge.get` widened to `number | undefined`;
        queries on undeclared label keys refused; `Counter.get()` sums instead
        of returning the first series as the whole; `set(labels)`/`observe(labels)`
        with no value throw instead of silently dropping the write;
        `setDefaultLabels` merges and refuses to rebind an identity key already
        set; `cache_keys_total` renamed (a gauge wearing a counter's suffix) and
        `queue_jobs_completed`/`_failed` given theirs; `BACKLOG_AGE_SECONDS` and
        `LONG_JOB_SECONDS` presets added because nothing there could express a
        backlog. `libs/shared/gpu-dispatcher`: one `gpu_job_outcomes_total`
        counter carrying every denominator label plus an exhaustive four-value
        outcome partition, replacing three counters with three label sets;
        `gpu_jobs_running` given real edges (and `gpu_jobs_dispatched` retired,
        since `sum by (endpoint)` recovers it); `recordJobCancellation` given a
        required `phase` with no default, because only the caller knows which
        level the job was in and a default would be a guess that drives the
        other one negative; `recordJobDeadLettered` added for the fourth
        terminal outcome; queue-wait buckets extended to the 24h dead-letter
        deadline and the 300 -> 3600 duration gap filled; both enum gauges
        converted to state sets with one polarity; and `GpuMetricsOptions.prefix`
        deleted -- it was documented "(default: 'gpu_dispatcher')" and read by
        nothing.

        The compiled `.js`/`.d.ts` shadows tracked next to
        `libs/shared/metrics/src/*.ts` were patched in lockstep: vitest resolves
        `./types` to the stale `types.js`, so editing only the `.ts` changed
        nothing and the test kept asserting the old name. Regenerating them with
        plain `tsc` was tried and backed out -- the tracked artifacts carry
        `./registry.js` import specifiers that a plain emit does not produce.

        Verification: 11,510 workbench-kit tests, 71 metrics, 271
        gpu-dispatcher; 0 typecheck errors across all three projects under both
        their lib and spec configs; export map regenerated clean at 158 entry
        points and 0 violations -- which also retires two pre-existing
        violations, since S10.2 added `./trace-continuity` to package.json and
        tsconfig.base.json without a ratifying entry and `--check` had been RED
        ever since. A 61-mutation sweep caught 60. The two survivors were both
        real gaps and both closed: `coversFloor` could be pinned to true because
        every assertion that touched it went through `boundaryFaults`, which
        refuses the same condition by a different route; and the quantile
        comparison at the overflow boundary turned on floating point, since
        `1 - 0.95` is `0.050000000000000044` and a declared p95 against a
        declared 0.05 overflow share -- the exact case where the quantile sits
        ON the boundary and is unresolvable -- came out resolvable by one part
        in 10^17. A `SHARE_TOLERANCE` of 1e-12 fixes it and makes the strict/
        non-strict distinction a provably equivalent mutant: removing the
        tolerance IS caught.

        Four targeted mutations confirmed the gpu-dispatcher spec earns its
        keep: reinstating the cancellation leak fails 4 tests, the writer-less
        running gauge 5, the 300s queue-wait ceiling 3, and the enum-in-value
        health gauge 3.

        Scope stated: the module is `contract`-class, so the meter, clock and
        exporter it is decided against are the host's; S10.1's `Unit`,
        `ValueDomain`, `domainCardinality` and S9.3's `ImpactCount` are composed
        rather than re-decided, and S10.1's `UNITS` table gained the four
        workflow annotations (`{job}`, `{run}`, `{review}`, `{transition}`)
        rather than the catalogue forking its own. The declared figures a review
        cannot compute -- a subject population, an overflow share -- are carried
        with a required source field rather than estimated. `libs/shared` was
        repaired self-contained rather than importing the kit, because
        `libs/shared` is the lower layer. Pre-existing and left alone: 16
        typecheck errors in `libs/shared/gpu-dispatcher/src/validation.spec.ts`
        about a required `GpuJobResult.metrics`, unrelated to this work and
        present before it. -->
- [x] S10.4 Define SLOs and error-budget policies for interactive and
      asynchronous paths. Evidence: owners approve measurable objectives.
  - [x] S10.4.a Inventory critical interactive read/search/navigation/save/
        mutation/review/gate/publish-control journeys and user-visible success.
        Evidence: interactive SLI register.
  - [x] S10.4.b Inventory critical queued
        job/gate/provider/render/import/export/
        publish/notification/index/localization workflows and terminal success.
        Evidence: async SLI register.
  - [x] S10.4.c Define availability/correctness, latency/deadline/queue-age,
        durability/freshness, and partial/degraded success semantics with
        qualifying events and exclusions. Evidence: SLI specifications.
  - [x] S10.4.d Set target/window/error budget by domain/risk/priority and
        dependency contribution without hiding missing telemetry. Evidence: SLO
        table.
  - [x] S10.4.e Define multiwindow burn alerts, ownership/escalation, freeze/
        rollback/capacity actions, and release/feature-work error-budget policy.
        Evidence: policy.
  - [x] S10.4.f Validate SLIs against synthetic success/failure/degraded/missing
        data and historical or load evidence. Evidence: SLI tests.
  - [x] S10.4.g Obtain named service/domain/product/operations owner approval
        and quarterly review. Evidence: approval records.
        <!-- S10.4: shipped as `libs/oshun/workbench-kit/src/service-objectives.ts`
        (contract-class, entry point `./service-objectives`, 159 approved entry
        points and no violations) with a 125-test spec, plus in-place repairs to
        `libs/shakti/deployment/src/monitoring-observability.ts` -- the only
        shipped SLO register in this repository -- and two small extensions to
        S10.3's `workflow-metrics.ts` enums.

        THE VEIN: AN OBJECTIVE IS A RATIO AND THE FAILING SYSTEM COMPUTES ITS
        OWN DENOMINATOR. S10.3 was about the observation that never reached the
        instrument; this is one layer up and worse, because here the
        observations DID arrive and the ratio still lies. `good / valid` has two
        sides and the second one is never a fact about the world -- it is a set
        the system chooses. Which events qualify, which are excused, which have
        not finished yet and which are simply missing are all decided by code
        that the outage is happening to, and a number between zero and one comes
        out regardless, onto a dashboard with a green threshold line. Five ways
        the denominator moves, all five measured in that one shakti file.

        1. THE DENOMINATOR IS THE COMPLETIONS. `slo-media-transcoding-throughput`
        promised "95% of transcoding jobs complete within their expected
        duration" and computed
        `completed_total{within_sla="true"} / completed_total`. Both sides count
        completions, so a job that has not completed is in neither, and the one
        failure that matters -- the queue stops draining -- moves the ratio not
        at all. `completionsOnlyValue` is shipped beside `evaluateSli` so the
        spec can put both numbers on one set of observations: 950 successes and
        50 permanently stalled jobs read 0.95 honestly and 1.0 on that query.
        The fix is not a different query, because "everything admitted" is also
        wrong -- it is depressed by work still legitimately in flight, in
        proportion to the ARRIVAL RATE, so a traffic spike is indistinguishable
        from an outage, people learn the alert is noise, and the next honest
        attempt reaches for the completions denominator again. The correct
        denominator is admissions DUE BY NOW, which makes the deadline a field.

        2. THE EXCLUSION IS DECIDED BY THE THING THAT FAILED.
        `slo-notification-delivery` divided an unfiltered `delivered_total` by
        `dispatched_total{recipient_valid="true"}` -- two metric names, one
        filter, on one side -- so the numerator counted deliveries whose
        dispatches were never counted and the ratio could exceed one, which is
        an error budget that refills. Underneath the arithmetic: an exclusion
        whose predicate is computed FROM THE OUTCOME is evaluated by the code
        that produced the outcome. "Exclude client errors" removes a validator
        regression, a bad schema migration and a clock skew that rejects every
        token from the denominator, and the graph improves while it happens.
        `Exclusion` therefore carries `decidedBy` and `evidenceTiming`, and
        subject-system + from-outcome is refused; the same predicate is
        admissible when the fact predates the attempt, because a recipient
        marked invalid at enrolment cannot be manufactured by today's incident.

        3. THE QUERY MATCHES NO SERIES AT ALL. That module declared seven
        `PrometheusMetricDefinition`s and FIVE of its seven objectives were
        computed from names that were not among them --
        `shakti_workout_session_saves_total`,
        `shakti_media_transcode_completed_total`,
        `shakti_auth_request_duration_seconds`,
        `shakti_notification_delivered_total`/`_dispatched_total` and
        `shakti_biometrics_resource_utilization_ratio`. In PromQL a name that
        matches nothing is an empty vector, `empty / empty` is empty, and
        `expr > threshold` over an empty vector is never true: AN SLI THAT DOES
        NOT EXIST IS NEVER BREACHED. The register and the metric store sat in
        one file four hundred lines apart with nothing joining them.

        4. THE BURN RATE HAS NO PERIOD. The fraction of budget an alert lets you
        spend before firing is exactly `rate x window / period`, and the burn
        rate CANCELS out of that expression -- derived in
        `budgetFractionAtDetection` and asserted for a 20x and a 100x burn on
        the same alert. So `{24h, 3.0}` is a 10% alert on 30 days and a 43%
        alert on 7. Those rungs were transplanted: `slo-biometrics-ingestion`
        ran a 7-day window with `{24h, 4.0}`, which fires only after 57.1% of
        the week's budget is gone, and `{6h, 8.0}` at 28.6%;
        `slo-media-transcoding-throughput` spent 28.6% on its own 24h rung. And
        every one of the seven had the other hole: an alert never fires below
        its own rate, the lowest rate anywhere in the file was 2.0, so a steady
        1.5x burn empties a 30-day budget in twenty days and trips nothing ever.
        `burnLadder` derives the ladder instead -- four budget-consumption
        decisions (2%, 5%, 10%, 10%) at rates 14.4/6/3/1, with `W = f x P / R`
        rounded DOWN so rounding can only spend less. On a 30-day period that
        reproduces the familiar 1h/6h/24h/72h table exactly, which is the point:
        those are not four remembered constants.

        5. NO DATA IS NOT GOOD DATA. `0/0` during an exporter outage, a
        collector restart or a regional blackout makes every comparison against
        the result false, so the alert is silent for exactly the duration of the
        blackout and the compliance figure is an average over the intervals that
        reported. `evaluateSli` returns `null` and never a number for an empty
        valid set; `NO_DATA_POLICIES` refuses `good` outright and requires
        `unknown` to carry a coverage objective whose own policy is `bad` --
        a recursion that terminates, because if the coverage series is also
        missing, that IS the outage.

        Other decisions worth keeping. AN OBSERVATION IS A RECORD WRITTEN BY A
        COMPONENT, SO IT CANNOT WITNESS THE FAILURE OF ANYTHING UPSTREAM OF
        ITSELF: a server-handler SLI is blind to the request that never reached
        the handler, and -- the case that surprises people -- a client-observed
        availability SLI is blind to `client` and `network` despite sitting in
        front of both, because a browser that never loaded sends no beacon and
        "no beacons" is the same bytes as "no traffic". PARTIAL CREDIT NEEDS A
        FULL-SUCCESS FLOOR, because 30% of events at 0.5 credit reads 85% while
        nearly a third of the population had a bad time. A DEPENDENCY WITH NO
        PUBLISHED NUMBER CANNOT BE MULTIPLIED IN, so `achievableCeiling` returns
        an UPPER bound and names the unmeasured, and an objective carrying one
        must disclose it -- `slo:publish-target-ack` does, because an app store
        publishes no availability to us and writing 0.999 because it feels about
        right is the hidden telemetry S10.4.d refuses. A STANDBY IS A CLAIM
        ABOUT WHAT HAPPENS DURING A FAILURE, so an unexercised redundancy group
        contributes its primary alone. THE SHORT WINDOW IS WHAT MAKES AN ALERT
        STOP, not a second alert -- shakti's `{5m, 14.4}` beside its
        `{1h, 14.4}` was a standalone page on 0.17% of a budget next to a rung
        that could only ever fire later. A FREEZE THAT BLOCKS THE ROLLBACK
        BLOCKS THE REMEDY, and a freeze the frozen owner may lift ends whenever
        it becomes inconvenient. AND AN APPROVAL ATTESTS TO A VERSION, NOT A
        NAME: `objectiveFingerprint` covers the target, window, grades,
        exclusions, dependencies, alerts and policies and deliberately excludes
        prose, so rewording a rationale does not invalidate a signature and
        changing a target does.

        The bidirectional closure is the reason this module is where it is.
        Five S10.3 metric declarations carried a `consumers` entry of kind `slo`
        -- `slo:stage-transition-interactive`, `slo:gate-run-completion`,
        `slo:review-sla-48h`, `slo:job-success-rate`, `slo:publish-target-ack`
        -- and until now NOTHING defined them: the metric knew an objective was
        reading it and the objective's target, window, budget, owner and policy
        existed nowhere. `reviewObjectiveRegister` refuses both directions.

        Shipped register: 8 interactive journeys covering all eight kinds S10.4.a
        names, 11 async workflows covering all ten S10.4.b names plus `review`
        (a stated scope decision -- a review assignment is a queue with a human
        worker, and leaving it out would put the one workflow whose deadline is
        48 HOURS in the register that measures milliseconds), and 20 objectives
        with four named owner roles, four-rung ladders and four-policy sets each.
        Two S10.3 enum extensions rather than forks: `localization` joined
        `JOB_CLASSES` and `notification` joined `PUBLISH_TARGET_CLASSES`, since
        a notification is a publish -- something leaves the building for a target
        that acknowledges it or does not, and the read-back check is exactly
        "did the person get told".

        Shakti repairs: the seven missing metrics declared (with 0.2 present as
        an explicit boundary of the new auth histogram, because a threshold
        between boundaries silently measures the fraction under 0.25 -- a 25%
        looser objective than the one approved); the notification ratio made
        symmetric and moved onto an outcome partition with `expired` written by
        a sweep; the media denominator moved from completions to admissions with
        `abandoned` for the stall, and its `sliBasis` corrected from
        `window-based`, which contradicted its own counter-ratio query; all seven
        ladders redivided for their own periods with a 1x rung added and the
        5m/1h duplicates folded; and `validateSloDefinitions` added so none of it
        can regress -- it resolves every query against the metric register,
        checks `le=` against declared buckets, checks ratio symmetry, checks the
        query range against the claimed window, and checks the ladder arithmetic.
        It refuses a query it cannot parse rather than passing it silently,
        because a parser that returns success without checking is the same defect
        as the empty vector.

        Verification: 11,638 workbench-kit tests (125 new), 2,311 shakti
        deployment tests (19 new); 0 typecheck errors across both projects under
        both lib and spec configs; export map regenerated clean at 159 entry
        points, 0 violations. A 75-mutation sweep caught 69 on the first pass;
        all six survivors were real gaps and all six were closed (the exact
        `admittedAt + deadline` boundary; the `budgetRemaining` clamp at one; the
        long:short ratio band on both validators; the workflow-admission check,
        which was masked because the partition check refused the same condition
        by a different route -- S10.3's lesson, met again; and the blind-spot
        check, which nothing exercised through `reviewObjective` because no
        shipped journey has a blind spot). Re-run: 75/75. One defect the sweep
        found in my own code first: `budgetRemaining(0.99, 0.01)` returned
        8.88e-16 rather than 0, because `1 - 0.99` is `0.010000000000000009` --
        so the one moment a `budget-exhausted` freeze exists for was the moment
        it would not have fired, by one part in 10^15.

        Scope stated: the module is `contract`-class, so the clock, the query
        engine, the alert manager and the release gate it is decided against are
        the host's. S10.1's `fnv1a32` and `admitSpanName`, S10.3's lifecycles,
        catalogue and `resolvesThreshold`, and S9.3's `ImpactCount` are composed
        rather than re-decided -- the event-denominated budget is an
        `ImpactCount` because it is a target multiplied by a volume forecast, and
        printing that as a bare integer is how a forecast becomes a fact between
        one slide and the next. -->

- [x] S10.5 Add dependency health, saturation, queue age, and circuit-breaker
      dashboards. Evidence: fault injection changes the expected panels.
  - [x] S10.5.a Register databases, blobs, queues/workers, caches/search,
        providers/models/tools, signing/trust, publishers/channels, bridges/
        hosts, identity, and telemetry dependencies with owner/SLO. Evidence:
        dependency register.
  - [x] S10.5.b Display configured/not-configured/healthy/degraded/unavailable/
        failed/stale-probe, latency/error rate, last success/failure, and
        affected capability. Evidence: health panels.
  - [x] S10.5.c Display CPU/memory/GPU/storage/connection/thread/license/token/
        quota/provider capacity versus limits and trend. Evidence: saturation
        panels.
  - [x] S10.5.d Display queue depth/age by priority/type/tenant-safe cohort,
        throughput, lease loss, retries, dead letters, worker availability, and
        oldest work. Evidence: queue panels.
  - [x] S10.5.e Display circuit closed/open/half-open, reason, threshold,
        failures, opened/until, probes, affected calls, fallback/degradation,
        and authorized controls. Evidence: circuit panels.
  - [x] S10.5.f Link SLO/burn, traces/logs/jobs/incidents/runbooks/owners and
        apply bounded labels/access/redaction. Evidence: dashboard review.
  - [x] S10.5.g Inject timeout/throttle/saturation/queue
        stall/circuit/dead-letter faults and assert exact panel/alert/recovery
        changes. Evidence: dashboard fault tests.
        <!-- S10.5: shipped as `libs/oshun/workbench-kit/src/dependency-dashboards.ts`
        (contract-class, entry point `./dependency-dashboards`, 160 approved entry
        points and no violations) with a 110-test spec, plus in-place repairs to
        `libs/lilith/service-lib/circuit-breaker.ts` (the live breaker where the
        vein is measured), `libs/freya/db/src/observability.ts` (ten breaker
        configurations) and `libs/shakti/deployment/src/monitoring-observability.ts`
        (the dashboard register), and six dependency contributions added to S10.4's
        objectives.

        THE VEIN: THE INSTRUMENT IS DOWNSTREAM OF THE MITIGATION, SO EVERY
        PROTECTION IMPROVES THE PANEL WHILE THE OUTAGE CONTINUES. S10.3 was the
        observation that never reached the instrument and S10.4 the ratio whose
        denominator the failing system chooses. This is the layer a person reads
        at 3am, and it has a defect the other two do not: in front of every
        dependency sits a timeout, a retry, a breaker, a throttle, a bulkhead, a
        fallback, a dead-letter queue or a cache, each of which exists to REMOVE
        traffic, each of which therefore changes the population the panel is
        computed over, and each of which engages exactly during the incident. The
        direction is never random -- a mitigation removes the bad events, because
        that is what a mitigation IS -- so a panel over the survivors moves the
        reassuring way at the moment the system is worst.

        Measured rather than argued. `libs/lilith/service-lib/circuit-breaker.ts`
        is a live breaker with a `getStatus()` built for a panel. Its `execute()`
        increments `metrics.totalRequests` for every call INCLUDING the
        short-circuited ones and returns without touching
        `metrics.totalFailures`; `failureRate` is the second over the first.
        Driven by hand against a dependency 100% down for the whole observation,
        that panel reads 100.00%, then 33.33%, 4.35%, 0.45%, 0.04% as traffic
        continues. The error rate does not stop rising -- it FALLS, monotonically,
        toward zero, for as long as the dependency stays broken, and falls faster
        the harder the caller retries. Nothing is miscounted; every increment is
        correct on its own terms. The population changed underneath the ratio.

        Five more in that one file, all repaired. THE SUPPRESSED EVENTS ARE
        COUNTED NOWHERE -- `grep shortCircuit` returned nothing, so the number
        that replaced the errors was not merely off the panel, it was not
        recorded. THE MITIGATION'S OWN REJECTION IS BLAMED ON THE DEPENDENCY:
        the concurrency limiter called `recordFailure('overload')`, so a burst of
        local concurrency opened the circuit against a perfectly healthy
        dependency and the panel attributed our saturation to them. THE FALLBACK
        IS COUNTED TWO WAYS FOR ONE OUTCOME -- unrecorded in the open branch,
        a failure in the catch branch, so whether a degraded answer was a failure
        depended on which side of the breaker state the caller arrived on.
        HALF-OPEN HAD NO PROBE BUDGET and closed on ONE success, so recovery was
        a thundering herd authorised by a single lucky probe. And THE MANUAL
        CONTROL ERASED ITS OWN EVIDENCE: `reset()` assigned `this.state = CLOSED`
        and THEN called `setState(CLOSED)`, so the recorded transition was
        `closed -> closed` with the fallthrough reason `State change` and the
        `open -> closed` edge never entered `stateChanges` at all -- the one
        action an operator takes during an incident was the one action the
        history could not show, and a post-incident review read the override as
        a recovery.

        The theorem worth keeping: A LATENCY PERCENTILE ABOVE THE CENSORED SHARE
        IS THE TIMEOUT, NOT A MEASUREMENT. A timeout at T censors exactly the
        attempts slower than T, so the recorded sample is precisely the fastest
        `1-c` of attempts in order and an observed q-quantile is the population's
        `q(1-c)` quantile -- exact, not approximate. Under 2% timeouts a panel
        labelled p99 is the population's p97.02, and every quantile at or above
        `1-c` is bounded below by T and above by nothing, because the attempts
        that make it up were stopped and how long they had left is not in the
        data. The panel goes FLAT at T as the dependency degrades, and flat reads
        as stable. `identifiedQuantile` and `quantileIsIdentified` ship it.

        Other decisions worth keeping. AN ABSOLUTE FAILURE THRESHOLD IS A RATE
        THRESHOLD DIVIDED BY A TRAFFIC LEVEL NOBODY WROTE DOWN, and it is wrong
        in both directions at once: freya's `paystack` opens on five failures in
        sixty seconds, which is a 0.083% error rate at a hundred calls a second
        -- tripping on a dependency performing far better than its objective
        requires -- and at one call a minute the window does not hold five calls,
        so nothing opens it however completely the dependency has failed. A
        BREAKER CANNOT OPEN BEFORE ONE FAILURE HAS BEEN RECOGNISED AS ONE, so the
        request timeout is a hard floor on detection and `ai_vision`'s sixty
        seconds means every caller in the first minute waits the full sixty. A
        LEVEL WITH NO LIMIT IS NOT A SATURATION -- `shakti_biometrics_resource_
        utilization_ratio` is named a ratio, typed a ratio and divided by nothing
        anybody declared. THE BINDING LIMIT IS THE SMALLEST IN A CHAIN NOBODY
        DREW, and scaling out makes a shared limit bind harder while every
        per-instance panel improves. A MEAN ACROSS INSTANCES HIDES THE ONE
        REPLICA THAT IS FULL (one at 100% and nine at 10% averages 19% while a
        tenth of all traffic is refused). DEPTH COUNTS THE READY SET AND A LEASE
        HOLDS WORK OUT OF IT, so work held by a dead worker is in no terminal
        state and no ready state -- the depth gauge reads empty, the age gauge
        reads empty, and detection has a floor of one lease duration that nothing
        on the board discloses. A DRAIN ESTIMATE THAT IGNORES ARRIVALS
        under-reports without bound and prints a finite number for a queue that
        never empties. AN EMPTY QUEUE AND A BROKEN PRODUCER DRAW THE SAME LINE,
        so `starved` needs a declared expected arrival rate to exist as a verdict
        at all. A CUMULATIVE DEAD-LETTER TOTAL IS MONOTONE, so an alert on it
        fires once and is silenced permanently, and the next batch of abandoned
        work arrives into a muted alert. A TRACE LINK IS EITHER AN EXEMPLAR OR AN
        UNBOUNDED LABEL. And `not-configured` IS A CONFIGURATION FACT, NOT A
        HEALTH FACT -- three axes collapsed into one enum is what lets a
        dependency nobody configured render as healthy and an hour-old cached
        answer render as current, so `panelStateOf` is a total join over the
        three and the rendered value is compared against it rather than trusted.

        The evidence clause, run rather than asserted. Six fault transforms MOVE
        work between census fields instead of inventing it, and the partition
        identities are checked after every one at seven magnitudes, so a delta is
        a real change and not an accounting artefact. Against the board shape
        this repository ships -- an error rate over CALLS, a depth over the READY
        set, no witness panels -- three of the item's six faults are
        undetectable, and two of those three move it the REASSURING way:
        injecting `circuit-open` improves the error rate and the latency
        quantile, injecting `dead-letter` improves the queue depth, and injecting
        a 50% `throttle` moves nothing at all. The board does not go quiet, it
        goes green, and the reader acquires positive evidence to look elsewhere.
        With the witness panels the same six are all detected -- and the
        reassuring movement does not go away, it gets accompanied, which is the
        whole design.

        The bidirectional closure, both ways. Ten `dep:` ids appeared in S10.4
        objective contribution chains and NOTHING defined any of them: no kind,
        no owner, no affected capability, no panel, while each carried a declared
        availability being multiplied into a ceiling. And six dependencies a
        workbench actually calls -- the worker pool every asynchronous objective
        runs on, the signing service and trust store a publish needs, the channel
        a notification leaves over, the cache the interactive latency objective
        depends on, the provider gateway -- were in no chain at all, so
        `achievableCeiling` multiplied them at 1.0 and every one of those
        ceilings was optimistic. Added, with all twenty objectives re-checked:
        every target still sits under its corrected ceiling. The three remaining
        uncovered dependencies are all `supporting`-tier and are reported at
        `repair` severity rather than refused, because a supporting dependency
        with no objective is a stated product position and the register records
        it instead of manufacturing a target.

        Shipped register: 19 dependencies covering all 17 kinds and all ten
        groups the item names, one board per dependency with its readings DERIVED
        from the protections in front of it (so adding a `throttle` adds a
        `throttle-rate` panel and forgetting to build it is a finding rather than
        a silence), and a nine-entry suppression table binding each mitigation to
        the ONE reading that witnesses what it removed.

        Verification: 11,766 workbench-kit tests (127 new); 47 lilith
        service-lib circuit-breaker tests (11 new, all 36 prior still passing);
        114 freya db tests (7 new); 2,319 shakti deployment tests (7 new);
        0 typecheck errors across all four projects under both lib and spec
        configs; export map regenerated clean at 160 entry points, 0 violations.
        A 149-mutation sweep caught 129 on the first pass; all 15 real survivors
        were genuine gaps and all 15 were closed (a timed-out attempt counted as
        an error; the identified-quantile clamp; not-configured beating an
        expired probe; a resource already past its limit; arrivals exactly
        meeting completions; an ESTIMATED population used at its estimate rather
        than its observed floor for k-anonymity; forced-closed asked for a
        short-circuit count; the supporting-tier severity split, in both
        directions; a dependency an objective names and the register omits; the
        comparison tolerance on a delta; the circuit and queue reading sets; and
        `reviewDashboard` admitting a board that draws everything and detects
        nothing). Re-run: 149/149. The sweep also found a defect in this module
        itself: `injectFault` carried a cumulative total forward across recovery
        so that a monotone panel would show as latched, and NO reading kind read
        a cumulative total, so the mechanism could not fire. `DashboardSpec`
        gained `deadLetterCumulative` -- the same shape of choice as
        `depthCoversUnsettled` and `errorRateDenominator`, and the only one
        whose consequence appears on the way OUT of an incident.

        Scope stated: the module is `contract`-class, so the clock, the query
        engine, the dashboard server and the fault injector it is decided against
        are the host's. S10.4's owners, objectives and ceilings, S10.3's
        k-anonymity, aggregations and label privacy, S10.1's bounded domains and
        S9.3's `ImpactCount` are composed rather than re-decided. The seven
        original shakti dashboards were NOT given invented panel queries: their
        contents live in a Grafana this repository does not contain, so they
        report `no-declared-panels` and say why. -->

- [x] S10.6 Add privacy-safe structured logging. Evidence: automated log scan
      catches content, tokens, secrets, PII, and protected identifiers.
  - [x] S10.6.a Define versioned event names and allowlisted typed fields for
        lifecycle/state/error-class/correlation/trace/operation/version/
        duration/usage/cost and bounded context. Evidence: logging schema.
  - [x] S10.6.b Define severity and required/optional sampling for validation,
        auth, dependency, retry, job, provider, publish, audit linkage,
        degradation, and incident events. Evidence: event catalog.
  - [x] S10.6.c Exclude raw authored/source content, prompts/responses,
        file/URL/ query/path, tokens/credentials, stack/SQL, PII, protected
        identity, learner/biometric, and unreleased asset detail by default.
        Evidence: field prohibition policy.
  - [x] S10.6.d Redact/tokenize/pseudonymize only approved fields before log
        emission and treat redaction failure as drop/quarantine, not raw
        fallback. Evidence: redaction tests.
  - [x] S10.6.e Configure transport encryption, regional storage, access,
        retention/deletion/legal hold, export, sampling/rate limits, and
        unavailable exporter behavior. Evidence: log lifecycle tests.
  - [x] S10.6.f Scan source/config/generated logs and runtime output with canary
        content/secrets/PII/protected IDs; minimize retained failures. Evidence:
        automated leakage report.
        <!-- S10.6: shipped as `libs/oshun/workbench-kit/src/log-privacy.ts`
        (contract-class, entry point `./log-privacy`, 161 approved entry points
        and no violations) with a 239-test spec, plus the harness
        `tools/log-leak-scan.mjs` (nx target `log-leak-scan`) and in-place
        repairs to `libs/shared/logging` -- the new `src/redaction.ts`, and
        changes to `logger.ts`, `types.ts`, `middleware/request-logger.ts` and
        `transports/elasticsearch.ts` -- with a 93-test `redaction.spec.ts`.

        THE VEIN: A RECORD FANS OUT TO EVERY SINK AND THE REDACTION WAS
        ATTACHED TO ONE OF THEM -- AND THE ONE IT WAS ATTACHED TO IS THE COPY
        NOBODY KEEPS. S10.3 was the observation that never reached the
        instrument, S10.4 the ratio whose denominator the failing system
        chooses, S10.5 the panel computed over what a mitigation had already
        removed. This is the only one of the four where the defect is not a
        wrong number: a log record is not aggregated, sampled or divided by
        anything. It is COPIED, verbatim, to every destination somebody
        attached, and a safety property attached to one destination is not a
        property of the record.

        Measured rather than argued. `OshunLogger.log()` called
        `this.pino[level](logData, message)` and then, separately,
        `createLogEntry(...)` followed by `writeToTransports(entry)`.
        `redact.paths` is a pino option, so pino censored what pino serialized,
        while `createLogEntry` re-read the ORIGINAL `data` and `error` objects
        and handed them to the Elasticsearch, HTTP, TCP and file transports.
        Driven by hand through the real logger with nine canaries, three were
        stopped on stdout and NONE on the transports: `password`, `apiKey` and
        `nested.secret` were redacted for the console and shipped raw to the
        index, while `deep.a.token`, an axios error's
        `config.headers.Authorization`, its `config.data` prompt, a learner
        email, a reset token in the message and a pg `internalQuery` leaked on
        both. Not one canary was stopped on the path that leaves the box.

        The direction is what makes it a vein rather than a bug. The sink that
        redacted is a terminal a developer reads for ten seconds and discards;
        the sinks that did not are the ones with a retention measured in months
        and a search box on top. So an engineer who tests redaction the obvious
        way -- log a secret, look at the console, see `[REDACTED]` -- has run
        the experiment on the only copy that is not kept, and has acquired
        positive evidence for a property the retained copies do not have.
        `redactionCoverage` names that shape specifically:
        `only-the-ephemeral-sink-is-sanitized` fires when the covered sink has
        the SHORTEST retention, and it is distinct from
        `redaction-attached-to-sink-not-record`, because partial coverage is
        merely bad and partial coverage of the throwaway copy is misleading.

        Six more in shipped code, all repaired. A DENYLIST OVER AN OPEN WORLD OF
        FIELD NAMES HAS UNBOUNDED FALSE NEGATIVES: `DEFAULT_REDACT_PATHS` was
        sixteen entries and pino's `*` matches exactly ONE level, so
        `deep.a.token` at depth three was unmatched -- the list was not short
        through carelessness but because the set of names that can carry a
        secret is not enumerable in advance. AN ERROR IS AN OPEN MAP AND THE
        LOGGER SPREAD IT: `serializeError` walked
        `Object.getOwnPropertyNames`, skipped five names and copied the rest, so
        axios brought `config.headers.Authorization` and node-postgres brought
        `internalQuery` and `detail`, with `stack` copied unconditionally while
        S10.6.c prohibits stack and SQL by name. A PATH-BASED REDACTOR CANNOT
        SEE INTO A STRING: `createMessage` returned
        `${req.method} ${req.url} ...` with `req.url` being `originalUrl`, query
        string included, and the same file put `data.query` and `data.ip` on
        every record UNCONDITIONALLY -- `logHeaders` gated headers, `logBody`
        gated bodies, and nothing at all gated the query string or the client
        IP. THE FIELD PROHIBITION POLICY WAS A TYPE, AND A TYPE FORBIDS NOTHING
        AT RUNTIME: `PrivacyConfig` declared six controls, was exported from
        `index.ts`, was not a member of `LoggerConfig`, was accepted by no
        parameter and read by no code, while the repository's only spelling of
        `redactFields` was a bare literal in `FREYA_LOG_CONFIG` passed to no
        logger, whose one test asserts
        `expect(FREYA_LOG_CONFIG.redactFields).toContain('password')` -- reading
        the array back out of the object that declares it. REDACTION FAILURE
        FELL BACK TO RAW in two places, `writeToTransports` catching into
        `console.error(name, error)` and the Elasticsearch transport's
        `sendBatch` doing `buffer.unshift(...entries)`, an unbounded re-queue,
        so the failure path of a sink that could not accept a record was a
        second sink that agreed to no policy. And pino's `base` was
        `this.context`, merged into every line without passing through
        anything, so the base context reached stdout unredacted AND duplicated
        on every record.

        The finding that was not in the item's text and changes what the
        evidence means: THE TEST SUITE WAS EXERCISING A STALE BUILD ARTIFACT.
        `libs/shared/logging/src` shipped TRACKED `logger.js`, `index.js`,
        `types.js`, `events.js` and their `.d.ts` and `.map` twins beside the
        `.ts`, committed in July while the `.ts` were last touched in April and
        May. Vitest resolved `./logger` to `logger.js`; tsx resolved it to
        `logger.ts`. Proved by driving both: the repair produced
        `droppedProperties` on the transport entry under tsx and a full stack
        under vitest, from the same source line. So every one of the 52
        pre-existing tests had been passing against a compiled copy of code
        nobody had edited in three months. All twelve artifacts deleted; the 52
        tests still pass, now against the source. This is also exactly what
        S10.6.f's word "generated" is for -- a committed `.js` is invisible to
        any scan written `--include="*.ts"` -- so `generatedShadows()` in the
        harness is a standing check for it. NOT FIXED, and reported rather than
        silently left: seven other libraries under `libs/shared` have the same
        tracked build output (41 `.js` and 77 `.d.ts` across config, database,
        http-client, metrics, security, storage and types). Each needs its own
        test verification, since a suite may currently pass BECAUSE of the stale
        copy, and that is outside what S10.6 asks for.

        What the repair can and cannot reach, stated rather than glossed. Re-run
        after the fix, the same nine canaries go from 3/9 stopped on one sink and
        0/9 on the other to 7/9 stopped on BOTH, with perfect sink parity on all
        nine. The two that remain are both carried IN THE MESSAGE STRING, and
        they are unfixable in a general-purpose logger by construction: the
        interpolation happened at the call site before the logger was called, so
        the logger receives one opaque sentence and no path-based rule can reach
        into it. That is why the kit's contract makes the message a
        `templateId` plus bounded parameters and `sanitize` refuses a free-text
        message by name -- the structural fix belongs in the contract a caller
        opts into, and the general logger gets the fan-out fix.

        Decisions worth keeping. AN ALLOWLIST PROJECTION HAS NO DEPTH TO BE
        DEFEATED AT -- `project` emits the declared fields and drops the rest,
        so an undeclared field is ABSENT rather than unmatched, which is the
        only answer to a denylist over an open world. A FIELD IS CLASSIFIED BY
        WHAT IT CARRIES AND NOT BY WHAT IT IS NAMED, so every surviving string
        value is re-scanned with S10.1's detectors and a `safe` declaration
        contradicted by its value is quarantined -- a declaration is a claim by
        whoever wrote the schema about what call sites will put there. THE
        WEAKEST SINK DEFINES THE RECORD'S EXPOSURE, so coverage is per-sink with
        no averaging, and egress dominates retention when choosing the weakest
        because a record we can still purge is a different problem from one a
        vendor has indexed. A COUNTED EVENT MAY NOT BE SAMPLED, and rate
        limiting is WORSE than sampling for a count rather than better: a
        sampled count divides back out by a known rate, and a rate-limited one
        is censored at a ceiling nobody records, so it reads FLAT at the limit
        however far the true rate rises -- S10.5's censored quantile arriving
        through the logging pipe. HARM DECIDES THE RESPONSE AND THE RESPONSES
        DIFFER: a credential is closed by rotation, personal data cannot be
        un-disclosed, and an embargo is the one harm that EXPIRES, which is why
        `unreleased-asset` gets relief on a long retention and nothing else
        does. A TOKEN OVER A LOW-CARDINALITY DOMAIN IS A RENAME, so
        pseudonymisation has a declared floor. AND A LEAK REPORT THAT QUOTES THE
        LEAK IS A SECOND LEAK -- S10.6.f's "minimize retained failures" is the
        only clause in the item about the SCANNER rather than the logger, and
        every finding carries a fingerprint and a length, composing the decision
        S10.1's `HazardFinding` already made rather than restating it.

        Composition rather than re-decision. S10.1's thirteen hazard classes are
        imported and extended by exactly four -- `sql`, `query-string`,
        `model-output` and `unreleased-asset` -- being the classes S10.6.c names
        that span attributes never carry; restating the thirteen would create
        two lists that drift, and a value classified `pii` by one and not the
        other passes whichever check runs second. `classifyValue`, `hazardsIn`,
        `isBounded`, `templateFaults`, `OTHER_SENTINEL` and `fnv1a32` are
        S10.1's; `ImpactCount` is S9.3's, and a sampled count comes back
        `estimated` carrying both its observed value and its fraction. One rule
        was corrected by the register catching it: requiring `isBounded` of
        every field domain imported a METRICS constraint -- a dimension costs
        one series per distinct value -- into a context where a field is stored
        once per occurrence, and it forbade the `template` domain that a route
        needs. Scoped to `enum`; boundedness is still required of context keys,
        which is where S10.6.a's "bounded context" actually lives. The route
        field is `safe` rather than `url` because S10.1's detectors return no
        hazard for `/studio/{projectId}/assets` and `url` for a real one, and
        what keeps that honest is `templateFaults` refusing a value whose
        segment still looks like an identifier -- "the template was concatenated
        rather than substituted", which is the shipped middleware's `req.url` in
        one sentence.

        The evidence clause, run rather than asserted. `tools/log-leak-scan.mjs`
        drives the REAL logger with ten canaries and FOUR controls, because a
        harness with no negative control proves that it ran and not that it can
        fail: a positive control planted in a field nothing matches MUST reach
        both sinks or the capture rig is not observing what it claims; the
        hazard canaries must be stopped on BOTH; and SINK PARITY is checked over
        every canary rather than asserted for the ones somebody remembered. All
        three of its failure modes have been observed failing -- the static pass
        reported 12 findings before the fixture allowances, and the
        generated-shadow check would have reported 4 before the deletion. Its
        allowances are RFC 2606 reserved domains and TLDs, RFC 5737 and RFC 3849
        documentation IP ranges, the published test card numbers and one
        explicit `NOTREAL-` marker, tested against the MATCHED SPAN rather than
        the line -- an allowance evaluated against the line would let one
        `example.com` in a fixture hide a real key three columns to the right.

        One defect this item's own change introduced, caught by the pre-commit
        ratchet and worth keeping: A SHARED LIBRARY MAY NOT DISCRIMINATE A UNION
        ON A BOOLEAN, BECAUSE ITS CONSUMERS CHOOSE `strict`. `redactEntrySafely`
        first returned `{ ok: true; entry } | { ok: false; quarantined }`, which
        typechecks cleanly under `libs/shared/logging`'s own config and FAILS in
        `libs/yemaya/agents`, whose `tsconfig.json` sets `"strict": false` --
        without `strictNullChecks` the `true`/`false` literal types widen to
        `boolean`, the union stops discriminating, and every variant-specific
        property access is an error. The library does not control the settings
        it is compiled under, so it has to pick the narrowing that survives
        them: the result is now discriminated by a STRING, and the three
        outcomes are genuinely three (`redacted`/`dropped`/`quarantined`) rather
        than two with `quarantined: null` standing in for the third.

        The commit's typecheck ran 399 projects because `tsconfig.base.json` is
        staged. Twelve fail, and all twelve are red for reasons that predate
        this change -- `@hathor/llm-npc` on a non-exhaustive switch (TS2366),
        `@hathor/event-handlers` on a stale composite build-info (TS6305),
        `@yemaya/agents` and the `@lilith/*` set on the `libs/contracts` refined
        -zod incompatibility. Only two of the twelve import `@oshun/logging` at
        all, and both report zero logging errors after the fix above, so the
        remaining failures are not this change's. Committed with
        `SKIP_TYPECHECK=1`, which is the escape the hook itself prints for this
        case; the two projects this item owns typecheck clean under both their
        lib and spec configs.

        Verification: 12,006 workbench-kit tests (239 new), 157 files, all
        passing; 145 shared/logging tests (93 new, all 52 prior still passing);
        0 typecheck errors in both projects under both lib and spec configs
        (including 12 pre-existing `void`-return errors in the shared/logging
        specs, fixed); export map regenerated clean at 161 entry points, 0
        violations; `log-leak-scan` exits 0 with the positive control escaping
        and all nine hazard canaries contained on both sinks.

        Scope stated: the module is `contract`-class, so the clock, the
        transports, the log store and the CI runner it is decided against are
        the host's. Every function is a decision over data. -->

        NOTE (S10.6, for S10.7): `evaluateDeletion` over the shipped sink
        register answers `partial` -- only Elasticsearch can delete by query,
        `http` is append-only-immutable and the rest expire by partition. S10.7
        asks for retention, legal hold and integrity over AUDIT records and will
        need that answer, along with `evaluateLegalHold`'s
        `hold-cannot-override-partition-expiry`, which is the case where a hold
        is recorded and the data goes anyway.
- [x] S10.7 Add audit retention, export, legal hold, and integrity verification.
      Evidence: tamper test and retention test.
  - [x] S10.7.a Classify audit event types/data fields by retention source,
        minimum/maximum, tenant/regulatory/contractual policy, and purge rules.
        Evidence: audit retention matrix.
  - [x] S10.7.b Implement immutable append-only storage/partitioning with
        hash/signature chain or equivalent integrity, trusted timestamp, backup,
        and restricted writer. Evidence: integrity design tests.
  - [x] S10.7.c Apply retention/expiry/archive/purge while preserving required
        tombstone/integrity linkage and deleting prohibited content. Evidence:
        clock-controlled retention tests.
  - [x] S10.7.d Implement legal hold by scope/reason/authority/start/release,
        prevent held purge, restrict visibility, and audit every action.
        Evidence: hold tests.
  - [x] S10.7.e Export authorized query/time/tenant/object/event scope in stable
        schema with redaction policy, completeness, hashes/signature, chain
        verification, and export audit. Evidence: export tests.
  - [x] S10.7.f Continuously/sample-verify sequence/hash/signature/timestamp/
        storage/backup consistency and surface missing/reordered/changed events.
        Evidence: verification job.
  - [x] S10.7.g Tamper, delete, reorder, duplicate, corrupt, expire, hold,
        export, and restore audit fixtures. Evidence: adversarial lifecycle
        report.

        <!-- S10.7: shipped as `libs/oshun/workbench-kit/src/audit-integrity.ts`
        (contract-class, entry point `./audit-integrity`, 162 entry points 0
        violations) + 101-test spec; `tools/audit-integrity-scan.mjs` (nx target
        `audit-integrity-scan`, run under tsx because `@oshun/audit-platform`'s
        `main` is `./src/index.ts`); and repairs to
        `libs/shared/audit-platform`'s `retention.ts`, `hash-chain.ts` and
        `evidence-export.ts`.

        THE VEIN: AN INTEGRITY CHECK IS A COMPARISON, AND BOTH SIDES OF THIS
        ONE CAME OUT OF THE SAME BOX. A hash recomputed from the document that
        stores it proves the document is internally consistent -- a property
        every competently forged document has. Detection needs ONE value the
        checker holds and the writer could not rewrite; if none can be named,
        what ran was a consistency check and it has to SAY so rather than
        return ok.

        Measured before writing anything, by driving the real services through
        sixteen probes: ten were undetected. `verifyChain()` seeds its expected
        previous-hash from `this.chain.range(start-1,start)[0].chainHash` --
        the same sidecar under attack -- and had no anchor parameter at all, so
        editing an event and recomputing the sidecar forward returned `ok:true`
        with 10/10 verified; truncating the tail of log AND chain returned
        `ok:true`; restoring from a five-event-old backup returned `ok:true`
        with the tip going backwards recorded nowhere. `getChainTip()` was
        commented "used by external anchors" and nothing supplied one.
        `verifyExport()` recomputed `hashBundle(bundle.events)` and compared it
        to `bundle.custody.bundleHash`, both operands inside the document, and
        NEVER READ `bundle.signature` -- so a forged signature verified. The
        seal covered the ANSWER and not the QUESTION: `filterApplied`, `reason`
        and `generatedBy` were all rewritable over the same bytes, making a
        single-tenant subset re-presentable as a complete export of everything.
        And the check was only runnable by the party being audited, since the
        chain arm needs `options.chainedStore`, i.e. the live store.

        Retention was the same failure pointed at time. `ageInDays` returned
        NaN for an unparseable `occurredAt`, NaN loses all four `<` comparisons
        in the tier ladder, and the record fell through to the final `else`:
        `occurredAt: 'not-a-timestamp'` came back `tier: 'past_retention',
        ageDays: NaN` and landed on `getPurgeCandidates` on its first sweep. A
        far-future date came back `ageDays: 0` and was retained forever. The
        age was keyed on `occurredAt`, which the writer chooses and the ingest
        service copies unbounded beside the `ingestedAt` it stamps itself.
        `redactEvent` returned a redacted COPY and deleted nothing;
        `getPurgeCandidates` returned a list; and `CanonicalAuditEventStore` has
        no delete method -- so "no delete method is the strongest enforcement
        of immutability" is really the absence of a governed deletion path,
        which forces every real deletion through an ungoverned one, while
        `InMemoryCanonicalAuditEventStore.resetForTests()` clears every event on
        the production type behind a comment claiming it "only runs in test
        setup". A legal hold was `RetentionPolicy.legalHold`, one boolean on a
        config row: `update({...policy, active:false})` reported `legalHold:
        false` for a record reported `true` a moment earlier because the
        inactive branch returned before the hold branch, and `unregister(tag)`
        removed the hold outright -- neither leaving a record, naming an
        authority, or being a release. And `redactEvent`'s `'pii'` mode
        replaced `actorId` with `sha256(actorId).slice(0,32)` "so identity
        correlation remains possible", which reproduced
        `sha256('ada@example.com')` truncated to 32 hex EXACTLY -- S3.3.g's
        `content-digest` residue written down as a feature -- while leaving
        `reason` and `metadata`, where the address survived verbatim.

        SEVEN DECISIONS. Verification TAKES an anchor and refuses without one,
        reporting `unverifiable` rather than `verified` for every sequence no
        anchor covers, which makes the exposure window a real quantity. A
        witness that is the writer is NOT a witness, so the six witness kinds
        are graded and only three count as coverage. Retention and integrity
        are the same mechanism pointed in opposite directions, so expiry
        PRODUCES a tombstone. The digest that keeps the chain and the digest
        that re-identifies the subject are the same bytes and only a KEY
        separates them: the commitment is keyed with a per-record nonce the
        purge destroys, so the link survives and the lookup key does not, and
        `tombstoneFaults` asks about the nonce by name. The seal covers the
        question and the answer, with completeness four-valued because
        `duplicated` was measured rather than imagined -- the store's cursor
        silently restarts at the head when it cannot find the cursor row, and
        the real `EvidenceExportService` produced a bundle reporting
        `eventCount: 7` over six distinct events with `verifyExport` returning
        `ok: true`, the duplication sealed in and certified. A break stops the
        claim and not the report, so the run emits a row for EVERY entry, three
        checks each, three-valued -- the `IntegrityReport` S7.17 already
        renders and which nothing in this repository could produce. And a
        deletion's record must outlive what it deleted, the one retention rule
        specific to an audit log: if the class recording a purge expires before
        the class it purged, "lawfully deleted" and "never here" become the
        same bytes.

        Repairs to the shipped platform, all measured: `verifyChain` now takes
        anchors, walks every entry instead of returning at the first break,
        emits three-valued per-entry rows, and gives `order-violation` its
        first producer; `checkTipAgainstAnchors` catches truncation and
        rollback, which no per-entry walk can. `verifyExport` reads the
        signature, verifies it under a key the CALLER already trusted, seals
        the question in a `custodyHash`, reports distinct-vs-emitted counts and
        a completeness verdict, and returns `unverifiable` rather than `ok`
        when nothing outside the bundle was supplied. Retention refuses to date
        an undatable record instead of purging it, measures age on the
        platform-stamped clock and reports which one it used, decides the hold
        before the policy's own state, and requires a keyed salt for
        pseudonymisation -- dropping the identifier rather than emitting a
        lookup key when none is configured.

        Verification: 12,108 workbench-kit tests (101 new), 158 files, all
        passing; 1,000 audit-platform tests (28 new, every prior one still
        passing), 49 files; 0 typecheck errors in both projects under lib and
        spec configs; export map regenerated clean at 162 entry points, 0
        violations; `audit-integrity-scan` exits 0 with all three controls
        passing. The scan reports three columns -- shipped-with-nothing,
        shipped-given-an-anchor, kit -- and the gap between the first two IS
        the cell: with nothing from outside the shipped verifiers miss 6 of 11
        lifecycle actions, and handing the SAME code an anchor or a trusted key
        recovers 4 of them. CONTROL 3 is the strong result: `reviewLifecycle`,
        handed the platform's signal inventory, predicts the measured miss set
        EXACTLY in both columns (6/6 and 2/2, no false positives or negatives).
        The two it still misses under either column -- `duplicate` and `hold` --
        are design gaps rather than missing calls: there is no per-object
        sequence for a replay to repeat, and a hold that IS a config row is
        deleted when the row is.

        CONTROL 3 earned its place three times. Its first version predicted
        misses from `needsExternalValue` alone and was WRONG about three
        attacks, because a verifier misses an attack for two different reasons
        -- no witness, or no reading at all -- and that column only covers the
        first. Its second version went stale the moment the platform was
        repaired and caught that too. Its third arm asks the same question of
        THIS MODULE, and the first answer was a finding: `VERIFICATION_SIGNALS`
        named `tip-below-anchor`, `LIFECYCLE_BINDINGS` bound both `truncate`
        and `restore` to it, and `runVerification` never emitted it -- the
        `signal-with-no-producer` fault this module defines, in this module,
        with the scan's kit column quietly computing the detection in the
        harness instead. `runVerification` now produces it (and
        `verificationHeadline` leads with it, because `exposureWindow` counts
        entries ABOVE the anchor and a truncated log has none, so the headline
        would otherwise have claimed full coverage of a log missing its tail).
        All three arms now predict `none`.

        Scope stated: the module is `contract`-class, so the digest, the key,
        the timestamp authority, the clock and the store it is decided against
        are the host's. S3.3's retention resolution, hold record and purge
        steps, S7.17's report shape, S10.6's field classes and S9.3's
        `ImpactCount` are composed rather than re-decided.

        TRAP FOUND, FIXED IN THIS LIBRARY AND OPEN ELSEWHERE: a raw NUL byte in
        a source file makes it BINARY to grep, so the repository's own
        mandatory stub-indicator scan and `production-scan` read nothing from
        it and report zero hits for the same reason a clean file does. This
        module was written with two (separator bytes in `sealPreimage`) and the
        first adversarial scan over it was therefore meaningless. Fixed here by
        length-framing each member instead of joining on a separator -- which
        also removes a real ambiguity the module's own text forbids -- and in
        `log-privacy.ts` (S10.6) and `trace-continuity.ts` (S10.2) by writing
        the separator as `\u0000`. **33 files repo-wide still have this and are
        invisible to every grep-based gate**, across contracts, euterpe, metis,
        isis, yemaya, sophia, aphrodite and the V-series. -->

        NOTE (S10.7, for S10.8): `AUDIT_RECORD_CLASSES` includes
        `billing-event`, and `classRetention` resolves its bounds but nothing
        yet meters it. S10.8 asks for cost attribution with sampled invoice
        reconciliation, and a cost record is an audit record with a number on
        it: its retention row, its `attestsAbout` edge and the
        `attestation-expires-before-its-subject` rule all apply, and a
        reconciliation that outlives the records it reconciled is the same
        defect this cell named.
- [x] S10.8 Add provider, storage, compute, egress, and human-review cost
      attribution. Evidence: sampled invoice reconciliation.
  - [x] S10.8.a Maintain versioned rate cards and units for provider/model/tool,
        database/blob/cache/search/queue/log, CPU/GPU/memory, transfer/egress,
        DCC/license, and reviewer role/time. Evidence: rate registry.
  - [x] S10.8.b Capture metered quantity, unit, source, time window, region/
        tier, attempt, job/run/artifact/release, actor/project/tenant/provider
        attribution, and confidence. Evidence: usage schema.
  - [x] S10.8.c Calculate estimated/reserved/actual/refunded/unallocated cost
        with currency/rate version and no fabricated zero on missing data.
        Evidence: calculation tests.
  - [x] S10.8.d Allocate shared storage/compute/egress/observability/license/
        reviewer cost through approved reproducible rules and retain raw/derived
        distinction. Evidence: allocation tests.
  - [x] S10.8.e Reconcile provider invoices, cloud billing exports, storage/
        egress meters, license records, and review time samples to internal
        usage. Evidence: sampled reconciliation.
  - [x] S10.8.f Record variance/missing/late/duplicate/disputed/refund/credit
        and correction without rewriting prior periods. Evidence: variance
        tests.
  - [x] S10.8.g Expose permission-aware cost by domain/workbench/project/tenant/
        capability/provider/resource/release with privacy and bounded labels.
        Evidence: cost projection tests.

        <!-- S10.8: shipped as `libs/oshun/workbench-kit/src/cost-attribution.ts`
        (contract-class, entry point `./cost-attribution`, 163 entry points 0
        violations) + a 107-test spec, `tools/cost-attribution-scan.mjs` (nx
        target `cost-attribution-scan`, under tsx), and repairs to
        `libs/shared/ai/src/usage/index.ts`,
        `libs/shared/gpu-dispatcher/src/cost-tracker.ts` and
        `libs/oshun/creative-orchestrator/src/usage-accounting.ts`.

        THE VEIN: A COST IS A MEASUREMENT TIMES A PRICE, AND EVERY MISSING
        FACTOR RESOLVES TO A NUMBER INSTEAD OF A REFUSAL. Thirty probes over
        the three shipped meters, twenty-five findings, five green controls.
        Four ways it fails, plus a fifth with no code to point at:

        1. AN INVENTED PRICE. `UsageTracker.getPricing` ended with
           `// Default to a reasonable estimate` and `{ 1.0, 3.0 }` per million.
           Twenty opus-class calls through a `maxCost: 1.0` gate settle at
           $3.20 and the gate fires on that, against ~$60.00 of real spend.
        2. A SUBSTITUTED PRICE. `CostTracker.calculateCost` priced an unknown
           GPU at the DEFAULT type's rate: 60s of `'H200'` recorded 2.34c at
           RTX_4090's rate, 7.1x under H100, while the record still said H200.
           The number and the label came from different GPUs.
        3. AN ABSENT PRICE. One line down, `return 0` — an hour of H100 against
           an empty card recorded $0.00 and summed in as real. Its neighbours:
           an untimed job took `calculateDuration` -> 0 -> free, `enabled:false`
           handed the caller a $0.00 record to persist, and cents rounded to
           1/100c made every T4 job under 0.18s free ($28.00 lost per million,
           job count exactly right).
        4. AN ABSENT QUANTITY, in the module that gets everything else right.
           `priceUsage` THROWS when a unit has no rate — the honest seam — but
           the loop opens `units[unit] ?? 0; if (quantity === 0) continue`, so
           THE REFUSAL WAS GUARDED BY THE PRESENCE OF THE VERY DATA WHOSE
           ABSENCE IT EXISTS TO CATCH. Report the unpriced unit and it refuses;
           omit it and the same receipt prices clean. Measured: `priceUsage(…,
           {})` returns `costUsdMicros: 0`, zero line items, basis
           `'metered-rate-table'`; `{}` and `{ cachedInputTokens: 0 }` return
           byte-identical results.
        5. And an unmetered class contributes the most confident zero of all,
           because no branch was taken to reach it. `storageByteSeconds` and
           `egressBytes` are in the unit vocabulary with no producer anywhere,
           and reviewer time has no rate in the repository at all.

        THE WITNESS IS THE STATEMENT AND IT IS ONLY A WITNESS OVER THE PART
        THAT DID NOT COME FROM IT. `MeasuredUsageReceipt.costBasis` already
        recorded, per receipt, whether the figure came from our rate table, the
        provider's billing, or an unsettled reservation ceiling —
        `reconcileProviderInvoice` never read it. So a 100% provider-billed
        total reconciled `matched` with drift 0 against that provider's own
        invoice; one 900,000-micro ceiling against a correct 15-micro invoice
        reported drift-alert at -899,985, reading as the PROVIDER under-billing
        us by $0.90; an invoice of 0 with zero receipts reported `matched`.

        ALSO MEASURED AND REPAIRED: `cacheReadInputTokens` and
        `cacheCreationInputTokens` arrive on the response, are stored on the
        record, and were read by NOTHING — 100k cache-read tokens on an Opus
        model recorded $0.0525 against a real $0.2025, and 100k cache-WRITE
        tokens $0.0525 against $1.9275 (36.7x under, on the most expensive
        token class this repository's own workload generates).

        TRAP, IN MY OWN REPAIR: `ceil(0.028 * (100/1000) * 10_000)` returns 29
        rather than 28, because `0.028 * 0.1` is `0.0028000000000000004` and
        the ceiling amplifies the last bit into a whole unit. The rate is now
        rounded to an integer of micro-cents per second ONCE, where the
        intended value is unambiguous, so every later step is exact. And the
        first pass at the same repair fixed `totalCostCents` while leaving
        `byJobType`/`byGpuType` doing `+= costCents ?? 0` — the same defect one
        field over, caught by the mandatory adversarial scan over my own diff.

        The scan reports THREE columns — legacy (the pre-S10.8 behaviour,
        reproduced from the original source lines), shipped, kit — and the gap
        between the first two IS the cell: legacy 0/12 distinguishable, shipped
        12/12, kit 12/12, with a control proving the legacy arm misses
        everything so the harness can be seen to detect rather than only ever
        print clean. -->

        NOTE (S10.8, for S10.9): `reviewCostSystem` predicts ten defect classes
        and this harness has scenarios for six. The four unexercised —
        `unversioned-rate`, `unmetered-class-as-zero`, `prior-period-rewrite`,
        `residual-disclosure` — are real and named rather than quietly dropped.
        S10.9 asks for spend and unallocated-cost anomaly alerts, and an alert
        computed over a total that cannot say which of its parts were priced
        inherits every one of them: a burn-rate forecast over a partial
        denominator under-predicts exhaustion by exactly the share nobody
        metered, and `costComplete` is the field that says so.
- [x] S10.9 Add quota and anomaly alerts before hard exhaustion. Evidence:
      threshold and recovery tests.
  - [x] S10.9.a Register rate/concurrency/storage/compute/GPU/token/provider/
        license/queue/spend/reviewer capacity quotas by scope, window, owner,
        and hard limit. Evidence: quota registry.
  - [x] S10.9.b Define warning/critical forecast thresholds by absolute use,
        percentage, remaining time, burn rate, queue growth, and provider/reset
        delay. Evidence: threshold policy.
  - [x] S10.9.c Detect
        volume/rate/variance/retry/failure/resource/provider/cost/ unallocated
        and behavioral anomalies with versioned model/rule and uncertainty.
        Evidence: anomaly fixtures.
  - [x] S10.9.d Alert before exhaustion with affected capability/scope, current/
        forecast/limit/reset, owner, dashboard/runbook, mitigation, and safe
        context. Evidence: alert payload tests.
  - [x] S10.9.e Deduplicate correlated quota/anomaly/dependency symptoms and
        route/escalate by ownership and severity. Evidence: event storm tests.
  - [x] S10.9.f Test recovery/reset/quota increase/demand reduction/provider
        recovery and clear only after sustained safe state. Evidence:
        clock-controlled recovery tests.
  - [x] S10.9.g Verify hard limits still fail closed if warning/alert pipeline
        is unavailable. Evidence: alert outage test.

        <!-- S10.9: shipped as `libs/oshun/workbench-kit/src/exhaustion-alerts.ts`
        (contract-class, entry point `./exhaustion-alerts`, 164 entry points /
        0 violations) with a 108-test spec, plus
        `libs/oshun/workbench-kit/tools/exhaustion-alert-scan.mjs` (nx target
        `exhaustion-alert-scan`).

        THE VEIN: AN ALERT BEFORE EXHAUSTION IS A CLAIM ABOUT HOW MUCH TIME IS
        LEFT, AND EVERY THRESHOLD IN THIS REPOSITORY IS SET ON A LEVEL. "80% of
        the cap" answers how much is gone; the alert exists to answer how long
        you have got, and those coincide only when the burn rate is constant and
        known -- and the burn rate is the one quantity nobody measured. The rule:
        FOR EVERY THRESHOLD, STATE THE LEAD TIME IT BUYS AND THE MITIGATION IT
        HAS TO OUTRUN.

        Measured against the shipped code on 2026-08-14, all reproduced:

        1. `checkBudget` warns at `utilization >= warnAtFraction`. Against a
           1000-unit cap at 500 units a minute the first `warn` lands at t=95s
           and `exceeded` at t=119s -- twenty-four seconds. `BudgetCheck` was
           `{ verdict, utilization }`, so a caller could not tell twenty-four
           seconds from twenty-four hours.
        2. And at a coarse grain the warning state is UNREACHABLE: from used=700
           of 1000, a request of 50 is `ok` (0.750) and a request of 301 is
           `exceeded` (1.001).
        3. THE DEADLINE WAS ANCHORED TO THE EVENT IT WAS SUPPOSED TO OUTLIVE,
           and it is four words of source: `elapsedSinceCap = nowUnixSeconds -
           meter.lastUsedAtUnixSeconds`. The name says "since cap", the value
           says "since last use", and `consumeBudget` rewrites it on every
           consumption -- a timer that measures from the most recent occurrence
           of the thing it is timing out never expires while that thing keeps
           happening. Measured: 100 units/60s against a 1000-unit cap with a
           300s grace sits at `grace` after 24h at 143.4x over, with
           `buildUsageMeterCard` carrying the identical anchor so the customer
           still reads "Grace window active -- please wrap up in-progress work".
           The only exit is to stop, that is, to have already stopped.
        4. COMPOSED, 2 AND 3 GIVE UNBOUNDED OVERSPEND WITH ZERO WARNINGS. Over a
           30-day period: warn=0, exceeded=0, grace=41,667, final usage 4,168x
           the cap -- and the state it rests in is the one meaning "handled".
        5. A GRACE WINDOW IS A LOAN AGAINST A WARNING, and nothing asked whether
           the warning was delivered. That is S10.9.g from the other side: the
           hard limit must not depend on the alert pipeline, and the soft slack
           must.
        6. `zScore` returned `value === mean ? 0 : Infinity` for a zero-variance
           baseline, so against a baseline flat at 100 both 99 and 0 scored
           +Infinity -- two windows of an agent getting CHEAPER proposed a
           quarantine. `@metis/cost-tracking`'s `detectOutliers` and
           `@lakshmi/transactions` get the identical case right, so it is a
           defect and not a house convention.
        7. And +/-Infinity does not survive the wire: `JSON.stringify` turns it
           into `null`, so the proposal's whole evidence arrived reading as "no
           score computed".
        8. `AnomalyBaseline.sampleCount` was stored and read by NOTHING:
           baselines of n=2 and n=10,000 produced byte-identical verdicts,
           quarantine included, with no sample size, interval or rule version on
           any of them.
        9. `QuotaTracker.TenantState.warned` was declared, initialised `false`,
           reset `false` on window roll, and assigned `true` nowhere -- 100
           requests against a 100-request quota at an 80% warn point produced 21
           identical warnings with `warned` still `false`.
        10. A DEDUP MARK MUST NOT OUTLIVE THE WINDOW IT DEDUPLICATES WITHIN, and
           the ratchet that works is worse than the one that does not.
           `CostCalculator.lastAlertThreshold` suppresses repeats correctly and
           is cleared only by `clear()`/`resetTracking()`, while the window it
           guards is a ROLLING THIRTY DAYS. Measured on the real object: alert at
           95% of a 10,000c budget, take the S10.9.f recovery path and raise it
           to 100,000c, burn 95.5% of the new one -- ZERO alerts. Cross 100% once
           and no budget alert ever fires again for that calculator's lifetime.
           Recovery is the thing that silences the next exhaustion.
        11. `checkBudgetAlert` documents "the highest threshold that has been
           crossed" and assigned unconditionally in array order, so at 95% used
           `[0.5,0.75,0.9]` returned 0.9 and `[0.9,0.75,0.5]` returned 0.5.
        12. Five of the eleven classes -- concurrency, provider, license, queue,
           reviewer -- had no registry entry anywhere, and an unregistered quota
           contributes the most confident silence of all.

        TRAPS, BOTH IN MY OWN DIFF, both caught by the mandatory adversarial
        pass: `forecastExhaustion` reported `burnPerMs: 0`, `method:
        'endpoint-exact'` and `resetDetected: false` whenever there was no burn
        estimate -- an unmeasured rate reported as flat, a method that never ran,
        and a negative finding from an examination that never happened, which is
        S10.8's vein reproduced inside S10.9's own repair. And
        `const signed = z ?? 0` in the anomaly repair was a fabricated reading
        one guard away from a decision. Also: `precision: 'exact'` was claimed
        over a fitted float slope, and `dispatch` folded two UNRELATED quotas
        into one page because any non-root alert was suppressed as
        `derived-from-root` -- correlation becoming censorship.

        The scan reports THREE columns -- legacy (the pre-S10.9 behaviour,
        reproduced from the original source lines), shipped, kit -- over
        seventeen conditions: legacy 0/17, shipped 12/17, kit 17/17, and it exits
        non-zero if the legacy arm ever scores as well as the kit, which would
        mean the harness had stopped measuring. The five the shipped arm does not
        reach are the ones with no in-tree seam to reach them with: forecasting,
        pipeline-conditional grace, the enforcement split, the unregistered-class
        list and the invented reset instant all live in the kit by design. -->

        NOTE (S10.9, for S10.10): the enforcement split this section ships --
        `hardLimitReached` computed before the pipeline is read, soft slack
        withdrawn when it cannot deliver -- is one dependency's degradation plan
        written out by hand. S10.10 asks for that plan for EVERY dependency, and
        the field it will need is the one `QUOTA_WINDOW_KINDS` already forced:
        `concurrent` and `non-renewing` have no reset instant, so "degraded until
        it recovers" has no recovery time to quote and a mode matrix that assumes
        one will invent it.
- [x] S10.10 Add graceful degradation plans for each dependency. Evidence:
      dependency outage matrix names what remains read-only or unavailable.
  - [x] S10.10.a Inventory each dependency and capability/operation/read/write/
        job/gate/publish/release path that uses it. Evidence: dependency-
        capability matrix.
  - [x] S10.10.b Define trigger/classification for not-configured, slow,
        partial, stale, rate-limited, unavailable, corrupt, incompatible, and
        recovery state. Evidence: trigger tests.
  - [x] S10.10.c Decide per capability what remains live read, cached/stale
        read, local/offline edit, queued operation, read-only, limited, manually
        handled, or unavailable. Evidence: mode matrix.
  - [x] S10.10.d Define data/gate freshness, authorization, trust, rights/
        consent, cost, capacity, and safety constraints that prohibit fallback
        or fail-open. Evidence: invariants.
  - [x] S10.10.e Implement circuit/timeout/retry/cache/queue/provider
        alternative and explicit user/API state without fabricated output.
        Evidence: implementation tests.
  - [x] S10.10.f Define recovery probe, reconciliation/replay/recompute/
        revalidation, stale state clearing, user notification, and SLO.
        Evidence: recovery tests.
  - [x] S10.10.g Fault each dependency and verify exact retained/unavailable
        behavior across UI/API/job/audit/telemetry/runbook. Evidence: outage
        report.

        <!-- S10.10: shipped as `libs/oshun/workbench-kit/src/degradation-plans.ts`
        (contract-class, entry point `./degradation-plans`, 165 entry points / 0
        violations) with a 113-test spec, plus
        `libs/oshun/workbench-kit/tools/degradation-plan-scan.mjs` (nx target
        `degradation-plan-scan`), and repairs to
        `libs/shared/rate-limit/src/limiters/graceful.ts` (+ a 20-test spec),
        `libs/shared/rate-limit/src/types.ts`,
        `libs/shared/rate-limit/src/middleware/rate-limit.ts`,
        `libs/shared/http-client/src/circuit-breaker.ts`,
        `libs/shared/http-client/src/types.ts` and
        `libs/shared/resilience/src/circuit-breaker.ts`.

        THE VEIN: A DEGRADED ANSWER IS STILL AN ANSWER, AND EVERY ONE OF THEM
        WAS RETURNED IN THE TYPE THAT MEANS "WE ASKED". A degradation plan is a
        sentence about a dependency and the caller never sees the sentence -- the
        caller sees a value with the same type it had when everything worked, so
        the field that meant "the policy permits this" now means "we could not
        reach the thing that decides", and nothing on the value says which. The
        rule: FOR EVERY FALLBACK, NAME THE FIELD ON THE RETURNED VALUE THAT SAYS
        IT IS ONE, AND THE CONSUMER THAT BRANCHES ON THAT FIELD.

        The subject is `@oshun/rate-limit`'s `GracefulRateLimiter` -- this
        repository's only component whose name is the thing this cell asks for --
        plus both circuit breakers. Measured on 2026-08-14, all reproduced:

        I. THE VERDICT TRAVELS WITHOUT THE MODE.
        1. `RateLimitResult` is `{ allowed, remaining, limit, resetAt,
           retryAfter }` and not one field is provenance. `isDegraded()` is on
           the limiter; the result is what reaches `createRateLimitMiddleware`,
           which writes it into headers and a body. The two part company at the
           return statement.
        2. AND THE ONE NUMBER THAT COULD HAVE HINTED AT IT IS UNREPRESENTABLE IN
           BOTH TRANSPORTS. `createAllowResult()` returned `limit: Infinity`; the
           middleware does `result.limit.toString()`, so the header was literally
           `X-RateLimit-Limit: Infinity` (parses to NaN) and `JSON.stringify`
           rendered the same field `null`. Measured: `{"allowed":true,
           "remaining":null,"limit":null,...}`. S10.9's seventh finding on the
           degradation path.
        3. AND THE OTHER DEFAULT LIES ABOUT WHOSE FAULT IT IS. `deny-all` against
           a dead limiter produced HTTP **429** with `X-RateLimit-Limit: 0` -- the
           user told their quota is zero when the truth is that the quota is
           unknown. Wrong status class, wrong party, and "wait and retry" is the
           mitigation that keeps the dependency down.

        II. THE FALLBACK ANSWERS A DIFFERENT QUESTION.
        4. `fallbackStrategy: 'in-memory'` constructed `new InMemoryRateLimiter()`
           = 100/60s, with no field on the config through which the primary's
           policy could be passed. A deployment whose policy is 1000 reported
           `limit: 100`, in the same field, unmarked -- and per process, so the
           effective global limit was 100 x replicas while each client was told
           100. `status()` during an outage returned `{allowed:true, remaining:10,
           limit:10}` for a user who had spent their quota, from a store that had
           never seen the key.

        III. A TIMEOUT IS NOT A FAILURE TO ACT.
        5. `withTimeout` neither cancelled nor reconciled. Measured with a 20ms
           budget against a limiter that consumes and then answers in 200ms: the
           primary recorded one token, the fallback recorded another. Two tokens
           for one request.
        6. AND THE TIMEOUT BUDGET WAS DESTROYED BY WRITING THE FIELD. The
           constructor was `{ ...defaults, ...config }` and `operationTimeout` is
           optional, so `operationTimeout: undefined` -- which the type permits
           and every options-forwarding wrapper produces -- reached
           `setTimeout(fn, undefined)`, which fires after **1ms**. Measured
           against a HEALTHY limiter at a realistic 3ms round trip: 20 of 20
           answered by the fallback, `degraded=true`, primary consuming them all
           as well. The same spread disabled the breaker: `failureThreshold:
           undefined` makes `n >= undefined` always false, measured
           `circuit=closed` after 20 consecutive failures.

        IV. RECOVERY. `degraded` cleared on ONE success in the closed state. The
        substitute was never cleared or reconciled: six consumes during an
        outage, then a SUCCESSFUL `reset('user:jane')` against the recovered
        primary, and the fallback still held `remaining: 4`. `resetCircuit()`
        assigned `circuitState = 'closed'` and THEN read `previousState` from it,
        so the one event recording a manual intervention always said
        `{previousState:'closed', newState:'closed'}`. And `getCircuitState()`
        never evaluated the reset timeout, so an idle limiter read `open` forever.

        V. THE SAME SHAPE TWO LIBRARIES OVER. `@oshun/http-client`'s breaker
        returned `true` from `canExecute()` for every caller in half-open --
        measured **500 of 500** admitted on the first tick after `resetTimeout`,
        so the recovery probe was the entire production load aimed at the
        dependency that had just failed -- and `getMetrics().successes` was the
        half-open probe counter, measured **0** after 100 successful requests
        beside `totalRequests: 100`. `@oshun/resilience`'s breaker handed the
        caller `resetAt = openedAt + resetTimeoutMs` when refusing a half-open
        probe, which is by definition in the past: measured **Retry-After
        -0.5s**, i.e. "retry now", during the one state that exists to admit one
        probe at a time.

        VI. AND THE PLAN WITH NO CALLER. `GracefulRateLimiter` is exported from
        `@oshun/rate-limit`'s public barrel and constructed nowhere in this
        repository, so every defect above was latent on an exported API rather
        than live -- which is its own finding and the reason this cell ships the
        matrix rather than a runtime.

        VII. AND TWO IN THE KIT'S OWN S10.5 REGISTER, found by turning the rule
        on the prior cell. `dep:trust-store` declares `mitigations: ['timeout',
        'cache']` against `'verify a signature'`, and `dep:identity` declares
        `cache` against `'authorize any request at all'`. Revocation and
        withdrawal are NEGATIVE facts a cached copy can only be missing, so a
        cache in front of either fails open by construction, and neither
        registration carried a staleness bound or a direction. The rule those two
        needed: A CACHE MAY MAKE A DECISION STRICTER, NEVER LAXER.
        `cachedDecisionRisks()` reports both as a function rather than as a
        paragraph, so it stays true as the register changes.

        DECISIONS. A MODE IS SERVED BY DEPENDENCIES AND THE GRAPH DECIDES
        WHETHER IT CAN SERVE: `DEPENDENCY_SUBSTRATE` is new here because S10.5's
        `observedThrough` answers "can this dashboard report its own failure" and
        not "does this fallback run through the thing that failed".
        `dep:primary-store-scan` is a real standby for `dep:search-index` and is
        not one for `dep:primary-store`. READ-ONLY IS A PROPERTY OF THE SET AND
        NOT OF ANY PATH -- every effectful path stopped or queued and at least one
        read still answering -- computed by `productMode()` from the rows rather
        than declared beside them; `dep:primary-store` unavailable comes out
        `read-only` with 7 of 7 read paths answering and none of its 8 effectful
        ones, and `corrupt` comes out `unavailable`. UNKNOWN IS A CONDITION: a
        classifier handed no probe answers `unknown`, never `healthy`. CORRUPT
        OUTRANKS UNAVAILABLE and may not be replaced by a copy of itself.

        TRAPS, ALL FOUR IN MY OWN DIFF, all caught by the mandatory adversarial
        pass or by the module's own review of its own register:
        (1) the first `not-configured-treated-as-an-outage` check read the
        `because` prose for incident words and REFUSED the sentence "this is a
        decision about what was bought and not an incident" -- a word-level check
        cannot see a negation, so the state is now a `presentedAs` field;
        (2) `path:assisted-action`'s `read-only` fallback was served by
        `dep:model-provider-primary`, which rests on the gateway it was meant to
        survive -- found by my own substrate check, fixed by subtracting the
        closure;
        (3) the repaired `createAllowResult` fell back to `limit: 0` when no
        policy was declared, which is S10.8's vein reproduced inside S10.10's own
        fix -- a missing factor resolving to a number instead of a refusal; the
        policy is now required and the constructor throws without it;
        (4) `recordSuccess` read `consecutiveSuccesses` AFTER
        `transitionTo('closed')` zeroed it, so the counter that gates recovery
        was reset by the transition that indicates recovery -- the run length is
        now captured first.
        Plus one in the shipped register rather than the code: the recovery plan
        was derived from the path's KIND and the matrix from the dependency's
        ALTERNATIVES, so a save path whose only degraded mode is `unavailable`
        declared a replay of work it never accepted. `recoveryFor` now reads the
        modes the matrix actually contains.

        The shipped matrix is 41 capability paths over the 19 registered
        dependencies, 82 plans, 9 impairment rows each, reviewed clean:
        `reviewDegradationPlans` returns 0 refusals and 0 repairs.

        The scan reports THREE columns -- legacy (the pre-S10.10 behaviour,
        reproduced from the original source lines), shipped, kit -- over twenty
        conditions: legacy **0/20**, shipped **16/20**, kit **20/20**, and it
        exits non-zero if the legacy arm ever scores as well as the kit. The four
        the shipped arm does not reach are the ones with no in-tree seam: the
        substrate graph, the direction a cache may move a decision, the outage
        report's unobserved-versus-unaffected distinction and the
        configuration-versus-incident split all live in the kit by design. -->

        NOTE (S10.10, for S10.11): `productMode` calls `dep:primary-store`
        unavailable `read-only` because seven read paths still answer -- from
        `dep:read-cache`, whose contents were written by the store that is now
        down. S10.11 asks for a cross-store recovery point, and the same question
        arrives one layer lower: a backup of the datastore and a backup of the
        cache, the blob store and the search index are four snapshots taken at
        four instants, and `DEPENDENCY_SUBSTRATE` already says which of them is
        derived from which. A restore that reconciles rows against blobs without
        asking which side was authoritative at the recovery point is the same
        defect as a fallback that reports a substitute's number in the field that
        means the policy.
- [x] S10.11 Add backup consistency checks across relational, blob, search, and
      provenance stores. Evidence: restore drill.
  - [x] S10.11.a Define a cross-store recovery point using database transaction/
        log, blob inventory/version, search projection/checkpoint, provenance/
        ledger manifest, event/outbox, and key/config markers. Evidence:
        consistency contract.
  - [x] S10.11.b Verify backup catalogs, objects, checksums, encryption/
        decryptability, schema/version, retention, immutability, and required
        external refs before restore. Evidence: backup preflight tests.
  - [x] S10.11.c Restore relational metadata/revisions/events/audit, blob bytes,
        provenance manifests/ledger, and rebuild or restore search into
        isolation. Evidence: restore commands/log.
  - [x] S10.11.d Reconcile rows↔blobs, revisions↔hashes, provenance↔ingredients,
        search docs↔authoritative projections, tombstones/holds, and tenant
        counts. Evidence: integrity queries.
  - [x] S10.11.e Rehash stored bytes and validate read/search/history/audit/
        lineage/gate/bundle/publish-preparation journeys. Evidence: restored
        system tests.
  - [x] S10.11.f Measure achieved point/time, missing/inconsistent/orphan/
        protected findings, remediation, and next drill. Evidence: signed drill
        report.

        <!-- S10.11: shipped as `libs/oshun/workbench-kit/src/restore-consistency.ts`
        (contract-class, entry point `./restore-consistency`, 166 entry points /
        0 violations) with a 62-test spec, plus
        `libs/oshun/workbench-kit/tools/restore-consistency-scan.mjs` (nx target
        `restore-consistency-scan`), and repairs to
        `scripts/operations/v1-restore-reconcile.mjs` (+8 tests, 14 total).

        THE VEIN: A RESTORE IS A CLAIM ABOUT WHAT CAME BACK, AND EVERY CHECK
        ASKED WHETHER THE SERVICE ANSWERS. A drill proves the estate can be
        rebuilt; what the checks establish is that a process started. Redis
        answers PING, a directory exists, a mirror exits zero, a reconciler finds
        no contradiction between two lists it was handed -- and every one of
        those passes over an EMPTY restore, which is the failure the drill exists
        to catch. The rule: FOR EVERY RESTORE CHECK, NAME THE EXPECTED COUNT IT
        IS COMPARED AGAINST, AND THE INSTANT THAT EXPECTATION WAS RECORDED AT. A
        check with no expectation cannot tell "nothing was lost" from "nothing
        was examined"; an expectation with no instant cannot tell a lost row from
        one written after the backup boundary.

        Measured on 2026-08-14, all reproduced:

        I. AN EMPTY RESTORE PASSES.
        1. `reconcileRestore` returned `safeToResumeAutonomy: true, blockers: []`
           over SEVEN EMPTY INVENTORIES -- the same verdict a full estate
           produces. `validateRecordArray` is satisfied by an empty array, and no
           field on the result was a count.
        2. TWO DIFFERENT CLAIMS, ONE DENOMINATOR. `safeToResumeAutonomy` and
           `noCompletedMutatingEffectWillRepeat` were both literally
           `blockers.length === 0`, so an EXPIRED APPROVAL turned both false and
           an estate with no provider effects turned both true -- the second
           being true because nothing was examined.
        3. `redisRestoreVerified: true` IS ASSERTED FROM A PING. The box-loss
           drill's report is a jq template with seven literal `true`s; the only
           preceding Redis step is `redis-cli PING`, which an empty Redis answers.
           `objectRestoreVerified`, `fileStateRestoreVerified` and
           `configurationRestoreVerified` rest on a `mc mirror` that succeeds over
           an empty export, `[ -d "${path}" ]` on directories that may be empty,
           and `[ -f stack.env ]` which a zero-byte file satisfies. Only the
           PostgreSQL path has a floor (`[ "${checksum_count}" -gt 0 ]` with a
           matching `database_count` equality): one class of six is counted. And
           `keyRecoveryVerified: true` has no step at all.

        II. THE RECOVERY POINT IS ONE INSTANT AND EVERY STORE WAS CAPTURED AT ITS
        OWN.
        4. NOT ONE OF THE SIX MARKERS S10.11.a NAMES EXISTS. `db-backup.sh` sets
           one `TIMESTAMP` and writes `created=$TIMESTAMP` into a manifest
           covering N databases dumped SEQUENTIALLY, each `pg_dump` in its own
           transaction snapshot; neither backup script passes `--snapshot=`, calls
           `pg_export_snapshot()`, or records `txid_current()` or
           `pg_current_wal_lsn()`. `pg-backup.sh`'s per-database `createdAt` is
           `date -u` evaluated AFTER the dump finished -- a wall clock, and a wall
           clock cannot say which committed transactions are inside.
        5. THE ONE NUMBER THAT MEASURES THE SKEW IS WRITTEN AND READ BY NOTHING.
           `offbox-backup.sh` records `latestPostgresAgeSeconds` -- an explicit
           acknowledgement that the relational side is older than the Redis
           checkpoint and MinIO mirror taken in the same run -- and a grep for a
           reader returns the two lines that write it.
           `OSHUN_OFFBOX_HEALTH_MAX_AGE_SECONDS` bounds `now - generated`: the age
           of the RECEIPT, not the skew between the stores it describes.
        6. AND TWO OF THE FOUR STORE CLASSES ARE NOT IN THE BACKUP SET. The
           snapshot declares `postgresql`, `redis`, `minio`,
           `content-service-runs`, `computer-use-snapshots`,
           `configuration-and-secrets`. Search is real (`elasticsearch:8.11.0`,
           `qdrant`) with no marker, no backup and no reconciliation collection.
           Provenance is the sharpest omission: `isis_provenance_ledger` is a hash
           chain (`sequence`, `chain_fingerprint`,
           `parent_chain_fingerprint REFERENCES ...`), so its last
           `(sequence, chain_fingerprint)` IS a recovery-point marker sitting
           unused in the schema -- the one store in the estate that can prove its
           own completeness, and nothing asks it to.

        III. AND THE COMPARISON THAT DOES RUN COMPARES TWO COPIES OF THE SAME
        NUMBER. `reconcileExactCollection('artifacts', 'digestSha256', ...)`
        matched restored artifacts to external ones on the digest RECORDED IN THE
        ROW on each side, so an artifact whose bytes no longer hash to their
        recorded digest reconciled clean. S10.7's vein on the restore path.

        DECISIONS. A MARKER RECORDS WHAT IS INSIDE AND AN INSTANT ORDERS THE
        CAPTURES, and a recovery point needs both, because ordering is what
        decides which inconsistencies are possible. A WALL CLOCK IS ADMISSIBLE
        FOR NOTHING BUT A CACHE. THE REFERENCED STORE MUST BE CAPTURED NO EARLIER
        THAN THE STORE THAT REFERENCES IT -- which makes inserts produce harmless
        orphans instead of dangling references -- and the safe order is NECESSARY
        AND NOT SUFFICIENT, because a delete in the skew window still strands the
        reference unless tombstone retention is at least as long as the skew. AN
        UNMATCHED ROW ON THE AUTHORITATIVE SIDE IS DATA LOSS AND ON THE DERIVED
        SIDE IS GARBAGE. AGREEMENT BETWEEN TWO DERIVED SIDES IS NOT EVIDENCE
        ABOUT THE RECORD. AN EXPECTATION READ OUT OF THE RESTORED COPY HAS ITS
        CONCLUSION IN ITS PREMISES. RECOVERY TIME IS MEASURED FROM DETECTION and
        the achieved recovery point from the EARLIEST capture, because work
        committed after the oldest store's capture is not uniformly recoverable.

        REPAIRS. `v1-restore-reconcile.mjs` now takes an optional `expectations`
        block -- an old input still PARSES and no longer PASSES, which is the
        fail-closed direction -- refuses a count read from the restored copy or
        carrying no instant, reports an `examined` block so a clean verdict reads
        as coverage, splits `noCompletedMutatingEffectWillRepeat` from
        `safeToResumeAutonomy` so an expired approval no longer refutes it and an
        unexamined effect set no longer confirms it, and refuses an artifact whose
        stored bytes were never rehashed. Result schema bumped to `.v2`.

        TRAP, IN MY OWN MODULE, caught by the adversarial pass: every check in
        `recoveryPointFaults` is a loop over markers or over required classes, so
        a recovery point with NO markers and a caller who required NO classes
        passed all of them -- this cell's own defect, in this cell. There is now
        an unconditional `recovery-point-with-no-markers` refusal ahead of the
        loops.

        The scan reports THREE columns -- legacy (the pre-S10.11 reconciler,
        reproduced from the original source lines), shipped, kit -- over sixteen
        conditions: legacy **0/16**, shipped **9/16**, kit **16/16**, and it exits
        non-zero if the legacy arm ever scores as well as the kit. The seven the
        shipped arm does not reach need a per-store capture marker, a reference
        graph, a refutability declaration and a detection instant, none of which
        the shell tooling has a seam for; they live in the kit by design. -->

        NOTE (S10.11, for S10.12): this cell measures a drill that an operator
        chose to run, on a host they prepared, at an instant they picked. S10.12
        asks for game-day scenarios with a measured DETECTION time, and
        `DrillTiming` already refuses to compute a recovery time without one --
        the field is there and the estate has no producer for it. The scenario
        register S10.12.a wants is the same shape as S10.10's mode matrix one
        layer up: a scenario that no capability path traverses is a rehearsal for
        an incident nobody can have.
- [ ] S10.12 Add disaster-recovery runbooks and game-day scenarios. Evidence:
      measured RPO/RTO and follow-up issues.
  - [x] S10.12.a Define disaster scenarios for regional/service/data/key/queue/
        provider/search/blob/database/bridge compromise or outage and per-
        capability RPO/RTO. Evidence: DR scenario register.

        <!-- S10.12.a: shipped as `libs/oshun/workbench-kit/src/game-day.ts`
        (contract-class, entry point `./game-day`, 167 entry points / 0
        violations) with a 118-test spec, plus
        `libs/oshun/workbench-kit/tools/game-day-scan.mjs` (nx target
        `game-day-scan`, legacy **0/17**, shipped **5/17**, kit **17/17**), and
        repairs to `scripts/isis/run_runpod_disaster_recovery_gameday.sh`,
        `run_provider_outage_drill.sh` and
        `run_endpoint_region_failover_drill.sh` with 12 tests in
        `scripts/operations/v1-game-day.test.mjs` (wired into
        `verify:operations:v1-disaster-recovery`).

        THE VEIN: A GAME DAY IS AN EXPERIMENT, AND EVERY ONE OF THESE REPORTED A
        RESULT WITHOUT PERFORMING IT. The rule: for every scenario, name the
        capability path its fault interrupts and the observation that would have
        refuted its pass.

        The register itself is `WORKBENCH_GAME_DAY_SCENARIOS` -- ten scenarios,
        one per class S10.12.a names, every `targets` entry a dependency id in
        the S10.5 register -- and `WORKBENCH_RECOVERY_OBJECTIVES`, a recovery
        point and recovery time for each of the 41 capabilities in S10.10's path
        register. Which capabilities a scenario interrupts is DERIVED through the
        substrate closure rather than declared, so a scenario cannot claim an
        impact the dependency graph does not support, and an optional use is
        reported as degradation rather than interruption.

        MEASURED, BEFORE THE REPAIRS (all reproduced by running the code):

        1. The invocation `docs/reference/ci-cd.md` line 248 gives --
        `STRICT=false DRY_RUN=true RUN_PROVIDER_FAILURE_SCENARIO=false
        RUN_REGION_FAILURE_SCENARIO=false` -- emitted `"summary": {"total": 2,
        "pass": 1, "fail": 0, "skip": 1}`, `"overall": {"pass": true}` and logged
        `all game-day checks passed`, having injected no fault and executed no
        scenario.

        2. The predicate is the defect and it is the same in all three scripts:
        `pass: (all(.[]; .status != "fail"))` reads the absence of a failure as
        the presence of a success, so a run in which everything was skipped is
        the strongest pass the report can express.

        3. `DRY_RUN` defaults to `true` in all three, and in dry run the
        mutations are not applied AND every verification records `skip`.

        4. NEITHER DRILL COULD HAVE INJECTED ANYTHING IF IT HAD TRIED: both gate
        on `jq -r '.isReadOnly // true'`, and jq's alternative operator treats a
        literal `false` as absent, so the expression returns `true` for EVERY
        input. `echo '{"isReadOnly":false}' | jq -r '.isReadOnly // true'` prints
        `true`. Both drills therefore always took the `Environment is read-only
        for feature flags` branch. Reproduced against a stub admin API: with the
        gate repaired the three flag PATCHes go out and `drill.simulate_outage`
        passes; as shipped, none do.

        5. Nothing schedules any of them -- no workflow, no package.json script,
        no cron -- so S10.12.e's ROTATION has no second run to rotate to.

        6. `libs/neith/gameday`'s `SCENARIO_LIBRARY` targets `api`, `db`, `web`,
        `cache`, `auth`, `payments`, `edge`: not one is a dependency id.
        Four of the ten classes (queue, search, blob, bridge) have no scenario;
        the objectives are prose graded by a facilitator typing five numbers into
        `scoreDrill`; and the library has NO CALLER anywhere in the repo.

        7. Neither mutating drill had a `trap`, so any failure between injection
        and the linear restore step near the end exited with the fault applied.

        8. All ten rollback rehearsal records under
        `docs/releases/v1/verification/rollback-plan/2026-05-15/` carry
        `rehearsed-at` and `observed-RTO` and no detection instant -- S10.11's
        finding, restated -- and there is no fencing anywhere in the estate: a
        repo-wide search for `split-brain`, `fence`, `fencing`, `STONITH` returns
        GDPR subject fences and geofencing.

        REPAIRED IN THIS CELL, WITH TESTS: the game day's verdict is now
        three-valued (`passed` / `failed` / `inconclusive`) and a skip is not a
        pass; a scenario whose own drill reported `inconclusive` is not counted
        as executed; a drill that ran and FAILED is reported as a failure rather
        than as a missing artifact (`jq -e '.overall.pass'` exits non-zero on a
        literal `false`); the read-only gate honours an explicit `false`; both
        mutating drills reverse their injection from an EXIT/INT/TERM trap, and
        the handler claims itself BEFORE restoring, because the restore issues
        its own requests and a second signal arriving during them re-entered it
        without bound (found by the test, in my own code); and the report now
        carries detection latency, decision latency and a recovery time measured
        from the fault when the operator supplies the instants, and refuses to
        call an injected run conclusive without a detection instant.

        KEY DECISIONS worth reusing. NO CAPABILITY CAN BE PROMISED A RECOVERY
        POINT TIGHTER THAN THE CAPTURE SKEW, whatever tier it is in, because
        S10.11 established that the honest achieved recovery point is the
        EARLIEST store capture -- so every objective in the register carries the
        same recovery point and the tiering lives in the recovery TIME, and a
        register with no measured skew is refused outright rather than graded.
        THE RESTORE ORDER IS THE REVERSE OF S10.11'S CAPTURE ORDER AND NOT THE
        SAME RULE: capture the referring store first so inserts leave orphans,
        restore the referenced store first so no row is exposed pointing at bytes
        that are not back. A GAME DAY KNOWS WHEN THE FAULT STARTED BECAUSE IT
        CAUSED IT, so its recovery time runs from injection, where S10.11's runs
        from detection -- detection is the earliest instant a drill can honestly
        start from when nobody observed the onset, and using it here would
        discount exactly the interval the drill exists to measure.

        TRAP, AGAIN IN MY OWN MODULE: the adversarial pass found four checks that
        could not fire on the input whose defect they exist to catch -- a plan
        section silent when a scenario had no plan at all, an observer with no
        declared route skipped rather than reported, a duplicate recovery
        objective silently overwriting, and a `recoveredTo` after the fault
        producing a negative recovery point nothing refused. All four are now
        faults with tests.

        A SECOND PASS OVER MY OWN EXPORTS found three more of the same shape:
        `GameDayReviewInput.objectiveRegister` was a field `reviewGameDay` never
        read and `GAME_DAY_OBJECTIVE_REGISTER` a re-export nothing consumed
        (both deleted -- a declared input nothing reads is the same claim as a
        check that never runs); `RUNBOOK_SECTIONS` was a closed list nothing
        iterated, now wired through `missingRunbookSections`, which asks which of
        S10.12.b's eleven sections a runbook is silent on rather than what is
        wrong with the ones it has. Writing that function exposed a real gap:
        `recoveryInfrastructureDependencyId: null` means "the recovery runs in
        place", and nothing refused running in place for a REGIONAL scenario,
        where in place is exactly what is gone. It does now, and a `dep:`-prefixed
        value the substrate does not contain is refused as well, because a typo
        in that field wears the shape of an answer. -->

  - [x] S10.12.b Document detection/declaration authority, communications,
        dependency assumptions, recovery infrastructure/secrets, restore order,
        fencing, validation, failover, and rollback/failback. Evidence:
        versioned runbooks.

        <!-- S10.12.b: shipped as nine new runbooks under `docs/runbooks/`
        (`v1-dr-signing-service-outage`, `v1-dr-relational-corruption`,
        `v1-dr-signing-key-compromise`, `v1-dr-queue-loss`,
        `v1-dr-model-provider-outage`, `v1-dr-search-index-loss`,
        `v1-dr-blob-store-loss`, `v1-dr-database-loss`,
        `v1-dr-bridge-host-loss`), `v1-disaster-recovery.md` raised to version 2,
        `libs/oshun/workbench-kit/tools/runbook-scan.mjs` (nx target
        `runbook-scan`, `pnpm run verify:operations:v1-runbooks`), a rewritten
        16-test `scripts/operations/v1-dr-runbook.test.mjs`, and a repair to
        `WORKBENCH_GAME_DAY_SCENARIOS` with two tests. All ten registered
        scenarios now have a runbook; `runbookCoverageFaults` returns [].

        THE VEIN: A CONTRACT THAT GRADES ONLY ITS OWN FIXTURES SAYS NOTHING
        ABOUT THE ESTATE. S10.12.a shipped `Runbook`, `runbookFaults`,
        `missingRunbookSections` and `runbookCoverageFaults`, and every caller
        outside the module was `game-day.spec.ts` building `runbookOf()` by
        hand. Twenty-one fault kinds, none of them falsifiable, because no real
        runbook was ever passed in. The rule: a check whose only input is a
        fixture is the same claim as a check that never runs.

        THE BINDING. Each runbook publishes its structured statement as a fenced
        JSON block after an `<!-- oshun:runbook -->` marker, IN the document
        rather than beside it, so the statement the checker grades and the
        statement the operator follows are one object. `runbook-scan.mjs` parses
        it, grades it with the shipped contract, and refuses an undeclared
        finding, a DECLARED finding that stopped firing, a scenario with no
        runbook, two runbooks for one scenario, a silent section, and a
        dependency id the prose never mentions. Eight negative controls mutate
        the REAL statements and the scan exits 2 rather than reporting if any
        goes undetected -- including a guard that refuses a control whose fault
        is already present unmutated, since that control proves nothing.

        THE REGISTER WAS WRONG, AND THE RUNBOOKS ARE HOW IT SHOWED.
        `gd:regional-loss` listed four targets. `docker/docker-compose.yml`
        declares postgres, redis, minio, elasticsearch, kafka, prometheus,
        grafana, loki, tempo, jaeger and every API in ONE file with no placement
        constraint, which is one host. The omission was not a smaller claim, it
        was a WRONG one in the direction that flatters the estate: without
        `dep:identity` the model said a total box loss leaves SIGN-IN WORKING,
        and without `dep:notification-channel` it said the messages announcing
        the outage travel over a sender that is inside it -- so
        `communication-over-the-failed-dependency` and
        `authority-reachable-only-over-the-failed-dependency` could not fire on
        the one scenario they exist for. Four targets added, each citing what
        puts it on the box. The repair immediately produced three findings the
        fixture had been hiding: an observer blinded by the fault it watches
        (the panel reports over `dep:telemetry-pipeline`, which is now inside
        the blast radius -- which is exactly why every core dependency in the
        S10.5 register carries an `outOfBandCheck` naming a prober outside the
        collector failure domain), and two recovery-time breaches, because a box
        loss now interrupts "sign in", whose promise is fifteen minutes.

        MEASURED, AND EACH IS A RUNBOOK SECTION SOMEBODY WOULD OTHERWISE HAVE
        WRITTEN WRONG:

        1. THE PAGE THAT WOULD ANNOUNCE A BOX LOSS IS EVALUATED ON THE BOX.
        `docs/operations/alert-routing.md` routes `Prometheus up == 0 for any
        production target` to PagerDuty `oshun-platform` at SEV-1, and Prometheus
        is a service in the same compose file. The highest-severity route in the
        estate is the one `gd:regional-loss` removes first. Its `CloudWatch RDS`
        rows describe an AWS deployment; V1's PostgreSQL runs in docker.

        2. THE C2PA VERIFIER READS THE PUBLIC KEY OUT OF THE MANIFEST IT IS
        VERIFYING. `verifyAudioC2paManifest`
        (`apps/oshun/bff/src/generation/audio-c2pa-signer.ts`) calls
        `createPublicKey(manifest.publicKeyPem)`. Nothing compares `signerKeyId`
        or `publicKeyPem` against an allowlist and `dep:trust-store` has no
        implementation, so ROTATION DOES NOT REVOKE: manifests signed under a
        disclosed key keep verifying, and the disclosed key mints new ones that
        verify. S10.7's vein, in the key scenario.

        3. THERE IS A STANDBY-PROMOTION SCRIPT AND NOTHING TO PROMOTE.
        `infra/yemaya/postgres-ha/failover.sh` runs `pg_ctl promote`; the V1
        compose declares one PostgreSQL service and no standby, replica or
        pooler, and nothing outside `infra/yemaya/postgres-ha/` references that
        directory. `check_standby` refuses, correctly. Two of its steps would
        mislead if it did run: `update_pgbouncer()` logs "PgBouncer update
        requires manual configuration or automation" and RETURNS 0, so
        `do_failover` proceeds as though the pooler were repointed; and
        `promote_standby` calls `read -p` on lag > 60s, so it blocks on stdin
        under automation. Its split-brain control is a human typing `yes`.

        4. THE V1 JOB QUEUE IS NOT A BROKER. `jobs-route.ts` holds jobs in an
        in-process map and persists the whole set through `createSnapshotSink`
        into the `admin_store_snapshot` table -- so the durable record is in
        `dep:primary-store`, and a queue loss is a reload rather than a replay.

        5. `docs/operations/on-call.md` EXISTS, and an earlier draft of these
        runbooks said it did not. It describes a weekly primary/secondary
        rotation and an escalation ladder, names NO person, and holds no rotation
        config while instructing the reader to "open a PR against the rotation
        config in this doc". None of the register's four `rota:` identifiers
        appears in it or in `alert-routing.md`. The claim in every runbook is
        therefore the narrow, checkable one -- the name is registered, the route
        from it to whoever carries the pager is not -- and the test asserts THAT,
        not the absence of on-call documentation.

        THE ABSENCE REGISTER is the unusual half. Eight claims each runbook makes
        about what this repository does NOT have (no reindex entry point, the
        search index in no backup, no queue drain or replay script, no Postgres
        standby, the pgbouncer no-op, the self-attesting verifier, the fail-closed
        signer, the unused PITR config) are predicates in
        `v1-dr-runbook.test.mjs`, and each FAILS the day its absence stops being
        true. A document describing a gap the estate has closed misleads in the
        direction of caution, which is still misleading.

        TRAPS IN MY OWN WORK, both found by testing rather than by reading. The
        prose-vs-statement cross-check compared the statement against the whole
        document -- which CONTAINS the statement -- so every dependency id
        trivially "appeared in the prose" and the check could never fail; fixed
        to compare against the text before the marker, whereupon it immediately
        caught that `v1-disaster-recovery.md` §6 named three stores in prose and
        ten in its restore order. And `RUNBOOKS[].prose` was a field nothing read
        until that fix, which is the S10.12.a trap repeated: a declared input
        nothing reads is a check that never runs. Also corrected: a claim that
        `providerEffects` is the reconciler's first collection (it is the second
        of seven), and a sentence asserting "zero matches for replica" in the
        compose file, which matches `KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR` --
        an answer about the wrong subject in the right shape, in a paragraph
        warning about exactly that.

        AND THE LARGEST TRAP WAS IN THE CLAIM I INHERITED AND REPEATED. Three
        runbooks said "there is no fencing anywhere in this estate", which is
        S10.12.a's finding 12 restated. It is a claim about the WHOLE REPOSITORY
        supported by a grep over THREE DIRECTORIES for FOUR WORDS
        (`split-brain|fence|fencing|STONITH` across `docs/`, `infra/`,
        `scripts/`), and the estate's one real fencing token is in neither the
        directories nor the vocabulary: `live_media_pipeline_job_attempts`
        `.lease_token`
        (`libs/shared/live-media/src/sql-media-pipeline-job-store.ts`) carries
        `AND lease_token = $4 AND status = 'leased'` in the `UPDATE WHERE` of
        every heartbeat, lease expiry and completion, so a worker holding a
        superseded lease updates ZERO ROWS. That is textbook lease fencing and it
        works. What is absent is the STORE-LEVEL equivalent -- nothing rejects
        the writes of a host that returns after its replacement was promoted --
        which is what `FencingStep` asks for. All four fencing sections now scope
        the absence and NAME the token, because "the estate has never done this"
        and "the estate has not done this here" are different sentences and only
        the second is true; the second also tells the reader which pattern to
        copy. Finding 12 in the module header is narrowed in place, and the
        token's continued existence is a predicate in the absence register, so
        the narrowed claim is refutable in both directions. Same shape as the
        Yemaya vein: A CLAIM ABOUT THE WHOLE REPOSITORY HAS TO BE CHECKED
        AGAINST THE WHOLE REPOSITORY. -->

        NOTE (S10.12.b, on the authority sections): every runbook names its
        authority from the S10.5 dependency register (`workbench-platform
        on-call`, `delivery pipeline on-call`, `production operations duty
        officer`) rather than declaring it UNASSIGNED as S10.12.a's runbook did.
        That is a change of position and it was deliberate: the register already
        carries an owner with a role, a name and a `rota:` contact for every
        dependency, so writing UNASSIGNED was under-reporting a fact the estate
        had. What it does NOT have is a resolution from those identifiers to a
        person, and that is stated in every runbook and machine-checked. Naming
        the registered rota is reading the register; inventing a person would
        have been the defect S10.12.a warned about.
  - [x] S10.12.c Document reconciliation/replay for jobs, leases, outbox/inbox/
        events, provider outcomes, pending uploads, gates, and releases.
        Evidence: state-specific procedures.

        <!-- S10.12.c: shipped as `docs/runbooks/v1-reconciliation-and-replay.md`
        (nine state-specific procedures, one per subject, publishing a
        machine-checkable statement after an `<!-- oshun:replay -->` marker),
        `scripts/operations/v1-replay-inventory.mjs` (the missing PRODUCER, run
        against the live database), `libs/oshun/workbench-kit/tools/replay-scan.mjs`
        (nx target `replay-scan`, `pnpm run verify:operations:v1-replay`, 10
        negative controls), a 23-test `scripts/operations/v1-replay-procedure.test.mjs`
        wired into `verify:operations:v1-disaster-recovery` (66 tests pass), and
        a repair to `replayFaults` with 4 new spec tests (129 pass).

        THE VEIN: A STATE CLASS IS NOT A TABLE NAME. Every claim that this
        estate lacks a state class was a search over `CREATE TABLE` names, and
        V1's durable state lives in ONE key-value table -- `admin_store_snapshot`,
        measured at 415 rows under 52 store keys -- where a second job store,
        thirty-eight crypto invoices carrying on-chain transaction ids and the
        editorial release streams all sit invisible to that search. The rule:
        for every state class, enumerate the STORE KEYS as well as the tables,
        and name the query that produces the inventory.

        AND THE SECOND HALF, WHICH IS WHY THE CELL NEEDED A PRODUCER RATHER THAN
        ONLY A DOCUMENT: `scripts/operations/v1-restore-reconcile.mjs` is named
        as `nextRequiredControl` by `box-loss-restore-drill.sh` and by every
        S10.12.b runbook, and NOTHING IN THIS REPOSITORY PRODUCES ITS INPUT. A
        grep for `oshun.v1-restore-reconciliation-input.v1` returns the
        validator, its own unit test, and the kit scan that builds a synthetic
        one. The operator following the runbook reaches the required control and
        the step has no first action. IT HAS NO PRODUCER FOR A REASON THAT
        DECIDES WHAT TO BUILD: its seven collections match on `externalRef` or
        `idempotencyKey`, and the V1 schema's 146 tables contain NO COLUMN of
        either name (`external_refs`, plural and JSONB, is a different thing on
        two tables); four of the seven have no table at all; and
        `artifact_detection_report`, the nearest name match for `artifacts`, is
        about visual artifacts in generated media. The input describes an estate
        this is not, and writing a producer for it would mean inventing the
        columns to fill it. So the producer that shipped reads the NINE state
        classes instead, and the document says plainly that it is not a drop-in
        for the reconciler.

        MEASURED AGAINST THE RUNNING DATABASE on 2026-08-14 (docker
        `oshun-dev-postgres`, `oshun_dev`), all reproduced by running the code:

        1. jobs: 63 records over two store keys, 24 IN FLIGHT -- `generation-jobs`
        8 (7 failed, 1 queued) and `metis-ingest-jobs` 55 (32 completed, 23
        `awaiting_source_content`). Only the first has a boot recovery.

        2. provider-outcomes: 38 crypto invoices -- 20 expired, 10 PENDING, 8
        confirmed and all 8 carrying a `txId`. This is the one subject in the
        estate whose external side genuinely outlives a restore of ours and can
        be queried independently, and nothing reconciles against it.

        3. leases: the estate's one real fence (`lease_token`, S10.12.b's
        finding) is correct, and NOTHING RUNS IT. `recoverExpiredLeases` is
        reachable only through `MediaPipelineWorker` -> `createLiveMediaPipelineRuntime`
        -> `apps/aphrodite/streaming`, which appears in no compose file, no
        infrastructure manifest and no workflow; `docker/docker-compose.yml`
        declares no such service; and the relation exists in no database on the
        box, because the compose declares an `aphrodite` database the running
        server does not have. The table is created by `libs/aphrodite/database`,
        not by V1's schema -- which the scan caught in my own statement.

        4. releases were recorded as absent by the S10.12.a groundwork on the
        strength of "no `CREATE TABLE` matching `release` anywhere". That is
        true of the table names and FALSE ABOUT THE ESTATE:
        `v1_metis_publication_package` carries `release_id` and `release_history`
        as COLUMNS, and `admin-editorial-release-streams` is a store key. The
        same error as S10.12.b's fencing claim, one level down. Corrected in
        place, and both halves are now predicates.

        5. jobs' boot recovery reconciles the JOB and not the two Redis ledgers
        the job wrote before dispatch: `activeQuotaGate.admit()` and
        `activeBudgetGate.reserve()` both run pre-dispatch and nothing releases
        either, so a job held for review after a restore keeps its quota slot
        and its estimated spend.

        THE CONTRACT REPAIR. `externallyVisible` was read on exactly ONE branch
        of `replayFaults` -- the `kind: 'none'` branch -- and ignored on the
        other four, so an effect the outside world saw and a purely internal one
        were graded identically the moment a procedure declared any
        reconciliation at all: a replay that verifies itself by counting our own
        rows passed every check. Added `externalAuthority` and
        `externally-visible-effect-verified-only-from-our-own-records`. Two of
        the nine subjects now DECLARE that fault rather than silence it (jobs has
        no provider-side receipt; releases have no readback from
        `dep:external-publish-target`), and the declaration fails in both
        directions.

        TRAPS IN MY OWN WORK, all found by the adversarial pass rather than by
        reading. (a) `inFlight: measured.reduce((sum, m) => sum + (m.inFlight ?? 0), 0)`
        -- THE `?? 0` VEIN, IN THE PRODUCER SHIPPED BY THE CELL THAT QUOTES IT:
        a subject whose stores carry no state field reported `inFlight: 0`,
        which says none was in flight where the truth is that the store cannot
        answer. Now null, with `inFlightMeasuredBy` naming which stores counted.
        (b) The producer answered "is this class absent" out of ITS OWN
        REGISTER; it now queries the live schema and reports
        `declared-absent-and-contradicted`. (c) `compareInventories` accepted a
        restored side with MORE records than the source as `reconciled` --
        duplication is the failure every idempotency key here exists to prevent,
        and it was the one outcome the comparison could not report. (d) In the
        scan, the dependency-id prose check sat AFTER the absent branch's
        `continue`, so for three of the nine subjects it could not fire; moved,
        with its own control. (e) Two bindings for one subject silently kept the
        last. (f) I wrote that `admin_store_snapshot.version` is "the one
        per-store marker the estate has, and S10.11 recorded it had none" --
        an overreach: S10.11's subject was which committed transactions are
        inside a dump, which a per-document counter cannot answer. Narrowed in
        place to what it does do (order two readings of one store key).

        AND THE VEIN CAUGHT THE DOCUMENT THAT STATES IT. §§3-4 shipped in the
        first commit saying "there is no outbox in V1" and "there is no
        consumer-side deduplication table", on the strength of no `CREATE TABLE`
        matching `outbox` or `inbox` -- the exact error §0 of the same document
        warns about, made two sections later. **V1 HAS BOTH AND THEY ARE
        COMPLETE.** `apps/oshun/bff/src/metis/academic-integrity-appeal-store.ts`
        (store key `metis-academic-records`) keeps `operations`, an append-only
        log carrying a `revision`, and derives `notificationIntents` from it by
        REPLAY, so a verdict transition and the notification intent it produces
        are one durable operation -- transactional publication, without a table
        named for it. `deliveredNotificationIds` is the acknowledgement set and
        `listPendingNotifications()` is literally the intents minus it, which is
        the inbox pattern. `reconcilePendingAcademicIntegrityNotifications()` is
        called from `server.ts` at boot, deliberately after both durable stores
        hydrate, and returns `{ attempted, pending }` with `pending` RECOMPUTED
        after the drain rather than assumed. The idempotency key is the stable
        `messageId`, and the module says so: "a crash after message commit but
        before outbox acknowledgement is retried as a harmless duplicate on
        restart". What it lacks against `study.domain_event_ack` is `attempts`
        and `dead_letter`, so a poison intent is retried at every boot forever
        with nothing counting the failures. Corrected in the document, the
        register, the statement and the tests, and the correction is left
        visible in §0 rather than rewritten away -- it is the strongest
        argument for the rule that this cell produced. Only `pending-uploads`
        remains declared absent, re-checked with a second vocabulary
        (`multipart`, `presigned`, `uploadId`) rather than the first.

        THE SAME SELF-REFERENCE BIT THREE TIMES IN ONE PREDICATE. "Nothing
        produces the reconciler's input" is a claim about the repository, and
        the claim's own artifacts kept entering its result set: first the TODOS
        note quoting the schema string, then the new producer's header comments,
        then the test's own search pattern. Narrowed to the ASSIGNMENT form and
        scoped away from markdown and from itself, because naming a thing is not
        producing it.

        AND A CHECK IN S10.12.b'S OWN TEST WAS WRONG, exposed by this document
        being written: `no other runbook still claims to cover the V1 recovery
        path` asserted an exclusion BY NAME while its comment stated a
        REACHABILITY requirement. Under the name rule the only way to satisfy it
        was to leave the reconciliation handoff undocumented, which is the
        condition that whole file exists to end. Repaired to assert what it
        meant -- a document naming the recovery path must be LINKED from a
        scenario runbook -- which is the stronger check, and
        `v1-disaster-recovery.md` §10 now links here. -->
  - [x] S10.12.d Prepare synthetic/production-like data, fault controls,
        observers, timers, safety boundaries, abort, evidence capture, and
        success criteria. Evidence: game-day plan.

        <!-- S10.12.d: shipped as `docs/runbooks/v1-game-day-plan.md` (ten
        plans, one per registered scenario, publishing a machine-checkable
        statement after an `<!-- oshun:game-day-plan -->` marker),
        `scripts/operations/v1-fault-control.mjs` (the missing INJECTOR: six
        controls, each applied/confirmed/reversed on this box, with a durable
        reversal ledger and a `--self-test` that stands up its own containers),
        `libs/oshun/workbench-kit/tools/plan-scan.mjs` (nx target `plan-scan`,
        `pnpm run verify:operations:v1-game-day-plan`, 13 negative + 4 positive
        controls), a 14-test `scripts/operations/v1-game-day-plan.test.mjs`
        wired into `verify:operations:v1-disaster-recovery` (86 tests pass), and
        contract repairs in `game-day.ts` with 15 new spec tests (144 pass).

        THE VEIN: A FAULT CONTROL IS A CLAIM THAT SOMETHING BROKE, AND EVERY
        FIELD IN THE CONTRACT IS A SENTENCE ABOUT THE COMMAND RATHER THAN A
        READING FROM THE ESTATE. The rule: for every fault control, name the
        observation that distinguishes the injected estate from a healthy one,
        and confirm the injection by TAKING it. S10.12.a's rule was about the
        scenario's pass; this is the same rule one level down, about the
        injection itself.

        MEASURED ON 2026-08-14 against throwaway containers on this box, all
        reproduced by `--self-test`:

        1. `docker exec <c> kill -9 1` EXITS 0 AND KILLS NOTHING. The kernel
        discards SIGKILL sent to a PID namespace's init from inside it, so the
        process the drill meant to crash keeps serving. `kill -TERM 1` DOES
        work, because postgres installs a handler. The strongest signal is the
        one that does nothing and it is the one an operator reaches for.

        2. THE OBVIOUS CONTROLS DO NOT PRODUCE THE SAME FAULT, and there are
        THREE client-visible shapes, not one: `docker stop` -> `could not
        translate host name` in 539ms; `docker network disconnect` -> the same
        in 359ms; `docker pause` -> `timeout expired` in 5407ms; `iptables
        -j DROP` -> the same in 5370ms; `iptables -j REJECT --reject-with
        tcp-reset` -> `Connection refused` in 316ms. A host that is really gone
        KEEPS ITS NAME, so `docker stop` rehearses a graceful deregistration
        and measures a recovery time with every caller's connect timeout
        removed from it. `name-unresolvable` matches no scenario in the
        register; the control is registered anyway, so a plan naming it is
        REFUSED rather than left undescribed.

        3. ZEROING ONE 8KiB HEAP PAGE TOOK 185 OF 2000 ROWS OUT OF
        `select count(*)` WITH NO ERROR, `max(id)` still 2000 and the
        connection healthy. It is silent because `data_checksums` is `off` --
        which is how the RUNNING estate is configured -- so no drill on this
        estate can be graded by "did the store stay up".

        4. A TRAP IS A PROMISE A PROCESS MAKES ABOUT ITS OWN DEATH. S10.12.a's
        own repair added EXIT/INT/TERM traps to the two mutating drills; a
        drill that paused a container and was then SIGKILLed left it `paused`
        with nothing to unpause it. SIGKILL, OOM and reboot are exactly the
        deaths a trap cannot honour, and `reversesOnAbort: true` is satisfied
        by a trap. The driver writes the reversal record BEFORE injecting, so
        `--reverse-all` drains it from an unrelated process.

        5. THE ESTATE DEPLOYS NO TELEMETRY PIPELINE AT ALL.
        `infra/hetzner/docker-compose.yml` -- the compose the deploy workflow
        and every `v1-dr-*` runbook name -- has ZERO mentions of Prometheus,
        Grafana, Loki, Tempo, Jaeger, OTLP or Sentry. The S10.12.a register put
        `dep:telemetry-pipeline` in the regional blast radius citing
        `docker/docker-compose.yml`, which is the DEVELOPMENT compose (44
        mentions) and describes a different estate. The conclusion survives and
        the reason is worse: AN OBSERVER INSIDE THE BLAST RADIUS AND AN
        OBSERVER THAT WAS NEVER DEPLOYED PRODUCE THE SAME SILENCE, and only the
        first has a name in the contract.

        6. All 14 docker healthchecks on the estate run against `localhost`
        INSIDE the container they check, so a paused container cannot run its
        own healthcheck and a box loss takes every one. Following the
        contract's refusal of a blinded observer is what produced the two
        observers the plans actually use.

        THE CONTRACT REPAIRS, each forced by a reading rather than argued.
        `FaultControl` gained `presentsAs` (checked against a
        `PRESENTATIONS_BY_IMPAIRMENT` table, so an `unavailable` scenario
        cannot be rehearsed with a control that deregisters a name),
        `confirmedBy` and `movesTheRecoveryPoint`; `Observer` gained `detects`;
        `GameDayPlan` gained `unrehearsableBecause`. THE RECOVERY-POINT
        REQUIREMENT IS NOW CONDITIONAL AND THE RECOVERY-TIME ONE IS NOT: as
        shipped, `no-criterion-compares-the-recovery-point` fired on every plan
        without an RPO criterion, but most controls cannot cause data loss at
        all (a postgres crash replays its log -- measured), so it was demanding
        a criterion that passes whatever the estate does, which is the
        `aspect-passed-with-no-evidence` shape the same module refuses two
        sections down. AND `observer-blinded-by-the-injected-fault` TREATED
        EVERY IMPAIRMENT AS IF IT WERE `unavailable`: a `corrupt` store ANSWERS,
        so the check refused the only observer a corruption drill can have.
        Blindness now follows `silencesObservers(impairment)`.

        THE THIRD OPTION. Five of the ten scenarios cannot be injected on this
        estate -- no signing service is deployed, the model provider needs the
        undeployed ISIS admin API, the search index has no process of its own
        (it IS the relational store), and the bridge host is somebody else's
        workstation. Without `unrehearsableBecause` the author must either
        invent a control (the defect this cell warns about) or omit the plan,
        and an omitted plan is `scenario-with-no-plan`, which reads as an
        oversight rather than a measured impossibility. The declaration fails in
        BOTH directions: `planFaults` refuses one that also ships a control, and
        the scan re-checks each against the deployed compose, with four positive
        controls proving those patterns CAN match -- because a declaration
        guarded by a regex that cannot fire is an absence nothing re-checks.

        TRAPS IN MY OWN WORK, found by the adversarial pass. (a) THE
        SELF-REFERENCE, exactly as S10.12.c recorded it one section up: "does
        the document state this target has no process" was
        `markdown.includes(target)`, and the statement block IS part of the
        markdown, so every target the statement named satisfied the question the
        statement raised. The check could not fail for any input; it now reads
        the prose BEFORE the marker, with a negative control using
        `dep:trust-store`. (b) MY OWN PROBE WAS BLIND TO THE FAULT THAT HIDES
        BEST: `presentationOf` settled `healthy` on the socket's `connect`
        event, and a container frozen with `docker pause` reported HEALTHY --
        the kernel completes the handshake from the listen backlog whether or
        not any process will ever read from it. Found by the driver's own
        self-test reporting `injected: false` on a demonstrably paused
        container. It now requires an ANSWER, and counts a close-without-bytes
        as one, because only a live process hangs up. (c) The destructive
        confirmation asked whether the census was ZERO, which is right only for
        a control that destroys everything -- against the corruption control it
        would have reported the estate healthy for the one fault a liveness
        check already misses. It is a before/after comparison now. (d)
        `--self-test` exited 0 when docker was absent and `--reverse-all` exited
        0 with a destructive fault still applied: S10.12.a's finding 2 rebuilt
        twice inside the driver written to end it. Now 3 and non-zero. -->

        FINDINGS FOR S10.12.g, from this cell: F-1 no telemetry pipeline is
        deployed (high); F-2 all 14 healthchecks are inside their own container
        (high); F-3 the S10.12.a register cites the development compose for the
        estate's blast radius (medium); F-4 `data_checksums` is off, so page
        corruption is silent (high); F-5 a trap-based reversal does not survive
        SIGKILL (medium); F-6 five of ten scenarios are unrehearsable here
        (low).
  - [x] S10.12.e Execute rotating scenarios end-to-end without undocumented
        access/dependency and measure actual detection, decision, recovery
        point, recovery time, and correctness. Evidence: timed report.

        <!-- S10.12.e: shipped as `scripts/operations/v1-game-day-execute.mjs`
        (five drills, each standing up a disposable estate, seeding it, backing
        it up, injecting, POLLING for detection, restoring and comparing a
        census), the timed report at
        `docs/releases/v1/verification/game-day/2026-08-14/execution-report.json`,
        `libs/oshun/workbench-kit/tools/execution-scan.mjs` (nx target
        `execution-scan`, `pnpm run verify:operations:v1-game-day-execution`),
        a `rotate` job on `.github/workflows/v1-disaster-recovery.yml` with a
        WEEKLY `schedule:` trigger, and a 15th test in
        `v1-game-day-plan.test.mjs`.

        THE PREMISE CHANGED, NOT THE CONCLUSION. The S10.12.a note recorded this
        cell as NOT LOCALLY ACTIONABLE because "executing rotating scenarios
        end-to-end needs a live ISIS API and a real host", and that was true of
        the estate as it stood: every fault control in the repository injected
        by `PATCH`ing feature flags against an admin API nobody deploys here.
        S10.12.d shipped an injector that needs nothing but docker, so the
        missing piece was never the host. RE-READ A "NOT ACTIONABLE" NOTE
        AGAINST THE ESTATE THAT EXISTS NOW, NOT THE ONE IT WAS WRITTEN ABOUT.

        MEASURED, all five quantities read from the clock at the step that
        produced them, none supplied and none derived from another:

        | scenario                   | detection | recovery time | recovery point |
        | -------------------------- | --------- | ------------- | -------------- |
        | `gd:database-loss`         | 1520ms    | 1669ms        | 0ms            |
        | `gd:queue-loss`            | 1521ms    | 1572ms        | 0ms            |
        | `gd:blob-store-loss`       | 1520ms    | 1571ms        | 0ms            |
        | `gd:regional-loss`         | 1553ms    | 1907ms        | 0ms            |
        | `gd:relational-corruption` | 385ms     | 631ms         | **1433ms**     |

        THE ONLY NON-ZERO RECOVERY POINT IS THE ONLY DRILL THAT CAN DESTROY
        DATA, which is S10.12.d's finding arriving as a measurement rather than
        an argument: the corruption drill writes 100 rows AFTER the backup and
        before the fault, and those rows are genuinely unrecoverable. The
        restore returning exactly 2000 is the correctness check -- MORE would
        mean the backup restored was not the one taken.

        WHAT IT DOES NOT CLAIM. `estate: "disposable"` is a field in every
        execution record, not a caveat in prose. The runs stand up their own
        postgres/redis/minio because the development box's are shared with other
        worktrees, which is the `production-like` dataset §1 of the plan
        prescribes and the only one it allows. This establishes the procedure
        works and the measurements are real; it does not establish the
        production estate's recovery time.

        AND THE ROTATION IS NOW A ROTATION. S10.12.a's finding 5 -- "nothing
        schedules any of them ... there is no rotation because there is no
        second run" -- was still true after the first pass, and one pass is not
        a rotation however many scenarios it covers. The `rotate` job runs
        weekly and, in order: grades the plans, proves every fault control still
        injects AND reverses, executes the rotation, grades the timed report,
        keeps the report as an artifact, and drains the reversal ledger under
        `if: always()` -- because a trap does not survive the death that needs
        one. Five of the ten scenario classes stay absent from the rotation and
        are DECLARED, read from the plan document rather than restated, and
        asserted in both directions: a class this estate can inject that was
        left out fails, and a class the plan calls unrehearsable that the
        rotation exercised fails too.

        THE SCAN REFUSES THREE THINGS THE CONTRACT CANNOT SEE: a detection
        latency of zero (the shape S10.11 found in all ten rollback records -- a
        number written rather than measured; verified to fire by stamping both
        instants together), a restore that finished before it started, and a
        report that does not say what estate it ran against.

        TRAP, IN THIS FILE'S OWN HEADER: the comment documenting what the scan
        grades contained the glob `game-day/*/execution-report.json`, and `*/`
        CLOSED THE BLOCK COMMENT -- the parser then read the prose as code and
        failed at EOF, 200 lines later, with an error pointing nowhere near it.
        `node --check` names the real line where a bundler's scanner does not. -->

  - [x] S10.12.f Test split-brain prevention, degraded operation, queued work,
        user communication, monitoring/alerts, backup of recovered primary, and
        failback. Evidence: acceptance matrix.

        <!-- S10.12.f: shipped as `scripts/operations/v1-acceptance-drill.mjs`
        (ONE incident on a disposable estate built in this runbook's own
        topology: a box carrying the relational store, the queue, the blob store
        and the surface; an off-box repository; a clean recovery host), the
        matrix at
        `docs/releases/v1/verification/game-day/2026-08-14/acceptance-matrix.json`,
        `libs/oshun/workbench-kit/tools/acceptance-scan.mjs` (nx target
        `acceptance-scan`, `pnpm run verify:operations:v1-acceptance-matrix`, 29
        negative controls), the operator-facing
        `docs/runbooks/v1-acceptance-matrix.md`, 11 tests in
        `scripts/operations/v1-acceptance-matrix.test.mjs`, and an `acceptance`
        job on `.github/workflows/v1-disaster-recovery.yml`.

        THE PREMISE CHANGED AGAIN, EXACTLY AS IT DID FOR (e). The S10.12.a note
        left this cell unchecked because "an acceptance matrix is filled by an
        executed drill; a matrix written without one is exactly the
        `aspect-passed-with-no-evidence` the module refuses" -- true of the
        estate as it stood, and untrue of the estate S10.12.d and S10.12.e left
        behind. The missing piece was never the aspects, it was a drill, and
        there is now an injector that needs nothing but docker and a harness
        that stands up an estate and measures what happened to it.

        A ROW THAT PASSES IS A SENTENCE SOMEBODY WILL TRUST ABOUT THE ESTATE,
        AND THAT DECIDED THE HARDEST VERDICT IN THE MATRIX. The drill deploys a
        real Prometheus, a real blackbox exporter and a real rule, and the alert
        fires 9.1s after the box dies -- an honest measurement of a pipeline
        this drill stood up. `infra/hetzner/docker-compose.yml` contains NO
        prometheus, grafana, loki, tempo, jaeger, OTLP collector, blackbox
        exporter or alertmanager, so on the deployed estate nothing would have
        fired at all. The rule applied throughout: AN ASPECT PASSES WHEN THE
        PROPERTY WAS MEASURED ON THE ESTATE'S OWN MECHANISM, fails when the
        drill measured that the mechanism is absent or broken, and the
        measurement that would flatter it goes in the evidence instead of in the
        verdict.

        MEASURED, one incident, seven facets, every outcome computed from a
        comparison rather than written down:

        | aspect                            | outcome    | what decided it                                                                  |
        | --------------------------------- | ---------- | -------------------------------------------------------------------------------- |
        | `split-brain-prevention`          | **failed** | the returning box and the estate that replaced it both wrote id 541, two values   |
        | `degraded-operation`              | passed     | correct answers from part of the dependency set on 3 of 5 surveys, 0 empty-success |
        | `queued-work`                     | **failed** | the verified checkpoint restores 0 of 25 entries into a redis that answers PONG   |
        | `user-communication`              | passed     | the off-box notice read back 200; the on-box control did not answer in 2000ms     |
        | `monitoring-and-alerts`           | **failed** | the rule fires in 9088ms and 0 services that could run it are deployed            |
        | `backup-of-the-recovered-primary` | passed     | 10027 bytes captured between the restore and the failback, restoring to 501 rows  |
        | `failback`                        | passed     | the rebuilt box came back with 501 rows, 22 drained jobs, 25 entries, 12 objects  |

        THE FINDING THIS CELL WOULD KEEP IF IT KEPT ONLY ONE. `offbox-backup.sh`
        forces a `BGSAVE` and refuses to finish without `redis/dump.rdb` -- and
        the restore never reads that file. `redis-server --appendonly yes`, as
        the deployed compose starts it, creates a new empty AOF on start and
        ignores the RDB entirely: measured at 0 of 25 entries, answering PONG.
        `box-loss-restore-drill.sh` writes `redisRestoreVerified: true` on the
        strength of exactly that PING, which is the mechanism behind S10.11's
        finding rather than a restatement of it. The archive as a whole does
        carry the queue (the AOF travels with the data directory, and the
        directory restore came back with all 25 entries and the two-entry
        pending list intact), so what is broken is the artifact the backup
        VERIFIES: the queue's recovery point is not the instant the backup
        script measured, and a restore that took the checkpoint at its word
        would come up empty and pass its own check.

        AND THE COST OF THE MISSING FENCE, IN ROWS. §8 of the runbook says
        `Fencing -- ABSENT`, and this is what that sentence buys: the box came
        back after the clean host was serving, both accepted an insert on one
        primary key, and the failback -- which restores the capture -- discarded
        the row the old box accepted. Two writable estates for one key space,
        and everything the returned box wrote is thrown away by the procedure
        that repairs it. The same drill shows the estate's ONE real fence
        working one layer down: a stale `lease_token` matches 0 rows while the
        current holder's write lands, so the hole is specifically the promotion
        path.

        FOUR INSTRUMENT BUGS, ALL OF THEM IN THE DIRECTION THAT FLATTERS THE
        ESTATE, all found by running the drill rather than reading it:
        1. psql prints the command tag as well as the RETURNING row, so
           comparing stdout to the key reported BOTH writes refused while both
           rows were demonstrably there. The first matrix therefore recorded
           `split-brain-prevention: passed`. WHAT A STORE ACCEPTED IS A QUESTION
           FOR THE STORE, and the check now reads the value back.
        2. the alert poll was an attempt count, not a deadline: 120 iterations
           in under two seconds, faster than the rule's own `for:` window could
           elapse, recording `alertFiringAtMs: null` -- which reads exactly like
           a pipeline that never fires. AN INSTRUMENT THAT STOPS WATCHING BEFORE
           THE THING IT WATCHES FOR CAN HAPPEN IS NOT MEASURING.
        3. XAUTOCLAIM's reply begins with the next CURSOR, which is also
           `digits-dash-digits`, and the drain applied it as a unit of work: 23
           rows for 22 entries, the extra one keyed by a position in a scan.
        4. the on-box negative control fell back to `127.0.0.1` when the dead
           container had no address -- a question about the wrong machine, and
           one a busy development box could answer yes to. The addresses are now
           read before the kill.

        A NEGATIVE CONTROL FOR THE OBSERVER ITSELF. After the failback the drill
        FREEZES the same store with `docker pause` for 9s: the TCP-connect probe
        still reports `probe_success=1` and no alert fires, while the probe that
        requires an ANSWER reports a timeout. The pipeline the estate does not
        deploy would be blind to a wedged dependency even once it exists, which
        is S10.12.d's finding about `presentationOf` arriving as a property of a
        real alert rule.

        THE DOCUMENT AND THE REPORT ARE TWO ARTIFACTS AND ARE GRADED AGAINST
        EACH OTHER. S10.12.b's prose cross-check compared a statement to the
        document CONTAINING it, so it could never fail; here
        `v1-acceptance-matrix.md` is written by hand and `acceptance-matrix.json`
        by a drill, and the scan fails if an outcome stated in one differs from
        the one measured in the other, if a finding is missing from the
        document, or if the document cites a path nobody can open. The scan also
        re-derives every outcome from the report's own measurements (a row
        flipped without touching a number fails), grades the post-recovery
        capture with S10.11's `recoveryPointFaults`, times this runbook's own
        sentence about capturing before failing back, and re-reads every claim
        the matrix makes about the repository -- so the day somebody deploys
        Prometheus, writes a fencing step or stops verifying a checkpoint
        nothing reads, the matrix is describing an estate that no longer exists
        and the scan says so.

        WHAT IT DOES NOT CLAIM. `estate: "disposable"` is a field in the report.
        It does not establish the production estate's recovery time; it does not
        speak for the other nine registered scenarios, which have plans and
        runbooks and no acceptance matrix; and every step was performed by a
        program that already knew the order.

        FINDINGS FOR S10.12.g, emitted by the drill with the measurement that
        produced each and in the severities `FollowUp` admits: F-f1 a returning
        box writes into the key space of the estate that replaced it (critical);
        F-f2 the backup verifies a redis checkpoint the restore never reads
        (critical); F-f3 no telemetry service is deployed, so no alert has a
        producer (major); F-f4 no degraded mode exists during a regional loss --
        every fallback is co-located with what it backs up (major); F-f5 work
        written after the capture is lost and still sits on the returned box
        (minor); F-f6 a connect-only probe is blind to a frozen dependency
        (minor). -->

  - [x] S10.12.g Assign severity/owner/deadline to every finding, link backlog,
        retest critical fixes, and update runbook. Evidence: closed follow-up
        register.

        <!-- S10.12.g: shipped as
        `docs/releases/v1/verification/game-day/2026-08-14/follow-up-register.json`
        (THIRTY findings, every one this section measured), the operator-facing
        `docs/runbooks/v1-follow-up-register.md`,
        `scripts/operations/v1-follow-up-retest.mjs` (four retests against real
        containers, `pnpm run run:operations:v1-follow-up-retest`),
        `libs/oshun/workbench-kit/tools/follow-up-scan.mjs` (nx target
        `follow-up-scan`, `pnpm run verify:operations:v1-follow-ups`, 27 negative
        controls), `infra/hetzner/backup/verify-restored-state.sh`, 10 tests in
        `scripts/operations/v1-follow-up-register.test.mjs`, and a `follow-ups`
        job on `.github/workflows/v1-disaster-recovery.yml`.

        A CLOSURE IS A CLAIM THAT THE FIX WORKS, AND THE ONLY EVIDENCE FOR IT IS
        RUNNING THE THING AGAIN. `followUpFaults` shipped in S10.12.a refusing
        `critical-follow-up-closed-without-a-retest`, and its only callers were
        spec fixtures -- the shape every other half of this section was found in.
        The register it now grades closes ten of thirty findings, and the four
        retests behind those closures were executed twice on this box with
        identical verdicts: `retest:F-f2` and `retest:key-recovery` and
        `retest:drill-verdict` came back `fixed`, and `retest:D-F4` came back
        `remedy-does-not-fix-it`.

        THE RETEST THAT REFUTED ITS OWN FINDING, WHICH IS WHY THE VOCABULARY HAS
        A WORD FOR IT. S10.12.d raised `D-F4` as "`data_checksums` is off, so
        page corruption is silent", and both the game-day plan §2.1 and the fault
        control's own recorded reading stated that causally. Four arms, same
        table, same injection:

        | page data | `data_checksums` | what `count(*)` returned          |
        | --------- | ---------------- | --------------------------------- |
        | random    | off              | `ERROR: invalid page in block 1`  |
        | random    | on               | the same ERROR, plus the checksum |
        | zeros     | off              | **1815 of 2000 rows, no error**   |
        | zeros     | on               | **1815 of 2000 rows, no error**   |

        AN ALL-ZERO PAGE IS A VALID UNUSED PAGE TO POSTGRESQL BY DESIGN, so the
        silence was never the missing checksum; a garbled page was already
        refused by the page-header check before any checksum was involved.
        `POSTGRES_INITDB_ARGS: --data-checksums` is set anyway -- it buys the
        diagnosis on the garbled arms, costs nothing at initdb, and applies only
        to clusters initialised after it, which means every clean-host restore
        and NOT the running production volume. `D-F4` stays OPEN with its
        statement corrected in all three places that carried the wrong one
        (§2.1, the §10 findings table, `v1-fault-control.mjs`), and the S10.12.d
        test that guarded the refuted claim was rewritten rather than deleted:
        the finding is live, so what it guards now is the corrected sentence and
        the detection path that does work.

        THE OWNER FIELD IS READ, NOT ASSIGNED, WHICH IS THE DEFECT S10.12.a
        WARNED THIS CELL ABOUT. The contract refuses a team noun; it cannot tell
        whether the person named exists. Every owner here must be a contact in
        the SHIPPED dependency register and, where the entry names a dependency,
        one of THAT dependency's owners. Deadlines are arithmetic over a policy
        stated once (critical 14 days, major 30, minor 90), so a row cannot be
        quietly given six more weeks. Closures must match a record in
        `follow-up-retests.json` whose verdict is `fixed` and whose FINISHING
        INSTANT is the register's `retestedAtMs` -- a closure backed by a run
        rather than by a merged diff. And every finding the acceptance matrix
        emitted must appear, under its own id or an alias (`F-f3` is absorbed by
        `D-F1`, `A-12` by `F-f1`, `B-3` by `F-f2`).

        TWO REPAIRS TO THE RESTORE PATH, BOTH RETESTED. `F-f2`: `offbox-backup.sh`
        forced a `BGSAVE` and refused to finish without `redis/dump.rdb`, and the
        restore never read that file -- `redis-server --appendonly yes` creates a
        fresh AOF on start and ignores the RDB, so a host handed the verified
        checkpoint comes up with 0 of 25 entries answering PONG, and the drill
        wrote `redisRestoreVerified: true` on the strength of that PING. The
        capture now records `redisKeyCount`, `redisPersistentKeyCount` and
        `minioObjectCount` where the restore can read them, and
        `verify-restored-state.sh` compares them: two numbers, because keys with
        a TTL may legitimately expire and keys without one cannot, so only the
        persistent count is a floor. `B-2`: `keyRecoveryVerified: true` was a
        literal with nothing above it, and the required set is now DERIVED from
        every `${VAR:?}` in the deployed compose -- the set the stack refuses to
        start without -- so a new required secret cannot be forgotten.

        THE SEAM NEITHER SIDE'S TESTS COVERED, found by reading the two suites
        against each other rather than either on its own. The producer's test
        asserted the counts were COMPUTED (its `jq` stub printed every
        `--argjson` as one flat object and threw the filter away); the consumer's
        test asserted its reads against a manifest written BY HAND in its own
        fixture. So `inventory.redisKeyCount` could have moved to top level, been
        renamed or been mistyped, every restore would have refused every
        snapshot, and both suites would have stayed green. The stub now EVALUATES
        the object construction, and a new test extracts the paths
        `box-loss-restore-drill.sh` reads out of the manifest and resolves each
        against the manifest `offbox-backup.sh` just wrote. Verified by breaking
        it on purpose: moving the field out of `inventory` fails both, naming the
        path and both scripts.

        THE DOCUMENT AND THE REGISTER ARE GRADED CELL BY CELL, not just on the
        word `closed`. S10.12.b's prose cross-check compared a statement to the
        document CONTAINING it and so could never fail; S10.12.f fixed that for
        one field. Here state, severity, owner, deadline AND the tally sentence
        under the table are each compared, because the reader acts on the
        severity and the date as much as on the state, and a row that agrees on
        one column and lies on another is not a smaller defect. Five of the 27
        negative controls mutate the DOCUMENT rather than the register -- without
        those, the comparison has a side nothing can move, which is the same
        defect one level up.

        WHAT WAS BROKEN IN THE IN-FLIGHT WORK AND IS FIXED. The scan CRASHED --
        `context.document` was read and never populated, so the central artifact
        of this cell did not run at all. The overdue branch was unreachable on a
        wall clock (every deadline is in the future), and an unreachable check
        reports exactly what an absent one does, so the clock is injectable
        (`OSHUN_FOLLOW_UP_NOW_MS`) and is exercised at 2027-01-01, where all
        twenty open findings report late. `retest:key-recovery`'s third arm
        hardcoded `met: true` and fed it into the verdict -- a sentence dressed
        as a measurement -- and now measures the artifact, because the pre-fix
        behaviour was the ABSENCE of a check and running nothing against a bad
        input accepts it by definition.

        A SCAN WHOSE INPUTS ARE MISSING MUST NOT LOOK LIKE A CLEAN ONE, which is
        `all(.[]; .status != "fail")` over an empty array one level up. The scan
        refuses an absent register, an absent retest report and an absent
        published document; the test file exercises all three against a synthetic
        estate built at the same depth, and the complement -- that the real one
        grades clean -- so the refusals are not unconditional. The same register
        copied into an estate where its backlog links do not resolve exits 2, not
        1: with the unmutated register already carrying a fault a control exists
        to provoke, that control proves nothing, and the scan refuses to report
        from checks it can no longer vouch for.

        THE SCAN FAILS IN TWO WAYS AND THEY MEAN DIFFERENT THINGS. A MALFORMED
        register is a defect in the artifacts; an OVERDUE one is well-formed and
        the work is late, which is what a deadline is for. Both exit non-zero and
        the output separates them, because an operator sent looking for a broken
        document when a critical finding is simply past its date has been
        misdirected by the instrument.

        WHAT IT DOES NOT CLAIM. Twenty findings are open, and the register
        records who is accountable and by when -- not that anybody is working on
        them. `docs/operations/on-call.md` names no person and holds no rotation
        config, so the rota identifiers are real register entries that resolve to
        nobody carrying a pager; the owner column says which rota is accountable,
        not who will answer, and that gap is itself `A-11`'s neighbour. Nobody
        has agreed to the deadline policy (`agreedBy: null`); the windows are the
        ones this cell proposes, enforced arithmetically so that changing one is
        a commit somebody reviews. And the register covers what the game-day
        programme found -- not the estate's whole defect surface. -->

        NOTE (S10.12.a, on b-g): b through g are LEFT UNCHECKED DELIBERATELY, and
        the contract each of them needs is already shipped in
        `libs/oshun/workbench-kit/src/game-day.ts` -- what is missing is the
        artifact, not the definition. `Runbook` + `runbookFaults` name the eleven
        sections S10.12.b lists and enforce them (authority is a named person or
        rota, communications must not travel over the failed dependency, the
        recovery must not assume it, secrets must be recoverable out of band, the
        restore order must not contradict the reference graph, a promotion needs
        a fencing token applied BEFORE it, and there must be a failback);
        `ReplayProcedure` + `replayFaults` name S10.12.c's nine subjects and
        refuse a replay window bounded by a wall clock instead of by the recovery
        point; `GameDayPlan` + `planFaults` name S10.12.d's fault controls,
        observers, timers, abort, evidence and success criteria; `AspectResult` +
        `acceptanceFaults` name S10.12.f's seven aspects; `FollowUp` +
        `followUpFaults` name S10.12.g's fields and refuse a critical finding
        closed without a retest.

        Each remains unchecked for its own reason. (b) asks for VERSIONED
        RUNBOOKS. CORRECTION, measured after the S10.12.a note was first written:
        the estate has 27 runbooks under `docs/runbooks/`, several of them
        substantive and dated (`database-backups.md`, `object-storage-backups.md`,
        `provider-failover.md`, `shell-outage.md`, each with an `Owner:` and a
        `Last reviewed:` line). The gap is narrower and sharper than "there are
        none": **NOT ONE RUNBOOK IN THE REPO MENTIONS THE V1 RECOVERY PATH.** A
        grep across `docs/runbooks/`, `docs/reference/runbooks/` and
        `V3/runbooks/` for `box-loss-restore-drill`, `v1-restore-reconcile`,
        `offbox-backup` or `restic` returns NOTHING. `database-backups.md` -- the
        runbook an operator reaches for -- documents `scripts/db-backup.sh`, an
        RDS/S3/cron cadence and a `db-restore.sh` whole-database restore, and
        never mentions that a V1 restore is not finished when it finishes:
        `box-loss-restore-drill.sh` ends by writing
        `status: "restore-pass-reconciliation-required"`,
        `safeToResumeAutonomy: false` and
        `nextRequiredControl: "scripts/operations/v1-restore-reconcile.mjs"`, and
        no runbook carried that step. Its `Owner:` is `platform team`, which the
        module refuses as a team noun.

        PARTIALLY CLOSED IN THE SAME PASS: `docs/runbooks/v1-disaster-recovery.md`
        now exists -- all eleven sections, every step citing a script that is
        really there, machine-checked by
        `scripts/operations/v1-dr-runbook.test.mjs` (10 tests, in
        `verify:operations:v1-disaster-recovery`). The checks are the ones a
        reader cannot perform: every cited repository path exists, every `§N`
        cross-reference resolves, no authority line names a collective, the
        restore order is stated in the REFERENCE direction (blob before
        relational -- the reverse of S10.11's capture order), the reconciler is
        named along with the proof fields that demand it, and the two sections
        that cannot be written from the repository are DECLARED (`Owner:
        UNASSIGNED`, `## 8. Fencing — ABSENT`) rather than omitted, because an
        absent section and a satisfied one look identical in a document. It also
        states the gaps rather than papering over them: `keyRecoveryVerified` is
        unproven, and Redis/MinIO/file-state/configuration are asserted from
        steps an EMPTY store also passes.

        S10.12.b STAYS UNCHECKED, and the criterion for marking it is now
        precise: ONE of the ten registered scenarios has a runbook.
        `runbookCoverageFaults(WORKBENCH_GAME_DAY_SCENARIOS, [...])` reports the
        other nine, and each needs the same treatment -- written from scripts
        that exist, or declared absent where they do not. (c) the replay procedures: the outbox, lease and
        provider-outcome reconciliations were not read in this cell and must not
        be invented. **(c) IS NOW DONE** -- they were read, against the running
        database as well as the source, and the answer to two of the three was
        that the store is not where a table-name search looks: the outbox is
        genuinely absent, the lease fence is real and its reclaim loop is
        deployed nowhere, and the provider outcomes are thirty-eight crypto
        invoices under a snapshot store key. See the S10.12.c note above. (d) a game-day plan needs the fault controls that actually
        exist on this estate, which is a Hetzner box plus docker compose, and
        naming a control that has not been tried is a plan for a rehearsal nobody
        can run. (e) executing rotating scenarios end-to-end needs a live ISIS
        API and a real host: NOT LOCALLY ACTIONABLE, and the harness now refuses
        to call an un-injected run a pass rather than pretending otherwise. (f)
        an acceptance matrix is filled by an executed drill; a matrix written
        without one is exactly the `aspect-passed-with-no-evidence` the module
        refuses. **(f) IS NOW DONE** -- the drill exists, and the matrix it
        filled fails three of the seven aspects with a measurement behind each.
        See the S10.12.f note above. (g) a follow-up register needs owners and backlog references,
        and inventing a name to satisfy a field is the same defect as inventing a
        result.

        The thirteen findings this cell measured ARE the raw material for (g);
        they are enumerated in the S10.12.a note above and in the module header,
        with the four that were repaired marked as such.
- [x] S10.13 Add data-subject export and deletion orchestration hooks. Evidence:
      protected retention exceptions and deletion receipts are tested.

      <!-- S10.13 PARENT: all eight children are marked, and the parent's own
      evidence clause is the one S10.13.f and S10.13.g deliver — the retention
      exception is tested (`subject-retention-exception.spec.ts`, 34 tests) and
      the deletion receipt is tested (`subject-receipt.spec.ts`, 30 tests).

      WHAT BACKS THIS MARK. Eight kit entry points, 279 tests and eight scans
      carrying 115 negative controls between them, all passing against the
      source:

      | cell | module                     | tests | controls |
      | ---- | -------------------------- | ----- | -------- |
      | a    | subject-requests           | 25    | 9        |
      | b    | subject-data-map           | 17    | 14       |
      | c    | subject-orchestration      | 68    | 21       |
      | d    | subject-export             | 42    | 22       |
      | e    | subject-erasure            | 40    | 15       |
      | f    | subject-retention-exception| 34    | 12       |
      | g    | subject-receipt            | 30    | 11       |
      | h    | subject-lifecycle-matrix   | 23    | 11       |

      a and b were shipped in an earlier session; both modules were read in full
      in the session that marked this parent, and both scans pass.

      THE THROUGH-LINE, which only appears once the eight are read together: THIS
      ESTATE CONTAINS ITS OWN ANSWER AND APPLIES IT ON ONE PATH ONLY. The tip
      ledger pseudonymises a counterparty on erasure and hands them over whole on
      export. 23 stores fence on a digest and 35 on the raw account id. The
      deletion path signs every attestation with Ed25519 and the export's digest
      is self-computed. The legal hold is category-blind and covers all 28 purge
      partitions; the retention exception is category-scoped into a vocabulary
      that reaches 12 of them. The one orchestration with a plan executes
      nothing; the two that execute have no plan. In every pair the estate has
      already built the right thing once.

      AND THE ONE FACT THAT RUNS THROUGH ALL EIGHT: the machinery is real —
      signed attestations, fail-closed omission, honest `not_found`, a fence in
      58 stores, an object-locked archive — and what is missing is almost always
      a SENTENCE rather than a mechanism: what the subject was told, why the data
      is held, which tenant a row belongs to, what must happen first. -->

  - [x] S10.13.a Define verified request identity/authority, subject
        identifiers, tenant/scope, jurisdiction/purpose, request type, deadline,
        state, communication, and audit. Evidence: request contract.

        <!-- S10.13.a: shipped as
        `libs/oshun/workbench-kit/src/subject-requests.ts` (`SubjectRequest`,
        `subjectRequestFaults` with 29 fault kinds, `ESTATE_REQUEST_SHAPES`,
        `estateCoverageFaults`), 25 tests in `subject-requests.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-request-scan.mjs` (nx target
        `subject-request-scan`, `pnpm run verify:operations:v1-subject-requests`,
        9 negative controls), and the three registrations a kit entry point needs
        (package.json exports, `tsconfig.base.json` alias, `KIT_OWNED_ENTRY_POINTS`).

        AN OPERATOR'S AUTHORIZATION IS NOT THE SUBJECT'S REQUEST. They are
        different claims, made by different people, about different things.
        `POST /v1/admin/privacy/dsar/erase` gates on
        `admin:workspace:privacy` -- authorization to work the machinery -- and
        NOTHING on the type it accepts records that the subject asked or that
        anybody checked who they are. Both directions of that are dangerous in a
        way ordinary work is not: an erasure performed for the wrong person
        destroys a real account, and an access export performed for the wrong
        person IS the breach it was meant to prevent.

        THREE TYPES MODEL A SUBJECT REQUEST IN THIS REPOSITORY, THEY DISAGREE,
        AND THE RICHEST OF THEM IS THE ONE NOTHING CALLS. Measured, not asserted
        -- the scan reads each interface out of its source and re-derives the
        caller claim from a repository-wide search:

        | type                              | elements | reached by                          |
        | --------------------------------- | -------- | ----------------------------------- |
        | `privacy.DsarRequest`             | 6 of 12  | **nothing outside its own spec**    |
        | `privacy.DeletionRequest`         | 5 of 12  | the body of the erasure route       |
        | `persistence.DsarDeletionRequest` | 3 of 12  | the route, and the cascade that erases |

        FIVE OF THE TWELVE ELEMENTS ARE REACHABLE AT ALL (subject identifiers,
        tenant, scope, deadline, state). FOUR ARE CARRIED BY NO REQUEST TYPE IN
        THE REPOSITORY: authority, purpose, communication, audit. Three more --
        verified identity, jurisdiction, request type -- exist only on the type
        nothing reaches, which means the running estate cannot ask about them.

        THE COMPOUND ELEMENTS ARE SPLIT, AND THAT IS WHERE TWO OF THE FINDINGS
        WERE HIDING. S10.13.a names nine items and four are compounds. Graded as
        compounds, `DsarRequest` would score "jurisdiction/purpose" on a
        jurisdiction field alone and "identity/authority" on an identity check
        alone -- and it is precisely the missing halves that matter.
        `requestingActorKind` ADMITS `'authorized-representative'` and the type
        carries no evidence of that representative's authority, so
        `checkEligibility` treats a verified representative exactly like a
        verified subject. VERIFYING A REPRESENTATIVE IS NOT VERIFYING THEIR
        AUTHORITY: a solicitor's own identity checked to the highest strength
        available establishes nothing about their standing to act for this
        subject, and those are two evidence chains of which only the second
        decides whether the request is honoured.

        A DEADLINE IS AN OBLIGATION AND AN ETA IS A FORECAST. Neither reachable
        type carries a date the operator is held to: `executionEtaUnixSeconds` is
        a forecast of when work will finish and `hardDeleteAtUnixSeconds` is when
        the erasure executes. The contract anchors the deadline to RECEIPT
        specifically, because anchoring it to a step the operator controls --
        identity verification, say -- hands an unbounded extension to whoever is
        behind: don't take the step, and the clock never starts. An extension is
        a recorded act with a reason and a grantor, and a due date beyond the
        furthest recorded one is an extension nobody granted.

        THE ONLY SUBJECT-COMMUNICATION FIELD IN THE WHOLE PRIVACY LIBRARY IS ON
        THE OPERATOR-ACCESS TYPE. `OperatorAccessRequest.notificationToSubjectByUnixSeconds`
        and `OperatorAccessRequest.auditId` are the estate's only instances of
        two of the nine elements -- so the path where staff read a subject's data
        owes them a sentence and carries an audit anchor, and the path where the
        subject asserts a right does neither. Erasure is also the one operation
        that removes the rows which would show what was done, so a request with
        no audit id leaves a destructive act recorded only by its own absence.

        NINE TYPES IN THIS REPOSITORY ARE CALLED `DeletionRequest`, AND
        `DsarRequest` IS DECLARED TWICE. Demeter's portability lifecycle, two
        Iris privacy surfaces, two Aphrodite sovereignty models, Aphrodite's GDPR
        types, Kuanyin's autonomy analytics and Yemaya's account-deletion service
        all declare their own. THE SCAN FOUND THIS BY CONTRADICTING ME: my first
        caller check was a name search, it reported callers for a type that has
        none, and the negative controls refused to report rather than pass. A
        file declaring its own type of that name is now a HOMONYM rather than a
        caller, every one is recorded, and an undeclared one fails -- because
        "nothing calls this" is a claim about the whole repository and a name
        search answers it about all nine. The same pass caught the scan counting
        its OWN register file as a caller.

        A DECLARED ABSENCE IS A TWO-WAY CLAIM, which is S10.12.f's lesson applied
        to a type instead of a runbook. Every element the register says a type
        LACKS is searched for in that type's source by an indicator pattern, so
        the day somebody adds `auditId` to `DeletionRequest` the scan fails and
        the register has to be updated. Without that half, a gap statement
        outlives the fix and the estate looks worse than it is -- the mirror of
        the failure mode where it looks better.

        WHAT IT DOES NOT CLAIM. This defines the contract and measures the gap;
        it does not close it. No route was changed, no type was migrated, and the
        five reachable elements are still five. `SubjectRequest` has no producer
        yet -- which is the shape this section keeps finding, and is why the cell
        ships the estate register and the scan rather than the type alone. The
        remedy belongs to S10.13.b onward: the data map, the orchestration plan,
        and the receipt that reconciles them. -->
  - [x] S10.13.b Register domain/store/provider/processor data locations,
        identity mapping, export format, deletion/anonymization action,
        retention/hold exceptions, owner, and verification. Evidence: data map.

        <!-- S10.13.b: shipped as
        `libs/oshun/workbench-kit/src/subject-data-map.ts` (`DataLocation`,
        `dataMapFaults` with 20 fault kinds, `dataMapCoverageFaults`, and
        `ESTATE_DATA_MAP` -- 24 locations), 17 tests in
        `subject-data-map.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-data-map-scan.mjs` (nx target
        `subject-data-map-scan`,
        `pnpm run verify:operations:v1-subject-data-map`, 14 negative controls),
        and the three registrations a kit entry point needs.

        A STORE THAT CANNOT FIND THE SUBJECT CANNOT DELETE THEM, AND SAYING IT
        DELETES THEM IS THE WORST ROW IN THE MAP. A shared catalog with no
        per-subject column, a store whose rows are enumerated by whoever calls
        the eraser, an opaque volume: each can be given a deletion action in a
        spreadsheet and none can honour it. The identity mapping is therefore the
        FIRST column, and an unselectable mapping with a deletion action is
        refused twice -- once against the row, and once against the factory
        `buildBffDeletionErasers` really uses.

        FOUR VOCABULARIES DESCRIBE THE SAME SUBJECT'S DATA AND NONE OF THEM IS A
        MAP. Measured:

        | list                       | size | what it says                                       |
        | -------------------------- | ---- | -------------------------------------------------- |
        | `OSHUN_ARTIFACT_SCOPES`    | 28   | what an artifact IS -- never where it lives         |
        | `DELETION_SERVICE_IDS`     | 16   | what the fan-out erases; all 16 ARE artifact types  |
        | `EXPORT_SCOPES`            | 11   | **nine are not artifact type names**                |
        | backup `stateClasses`      | 6    | the only list about WHERE, and it is about volumes  |

        AGAINST THE SEVEN ATTRIBUTES THIS CELL ASKS FOR, the artifact scopes
        already carry export format and deletion action, carry retention only as
        a post-deletion window with no hold exceptions, and carry data LOCATION,
        IDENTITY MAPPING, OWNER and VERIFICATION not at all. Those four are what
        makes a map a map, and they are what this register adds.

        ONE PRECISE GAP THE FOUR VOCABULARIES HID. Twelve artifact types have no
        deletion service. Eleven are honestly `redaction_only` or
        `not_deletable`. The twelfth is **`persona`** -- `deletable: true`,
        `soft_delete_then_purge`, 90 days -- and no service erases it under that
        name. The BFF's `personalization_vector` eraser deletes an
        `activePersona` partition, so the data is probably covered under another
        service's name, AND NOTHING RECORDS THAT CORRESPONDENCE. A comparison of
        the two lists cannot see it, which is precisely the question a data map
        exists to answer.

        TWO CAPTURED VOLUMES ARE NAMED BY NO ERASER. `content-service-runs` and
        `computer-use-snapshots` are in the off-box snapshot and mentioned
        nowhere in `data-deletion`, `deletion-fanout` or `privacy`. Whether a
        subject is in them is UNDETERMINED, and the map records that as `opaque`
        rather than leaving them out -- a screen capture of a session is exactly
        what a subject means by "everything you have about me".

        THE ARCHIVE IS A LOCATION, AND IT IS THE ONE THAT UNDOES EVERY ERASURE
        ABOVE. 30 days, `provider-object-lock`, so a snapshot cannot be edited to
        remove a subject even if somebody wanted to; expiry is the only
        mechanism, and a clean-host restore replays every state class wholesale.
        **THIRTY DAYS IS THE ANSWER TO "HOW LONG AFTER AN ERASURE CAN THIS
        SUBJECT COME BACK", and until this row nothing in the repository said
        so.** The scan binds that number to `RETENTION_DAYS` in
        `offbox-backup.sh`, so changing the backup makes the map wrong out loud.

        WHAT THE DELETION SYSTEM GETS RIGHT, because a cell that only reports
        gaps is not measuring. Sixteen services with real erasers wired at the
        BFF boundary, Ed25519 attestations per service, fail-closed omission (an
        unconfigured category is left out and a deletion requesting it fails
        rather than silently passing), bytes purged before the metadata that
        maps to them, sessions erased before the profile so refresh credentials
        cannot outlive the account, and `voice_profile` honestly reporting
        `not_found` for a shared catalog instead of claiming an erasure it
        cannot perform. That last one is the opposite of a stub, and the map
        records it as `not-subject-keyed` + `none`.

        THE SCAN FAILS ON DRIFT AND REPORTS COVERAGE. Malformation and drift
        from source are failures: a service the map does not place, an eraser
        factory it describes wrongly, an archive window that no longer matches
        the backup, an unregistered dependency or artifact type. The ten coverage
        findings are REPORTED and do not fail, because they are the estate's gaps
        rather than the map's, and a map that correctly records a gap is doing
        its job -- the same split S10.12.f's acceptance matrix ships under. The
        state classes are read out of a jq filter inside a shell script, which no
        TypeScript import can reach and which is exactly where a list nobody
        maintains would hide.

        WHAT IT DOES NOT CLAIM. It does not establish that a subject IS or is not
        in the two opaque volumes -- it records that nobody has established it.
        It does not close the export vocabulary mismatch, and `buildExportBundle`
        still has no production caller while the real exporter at
        `apps/oshun/bff/src/data-export/` runs on artifact types instead. Owners
        are rotas from the S10.5 register, which S10.12.g already recorded
        resolve to nobody carrying a pager. -->
  - [x] S10.13.c Plan authorized search/export/delete with impact, dependencies,
        derived/backup/log/audit/provenance/search/cache/provider data,
        exclusions, and approvals. Evidence: orchestration plan tests.

        <!-- S10.13.c: shipped as
        `libs/oshun/workbench-kit/src/subject-orchestration.ts`
        (`OrchestrationPlan`, `orchestrationPlanFaults` with 44 fault kinds,
        `planCoverageFaults`, `planExecutionOrder`, `canonicalPlanStatement`,
        `ESTATE_ORCHESTRATIONS` and `buildEstateDeletionPlan`), 68 tests in
        `subject-orchestration.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-orchestration-scan.mjs` (nx
        target `subject-orchestration-scan`,
        `pnpm run verify:operations:v1-subject-orchestration`, 21 negative
        controls), and the registrations a kit entry point needs.

        A DEPENDENCY IS A CLAIM ABOUT WHAT MUST HAPPEN FIRST, AND A POSITION IN A
        LIST IS NOT ONE. This estate states exactly one ordering requirement, it
        is a safety property, and it is carried by an array index:
        `buildBffDeletionErasers` pushes `session` before `profile` and says why
        -- "so active refresh credentials cannot outlive account removal DURING
        THE SEQUENTIAL FANOUT". The sequential `executeDeletionFanout` that would
        honour that array has **no caller outside `*.test.ts`**. The production
        path is `buildDeletionRunner`, which registers every eraser as an
        independent bus consumer and dispatches ONE
        `subject.deletion.requested` event whose replies are collected in arrival
        order. The comment names the mechanism that would deliver the guarantee
        and the mechanism is not the one that runs.

        FOUR ORCHESTRATIONS, AND THE ONLY ONE WITH A PLAN EXECUTES NOTHING.
        Measured, each read out of the file that runs it:

        | orchestration                 | named steps | executor | outcomes  |
        | ----------------------------- | ----------- | -------- | --------- |
        | admin deletion workflow (010) | **8**       | **none** | typed     |
        | customer deletion fan-out     | 0           | real     | measured  |
        | admin DSAR erasure cascade    | 0           | real     | measured  |
        | customer data export          | 20          | real     | measured  |

        `ADMIN_DELETION_JOB_STAGES` is `discovery, redaction, content_removal,
        backup_purge, analytics_scrub, external_integrations, verification,
        closed` -- almost exactly the class list this cell asks for -- and the
        only writers of a stage transition are an admin route, its state store
        and an operator's React panel. `recordCount` and `failedCount` arrive in
        the request body, so a completed stage is a claim. `finalize_job` refuses
        only while a stage is `running` or `pending`, and a stage exists only
        once `start_stage` created it, so **seven of the eight can be absent and
        the job finalizes `succeeded`**: an unstarted stage and an inapplicable
        one are the same record.

        `backup_purge` IS A STAGE THE ESTATE'S OWN BACKUP MAKES IMPOSSIBLE.
        `offbox-backup.sh` accepts only `provider-object-lock`,
        `append-only-rest-server` and `provider-retention-lock`, under all of
        which a snapshot cannot be edited to remove a subject -- S10.13.b's
        thirty days, from the other side. An operator can still report the stage
        succeeded with a number they typed.

        THE HOLD IS CHECKED AFTER THE ERASURE, AND IT IS NOT GIVEN ANY HOLDS.
        `executeDsarErasureForSubject` runs `executeDsarDeletionCascade` -- real
        `deleteMany`s -- and only then calls `advanceToHardDelete`, whose result
        type can return `blocked-by-hold` about data that is already gone. And
        the gate is `input.holds !== undefined` while the runtime calls it with
        `{ request, nowUnixSeconds, shred }`, so an ABSENT ARGUMENT MEANS "NO
        HOLDS EXIST" and the check is skipped outright. Holds are evaluated once,
        in `createDeletionRequest`, which stamps `blocked-by-hold` or `pending`
        at filing -- so a hold placed during the soft-delete window, which is
        when holds are placed, is never consulted by the acts it exists to stop.
        The one orchestration that DOES check a hold before every stage is the
        one that erases nothing.

        A FORMAT IS A CHOICE ABOUT RENDERING AND SILENTLY A CHOICE ABOUT SCOPE.
        `resolveArtifactTypes` drops any artifact type whose `exportFormats`
        exclude the requested format: of the 20 exportable customer-visible
        types a subject receives **20 as json, 7 as csv, 5 as zip and 3 as
        markdown** -- and a dropped type gets NO section in the bundle, not the
        `available: false` with a reason the assembler produces for a type it
        cannot collect. The artifact is identical to one where those scopes held
        nothing. Two types are exportable and unreachable -- `persona` and
        `provenance_bundle`, both `exportable: true, customerVisible: false` --
        and `bundle-builder.ts` ships a real `persona` collector no customer
        request can reach.

        `redaction_only` IS A MODE NOTHING PERFORMS. Six artifact types declare
        it, the contract defines it as "retains the artifact with PII scrubbed",
        `resolveArtifacts` drops those types on `isArtifactDeletable` instead of
        scrubbing them, and NOTHING in the repository branches on
        `deletionMode ===` at all. The `redaction` stage is the only place the
        word appears in an executable position, in the orchestration with no
        executor.

        THE PLAN THE ESTATE RUNS, TRANSCRIBED AND GRADED BY THE SHIPPED
        CONTRACT. `buildEstateDeletionPlan` writes down what an `account_full`
        deletion really is -- fifteen erasures, one verification, `voice_profile`
        excluded because the builder constructs it through
        `createNoSubjectDataEraser` -- and the contract reports 18 findings on
        it. **No step states an impact**, and the estate has none to state:
        `rowsRemoved` exists only in the attestation a service signs AFTER
        erasing, and no search precedes the run. Against S10.13.b's 24-location
        map it reaches or excludes 16 and leaves **eight in silence**: the
        PostgreSQL cluster, Redis, MinIO, the two opaque volumes, the
        configuration snapshot, the 30-day archive that restores all of them, and
        `loc:persona`. **Two of the nine data classes are accounted for**;
        backup, log, audit, provenance, search, cache and provider are questions
        the plan cannot be asked. Twelve artifact types are dropped before a plan
        is built with no record that they were dropped.

        AND NOTHING IN THIS ESTATE SEARCHES. The cascade takes the records the
        caller enumerated -- its header says the subject-to-records mapping is
        the caller's responsibility by design -- the fan-out takes the artifact
        types the contract lists, and `discovery` is a stage in the orchestration
        that executes nothing. Impact cannot be stated by a plan that never
        looked, which is why the contract refuses an estimate credited to a
        search the plan does not contain.

        DECISIONS. AN OMISSION IS NOT AN EXCLUSION, because a location in no step
        and no exclusion arrives at the subject as silence, and silence is
        indistinguishable from a store that held nothing. AN EXCLUSION IS A FACT
        ABOUT THE MOMENT OF EXECUTION, not of filing, and a lapsed one is refused
        rather than honoured. IMPACT IS A MEASUREMENT OR A NUMBER SOMEBODY TYPED,
        so a declared count on an irreversible step is refused. AUTHORIZATION IS
        PER OPERATION, because searching, disclosing and destroying are three
        acts and one approval over "the request" collapses them in the dangerous
        direction -- and S10.13.a's rule is carried in, so a step that discloses
        to the requester on an operator scope alone is refused. AN APPROVAL BINDS
        A REVISION, and a subject asking for their own erasure IS the authority
        rather than a bypass of it, so only the other authorities owe a second
        signature. A STEP CANNOT RUN AFTER THE THING THAT LETS IT FIND THE
        SUBJECT IS GONE: afterwards it is not slow, it is blind -- it finds
        nothing and reports it.

        THE SCAN FAILS ON DRIFT AND REPORTS THE ESTATE. It re-derives the stage
        list, the writers of a stage transition, the caller set of the sequential
        fan-out, the ORDER of the two calls inside `executeDsarErasureForSubject`
        and the argument the second is not given, both scope filters and what
        each drops, and the accepted archive immutability modes. The 47 findings
        about the estate are REPORTED and do not fail, the same split S10.13.b
        ships under. Two of its own defects were caught by its negative controls:
        it counted its own register file as a stage writer -- the trap S10.13.b
        recorded -- and it counted `evidence_pack` as a store holding nothing
        because the builder names it under both factories.

        WHAT IT DOES NOT CLAIM. It does not fix any of this. The four
        orchestrations still do not know about each other; `AdminDeletionJob`'s
        `requestId` is documented as a UI deep-link and nothing writes a stage
        from a real erasure. It does not establish what is in the two opaque
        volumes, which S10.13.b already recorded as undetermined. -->

        <!-- Frontier note: S10.13.d onward inherit this vocabulary. The export
        isolation tests (d) have a measured starting point in the format gate
        above; the deletion tests (e) have the rehydration pair S10.13.b named
        and the ordering finding here; the exception record (f) has the hold
        checkpoints; the receipt (g) has the 47 findings this cell reports as the
        list of things a receipt would have to be honest about. -->

  - [x] S10.13.d Export access-controlled, portable, understandable,
        checksummed/encrypted data with source/category/purpose/time and no
        other subject/tenant data. Evidence: export isolation tests.

        <!-- S10.13.d: shipped as
        `libs/oshun/workbench-kit/src/subject-export.ts` (`ExportBundle`,
        `exportBundleFaults` with 25 fault kinds, `exportIsolationFaults`,
        `twoPartyExportFaults`, `exportElementFaults`, `ESTATE_TWO_PARTY_STORES`,
        `ESTATE_EXPORT_SCOPES`, `buildEstateExportBundle`), 42 tests in
        `subject-export.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-export-scan.mjs` (nx target
        `subject-export-scan`, `pnpm run verify:operations:v1-subject-export`,
        22 negative controls), and the registrations a kit entry point needs.

        A RECORD ABOUT TWO PEOPLE IS NOT THE FIRST ONE'S TO RECEIVE -- AND THE
        ERASURE IN THE SAME FILE ALREADY KNOWS IT. This is not a principle
        imported from outside. `tip-ledger-store.ts` erases a subject by
        replacing THEIR field -- `viewerId` if they were the viewer, `creatorId`
        if they were the creator -- with `redacted:<digest>` and leaving the
        counterparty's field intact, because the counterparty's record of that
        transaction is the counterparty's data. **A hundred lines up in the same
        file, `getExportRecord` spreads the whole entry** and hands both ids to
        whichever of the two asked first. Measured across four stores:

        | store         | erasure does            | export does | counterparty |
        | ------------- | ----------------------- | ----------- | ------------ |
        | tip-ledger    | pseudonymises one field | copies whole| every tipper |
        | group-session | prunes one roster entry | copies whole| every member |
        | follow        | removes own edges only  | copies whole| every follower |
        | appreciation  | removes own rows        | own rows    | **none**     |

        `appreciation` is the control: keyed by the subject, naming nobody else,
        and it produces no finding -- a register in which every row is a finding
        is measuring its own appetite. `group-session` is the sharpest, because
        the store ALREADY separates a private leave audit from a shared one, so
        it is not that nobody thought about who sees what: `exportSession` strips
        `inviteTokenSha256` and nothing else, and a participant receives the
        host's id, the whole roster and the shared leave log.

        A PORTABLE FORMAT IS A PROMISE ABOUT THE BYTES, AND THIS ONE IS A FIELD
        INSIDE THEM. `assembleExportBundle` always `JSON.stringify`s, the route
        hardcodes `contentType: 'application/json'`, the download is
        `<exportId>.bundle.json`, and **there is no csv, markdown or zip renderer
        anywhere under `data-export/`** -- while the bundle records
        `format: 'csv'` when csv was asked for. So the format parameter does not
        do the one thing it is for, and does do the one thing it must not: it
        drops scopes (S10.13.c measured 20 json / 7 csv / 5 zip / 3 markdown of
        20). A subject who asks for markdown receives three scopes, in JSON, in a
        file that says it is markdown.

        AN EXPORT IS A NEW COPY, AND WHERE IT LANDS DECIDES HOW LONG IT OUTLIVES
        AN ERASURE. The assembled bytes are held `bytesBase64` -- an encoding,
        not a protection -- in `admin_store_snapshot`; nothing in the durable
        snapshot store encrypts; and that is the `postgresql` state class the
        off-box archive captures under object lock for **30 days**, the number
        S10.13.b bound to the backup script. So an access export taken shortly
        before an erasure puts a complete unencrypted copy of the subject beyond
        that erasure's reach for a month. The LIVE bundle is genuinely erased,
        through the `customer_export_bundles` partition on the
        `generated_artifact` eraser -- which is what keeps this a finding about
        the ARCHIVE rather than about the store.

        THE TENANT IS SELECTED AND NEVER FILTERED ON, AND THE FIRST VERSION OF
        THIS REGISTER GOT IT WRONG. It said `tenantId` did not appear in the
        export surface at all; a negative control falsified that. The truth is
        worse: the canonical V1 readers `select` `tenantId: true` on every row
        and type their predicate as `where: { readonly userId: string }`, so a
        tenant filter is **unexpressible** rather than merely unused. A subject
        present in two tenants receives both, each row stamped with the tenant it
        came from, inside a bundle that has no tenant of its own. The evidence of
        the leak is printed in the payload.

        THE ELEVEN ELEMENTS, WITH BOTH COMPOUNDS SPLIT. Graded as
        "checksummed/encrypted" this export scores on a sha256 it really
        computes; graded as two, it is checksummed and stores a plaintext copy of
        the subject in a captured table. Four elements are carried by nothing --
        **understandability, encryption, purpose and tenant isolation**. Purpose
        has ZERO occurrences anywhere under `data-export/`, so a subject is told
        what is held and never why, which is the half an objection is made
        against. Source is a habit rather than a field: `BundleScopeSection` has
        none, `storagePartition` appears inside items where a collector happened
        to add one, and `coverageGaps` names a partition as a first-class field
        only when the data is ABSENT -- the estate is more rigorous about where
        data is not than about where it is. Category is
        `appliesToCategories[0]` and five scopes roll up to two, every second one
        `audit_history`, so **a subject who asks for their audit history receives
        sections labelled `billing_receipts`, `consent_history` and
        `support_history`** and nothing says those are what they asked for. Time
        is the instant of assembly, not the period covered, so an empty section
        cannot be read.

        ACCESS CONTROL IS THE ONE ELEMENT WITH NO CAVEAT, and a cell that only
        reports gaps is not measuring: a customer session, a 403 on
        `bundle.userId !== auth.userId`, a 404 unless the request is `completed`,
        an expiry purge before every read, `cache-control: no-store`, and an
        `ExportBundleSubjectDeletedError` fence. The collection is real too --
        each section hashes what it collected, the bundle sha256 is over the
        bytes the customer downloads, and a type with no in-BFF store is recorded
        `available: false` with a reason rather than fabricated. Four of the
        twenty scopes have no collector: `voice_profile`, `avatar_pack`,
        `audit_event`, `analytics_event` -- and the last two are the audit and
        derived-analytics classes S10.13.c found no deletion step for, so the
        same two classes are absent from both operations.

        THE DESTRUCTIVE OPERATION IS SIGNED AND THE DISCLOSING ONE IS NOT.
        Deletion produces Ed25519 attestations the orchestrator verifies against
        a public key; the export's digest is computed and served by the same
        surface, so it detects corruption and not substitution. That is recorded
        as a `repair` rather than a refusal, because a digest that only detects
        corruption is doing a real job badly named.

        WHAT IT DOES NOT CLAIM. The transcribed estate bundle carries NO items,
        on purpose: the estate's sections are assembled from per-store records
        whose shapes differ store by store, and inventing item rows to grade
        would be grading this file's imagination. So `exportIsolationFaults` is
        VACUOUS over it and the scan says so in as many words rather than letting
        a zero read as clean -- the item-level answer is the four-store two-party
        register. It does not fix any of this, and it does not establish that the
        three disclosing stores are the only ones; it establishes that they are
        the ones whose erasure already implements the rule their export does
        not. -->

  - [x] S10.13.e Delete/anonymize/withdraw in dependency-safe idempotent jobs,
        preserve only lawful held/minimal audit data, and prevent rehydration
        from cache/index/event/backup replay. Evidence: deletion tests.

        <!-- S10.13.e: shipped as
        `libs/oshun/workbench-kit/src/subject-erasure.ts` (`ErasureRun`,
        `erasureFaults` with 23 fault kinds, `executionOrder`,
        `estateFenceFaults`, `ESTATE_FENCE_CONVENTIONS`,
        `ESTATE_REHYDRATION_GUARDS`, `ESTATE_WITHDRAWAL`,
        `buildEstateErasureRun`), 40 tests in `subject-erasure.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-erasure-scan.mjs` (nx target
        `subject-erasure-scan`, `pnpm run verify:operations:v1-subject-erasure`,
        15 negative controls), and the registrations a kit entry point needs.

        A TOMBSTONE THAT LIVES INSIDE THE THING BEING RESTORED CANNOT SURVIVE THE
        RESTORE. Fifty-eight stores carry a deleted-subject fence and it is a
        REAL one: a second erasure is a no-op returning zero, and **118 non-test
        BFF files assert an active subject before a write**, so a replayed
        event's handler is refused. But the fence is a field of the same snapshot
        document as the rows, and every restore does
        `this.deletedSubjectIds = new Set(snapshot.deletedSubjectIds)` -- a
        replacement, not a union. **A restore from before the erasure returns the
        subject's rows AND the older fence set that does not contain them.**
        S10.13.b measured that archive at 30 days under `provider-object-lock`,
        so that is the window in which the estate's only anti-rehydration
        mechanism is absent from the thing being restored. This is a claim about
        a SHAPE rather than a symbol, so the scan reads the restore assignments
        themselves and fails the day one becomes a union.

        THREE OF THE FOUR SOURCES ARE GENUINELY GUARDED, which is what makes the
        fourth a finding rather than a mood:

        | source       | guard                          | survives its replay |
        | ------------ | ------------------------------ | ------------------- |
        | cache        | the owning store's fence       | yes                 |
        | search-index | **not captured at all**        | n/a                 |
        | event-replay | `assertSubjectActive` on write | yes                 |
        | backup       | the same fence                 | **no**              |

        The deployed compose runs no elasticsearch, opensearch, qdrant or
        meilisearch, so "not captured" is the honest answer and the scan fails
        the day a search profile is deployed. Four persistent buses call
        `replayUnacked()` at boot -- conversation, assignment, session,
        content-drop -- and their payloads are subject data, so an event unacked
        at the moment of an erasure IS redelivered afterwards and IS refused. The
        deletion bus alone is built `persistence: false`, deliberately: its
        signed attestations are the audit trail rather than a stream.

        THE PROOF OF AN ERASURE IS KEPT TWO WAYS IN ONE ESTATE. **35 stores keep
        the subject's own account id** in `deletedSubjectIds`; **23 keep a
        digest** in `deletedSubjectDigests`/`redactedSubjectDigests`. The split
        is clean -- no store carries both -- and nothing in the repository
        chooses. So nearly half the estate already demonstrates the minimal
        record of an erasure and the other half retains the person it erased. The
        interesting finding is not that a raw id is worse than a digest; it is
        that both are here, in stores that sit next to each other.

        AND THE RECORD OF THE ERASURE IS FILED UNDER THE ACCOUNT IT ERASED.
        `apps/oshun/bff/src/data-deletion/state.ts` has no `deleteForSubject` and
        no fence of its own, and its state map is keyed by `userId` -- so the
        whole deletion request, with its artifacts, derived jobs, tombstones and
        their signed attestations, the confirmation token, the reason and every
        timestamp, outlives the subject under their own key with no expiry. The
        tombstones' own stored shape carries service, status, rows, detail,
        signature, key id and instant and **no subject id**, so the contract
        correctly does not flag them: the residue that identifies the subject is
        the key it is filed under, not the payload.

        A WITHDRAWAL NAMES THE TWO THINGS THAT MUST STOP AND NOTHING ASKS EITHER
        OF THEM. `buildWithdrawalCascade` sets `storageBlockedAt` and
        `recallBlockedAt` to the withdrawal instant and builds up to **six** jobs
        -- a silent `.slice(0, 6)` -- with an eta 24 hours out. The two
        timestamps are read by exactly two things: the serializer that echoes
        them back to the customer, and the auth store's type declaration. No
        storage path asks whether storage is blocked; no recall path asks whether
        recall is; nothing anywhere advances a cascade job's status, so the eta
        passes unobserved. The subject is shown a block that is a record of an
        intention published as an effect -- which is the fourth jobs list in this
        section with no executor, after S10.13.c's three.

        DECISIONS. AN IDEMPOTENCY FENCE IS A RECORD OF A PERSON, necessarily, so
        the question is which record and not whether. A GUARD IS SCOPED BY WHAT
        IT SURVIVES: a fence that survives a re-run and not a restore is not a
        weaker fence, it is a guard against a different event, so the combination
        is refused rather than counted. A FENCE KEPT IN THE STORE IT GUARDS is
        only as durable as the thing it exists to outlive. A DELETE NEEDS NO
        FENCE AND AN ANONYMISE OR A WITHDRAW DOES, because repeating those
        changes the result -- a second anonymise pseudonymises the pseudonym, a
        second withdrawal re-dates a block the subject already has. ANONYMISATION
        IS AN EFFECT AND NOT A SOFTER DELETE, so it names the fields it replaces
        and what they become, and a replacement that still names the person is
        refused. AND AN HONEST "NOT CAPTURED" IS THE STRONGEST ANSWER THERE IS,
        so nothing further is asked of a source that holds no data.

        WHAT IT DOES NOT CLAIM. The transcribed run declares no dependencies
        between its fifteen jobs, because the estate declares none -- S10.13.c
        measured that -- and filling one in here would be transcribing a plan
        that is not run. It does not establish that the 35 raw-id stores are
        wrong and the 23 digest stores right; it establishes that the estate has
        not decided, and that it already owns the smaller answer. -->

  - [x] S10.13.f Record protected retention exception with authority/legal
        basis, scope, fields, reason-safe communication, expiry/review, access,
        and later deletion. Evidence: exception tests.

        <!-- S10.13.f: shipped as
        `libs/oshun/workbench-kit/src/subject-retention-exception.ts`
        (`RetentionException`, `retentionExceptionFaults` with 24 fault kinds,
        `exceptionReachFaults`, `exceptionElementFaults`,
        `ESTATE_PURGE_PARTITIONS`, `buildEstateRetentionException`), 34 tests in
        `subject-retention-exception.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-retention-exception-scan.mjs` (nx
        target `subject-retention-exception-scan`,
        `pnpm run verify:operations:v1-subject-retention-exception`, 12 negative
        controls), and the registrations a kit entry point needs.

        AN EXCEPTION THAT CANNOT NAME WHAT IT PROTECTS PROTECTS NOTHING. An
        operator granting a retention exception selects from **23** privacy data
        categories; `runComplianceRetention` evaluates
        `exception.dataCategories.includes(partition.dataCategory)` against
        **10**; **six are the same word**. The bridge between the two,
        `ComplianceRetentionAdminWorkspace`, types the field
        `readonly string[]`, so nothing in the compiler will ever notice them
        diverging.

        SIXTEEN OF THE TWENTY-EIGHT WIRED PARTITIONS ARE PURGED UNDER A CATEGORY
        NO EXCEPTION CAN NAME. `purgeExpiredComplianceRecords` wires 28
        partitions -- 5 `billing_history`, 5 `support_case`, 1
        `data_export_request`, 1 `consent_record`, **15 `review_package` and 1
        `evidence_pack`** -- and the last two are not in the 23. So an operator
        who must preserve DSA moderation reviews for a regulatory inquiry,
        operator incidents for a safety investigation, or Metis academic evidence
        for a dispute **has no category to say so**, and the purge removes them
        on schedule while the exception reads `active`. The other direction is
        the same defect: **17 of the 23 selectable categories** are asked about
        by no partition at all, so choosing `biometric` or `location` protects
        nothing and looks like it does.

        THE BLUNT INSTRUMENT BESIDE IT WORKS PRECISELY BECAUSE IT IS BLUNT. The
        legal-hold filter in the same function is by subject and status alone, it
        names no category, and it therefore covers every partition. The estate's
        precise instrument protects 12 of 28 and its imprecise one protects all
        28.

        AN UNREADABLE EXPIRY BECOMES AN ETERNAL ONE.
        `buildComplianceRetentionAuthority` resolves an `effectiveUntil` that
        does not parse to `null`, and the filter reads
        `effectiveUntilUnixSeconds === null` as unbounded. Neither half looks
        wrong alone, which is why the scan checks them together: a typo in a date
        is a permanent hold. This is S10.8's missing-factor defect -- every
        absent value resolving to a number instead of a refusal -- in a date.

        FOUR OF THE TEN ELEMENTS ARE CARRIED AND CAREFULLY CHECKED, and a cell
        that only reports gaps is not measuring: `AdminRetentionExceptionSchema`
        refuses an `effectiveUntil` at or before `grantedAt`, refuses a
        revocation with no revoker and no reason, and refuses
        `justification: 'legal_hold'` with no `legalHoldId`. Authority, legal
        basis, expiry and review are all genuinely there.

        FOUR ARE CARRIED BY NOTHING: **fields, subject-notification,
        reason-disclosure and access.** The record has no field-level scope, so
        "keep the billing record" is the only sentence it can say and never "keep
        the tax total and not the phone number". It says nothing about what the
        subject was told -- which S10.13.c and S10.13.d both refuse -- and
        nothing about whether telling them the reason is safe, **while three of
        the eight justifications (`fraud_investigation`, `safety_investigation`,
        `regulatory_inquiry`) are ones where it is not.** And nothing limits who
        may read data that survived an erasure for one named purpose.

        TWO OF THE FOUR STATUSES ARE ASSIGNED BY NOTHING. The mutator sets
        `active` and `revoked`; `expiring_soon` and `expired` are set nowhere,
        and the mutator even branches on `expiring_soon` -- code reading a state
        nothing can enter. This is MITIGATED, and the mitigation is the estate
        getting something right: the executor filters on `effectiveUntil`
        regardless of status, so the date is honoured even though the label
        never changes.

        DECISIONS. TELLING A SUBJECT THAT DATA IS HELD IS NOT TELLING THEM WHY,
        AND ONLY ONE OF THOSE IS ALWAYS SAFE -- so "reason-safe communication" is
        split into two fields and the refusals run in both directions: an
        exception nobody was told about, and a reason disclosed where disclosing
        it defeats the purpose of holding the data. A REASON WITHHELD IS WITHHELD
        UNTIL A DATE, because an order that forbids telling the subject does not
        forbid it for ever. SCOPE HAS A GRAIN, and a category-grained exception
        always keeps whichever of the obligation and the category is larger. AN
        UNREADABLE EXPIRY IS NOT AN ABSENT ONE and is refused separately. AND AN
        EXCEPTION THAT NEVER RELEASES IS A RETENTION POLICY.

        WHAT IT DOES NOT CLAIM. It does not establish that the 16 unprotectable
        partitions are wrong to be purged -- their retention windows may be
        right. It establishes that no exception can change them, which is a
        different and worse fact, because the surface that exists to change them
        reports success. -->

  - [x] S10.13.g Reconcile each processor/store outcome and issue a signed
        receipt of completed/pending/refused/retained items without leaking
        hidden data. Evidence: receipt fixtures.

        <!-- S10.13.g: shipped as
        `libs/oshun/workbench-kit/src/subject-receipt.ts` (`Receipt`,
        `receiptFaults` with 25 fault kinds, `estateReceiptFaults`,
        `ESTATE_RECEIPTS`, `buildEstateSubjectReceipt`), 30 tests in
        `subject-receipt.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-receipt-scan.mjs` (nx target
        `subject-receipt-scan`, `pnpm run verify:operations:v1-subject-receipt`,
        11 negative controls), and the registrations a kit entry point needs.

        A RECEIPT IS THE ONE ARTIFACT THAT DESCRIBES THE SUBJECT'S DATA TO THE
        SUBJECT, SO EVERY WORD IN IT IS BOTH AN ANSWER AND A DISCLOSURE.
        "Retained under a legal hold" answers the question and tells them an
        investigation exists; "erased from `metis_assessment_evidence`" answers
        it and hands them an internal store name. So a receipt is graded twice:
        for what it fails to say, and for what it says too loudly.

        THREE RECEIPT-SHAPED THINGS DIVIDE THE JOB SO THAT NO SINGLE ONE DOES IT:

        | artifact                     | reaches      | signed | can say "kept" |
        | ---------------------------- | ------------ | ------ | -------------- |
        | `DsarDeletionReceipt`        | an operator  | **no** | no             |
        | `DeletionAttestation`        | the customer | per item | no           |
        | `ComplianceRetentionReceipt` | **a log line** | no   | **yes**        |

        **THE ONLY RECEIPT WITH A WORD FOR DATA DELIBERATELY KEPT IS THE ONE
        NOBODY RECEIVES, AND THE ONLY ONE THAT IS SIGNED HAS NO SUCH WORD.**
        `blocked_by_legal_hold` and `blocked_by_retention_exception` exist in
        exactly one type, and that type is handed to `app.log.info`. So the
        entire retention-exception apparatus S10.13.f measured produces outcomes
        no subject can ever be shown.

        `refused` HAS NO WORD ANYWHERE AT ALL. `failed` is an error, not a
        refusal, and the difference is the whole of a subject's ability to
        appeal. `not-found` conflates three facts -- already gone, never held,
        and a store that cannot select the subject at all, which is S10.13.b's
        `voice_profile`.

        THE SIGNATURE IS PER ITEM AND NEVER OVER THE SET. `DeletionAttestation`
        is the strongest artifact in this whole section: a real Ed25519 signature
        over service, subject, deletion id, outcome and instant, verified against
        a public key before a deletion may complete. And nothing signs the
        COLLECTION, so an item can be dropped and every surviving signature still
        verifies -- while the claim a subject most needs is that these are ALL
        the items.

        NO RECEIPT DISTINGUISHES A PROCESSOR. Exactly one is in the deletion
        path: the wearable bridge, whose external delete throws
        (`WearableHealthExternalDeletionError`) and correctly fails the receipt
        closed. It produces no item of its own, so one `rowsRemoved` covers both
        sides of that boundary and **a subject cannot tell from any receipt that
        a third party ever held their data.**

        DECISIONS. FOUR STATES, AND `retained` IS THE ONE THAT MATTERS, because
        it is the only one a subject cannot infer from silence -- a receipt
        without it reports a partial erasure as a complete one by omission. A
        PENDING ITEM IS PENDING UNTIL A DATE, or "pending" and "we have not got
        to it" are the same word. RECONCILIATION IS PER PARTY AND A PROCESSOR IS
        A PARTY. AN INTERNAL IDENTIFIER IS NOT AN ANSWER -- which is why the
        audience is a field, and why the same wording is refused on a subject's
        receipt and allowed on an operator's. AND A SIGNATURE IS WHAT MAKES A
        RECEIPT EVIDENCE rather than a claim by the party with the motive to make
        it.

        WHAT IT DOES NOT CLAIM. The transcribed subject receipt adds no
        `retained` item, because the estate cannot produce one: the tombstone
        shape is `{ tombstoneId, artifactType, createdAt, expiresAt,
        attestation? }` and the attestation's vocabulary is
        `erased | not_found | failed`. Inventing one would be transcribing a
        receipt the estate cannot issue. -->

  - [x] S10.13.h Test cancellation, duplicate request, identity mismatch,
        provider unavailable, partial failure, hold release, backup restore, and
        deadline escalation. Evidence: lifecycle matrix.

        <!-- S10.13.h: shipped as
        `libs/oshun/workbench-kit/src/subject-lifecycle-matrix.ts`
        (`LifecycleRow`, `lifecycleMatrixFaults` with 14 fault kinds,
        `ESTATE_LIFECYCLE_MATRIX` — the eight scenarios over eleven paths), 23
        tests in `subject-lifecycle-matrix.spec.ts`,
        `libs/oshun/workbench-kit/tools/subject-lifecycle-matrix-scan.mjs` (nx
        target `subject-lifecycle-matrix-scan`,
        `pnpm run verify:operations:v1-subject-lifecycle-matrix`, 11 negative
        controls), and the registrations a kit entry point needs.

        A SCENARIO YOU CANNOT REACH IS NOT A SCENARIO YOU PASS. A coverage report
        has two colours and this needs three: "tested and works", "reachable and
        untested", and "there is nothing to test" produce the same empty test
        file, and only the middle one is fixed by writing a test.

        | scenario             | reachable | driven | outcome        |
        | -------------------- | --------- | ------ | -------------- |
        | cancellation         | yes       | yes    | no-op          |
        | duplicate-request    | yes       | yes    | refused        |
        | identity-mismatch    | **split** | export only | refused   |
        | provider-unavailable | yes       | yes    | failed-closed  |
        | partial-failure      | yes       | yes    | failed-closed  |
        | hold-release         | **no**    | —      | —              |
        | backup-restore       | yes       | **no** | refused        |
        | deadline-escalation  | **no**    | —      | —              |

        FIVE OF THE EIGHT ARE HANDLED AND DRIVEN AGAINST THE REAL MECHANISM, and
        a matrix in which everything is broken is measuring its own appetite:
        `runDeletion` re-reads the request after the fan-out returns and refuses
        to finalize one cancelled mid-run; a duplicate is 409
        `active_deletion_exists` over a fence in 58 stores; the export download
        is 403 `bundle_owner_mismatch`; a provider outage throws
        `WearableHealthExternalDeletionError` and fails closed; a partial failure
        finalizes `deletion_incomplete` naming the unsatisfied services.

        HOLD RELEASE IS UNREACHABLE BECAUSE NOBODY CAN OPERATE A LEGAL HOLD. The
        hold is the one instrument in this estate that genuinely blocks a
        deletion -- the admin workflow refuses `start_stage` for a held subject
        and the compliance purge skips every partition for one, category-blind
        where S10.13.f's exception reaches only 12 of 28. And **`legalHolds` is
        READ in two places and MUTATED in none**: no action route, no
        `legalHolds.push` or `.find` anywhere in the admin state store, and the
        only released hold in the repository is a seed literal called
        `lh-released-matter-closed`. Nobody can raise one when a matter opens or
        clear one when it closes.

        DEADLINE ESCALATION IS UNREACHABLE BECAUSE THERE IS NO DEADLINE.
        S10.13.a measured `deadline` as carried only by `DsarRequest`, which
        nothing outside its own spec file calls; the two dates the estate keeps
        are an execution schedule and a forecast; and a search of the whole
        subject-request path for an escalation returns nothing. **There is
        nothing to escalate from**, so the row is unreachable rather than
        untested -- and that is why it is the one row with no mechanism to name,
        which the contract refuses out loud.

        IDENTITY MISMATCH IS OPPOSITE ON ITS TWO PATHS: exercised and refused on
        the disclosing one, and unreachable on the destroying one, because the
        erase route gates on `admin:workspace:privacy` and its request type has
        no identity to disagree with. One row per scenario would have averaged
        those into "partly covered", which is the answer that stops anybody
        looking -- so reachability is recorded per path.

        BACKUP RESTORE IS THE ONE REACHABLE ROW NOTHING DRIVES, and writing the
        test would fail: S10.13.e measured that every restore replaces the fence
        set rather than unioning it. The existing durability specs restore the
        snapshot that CONTAINS the deletion, which is a different question.

        DECISIONS. REACHABILITY COMES FIRST, so a row that is unreachable AND
        exercised is a contradiction the matrix refuses rather than a strong
        result -- whatever that test drove, it was not the estate. AN
        UNREACHABLE ROW OWES A REASON AND THE REASON IS A CLAIM ABOUT THE SOURCE,
        because "no mechanism" and "a mechanism nobody can operate" are different
        repairs: one is a feature, the other a route. A TEST AGAINST A DOUBLE IS
        NOT NOTHING AND IT IS NOT THE ESTATE. AND THE EXPECTED OUTCOME IS PART OF
        THE ROW, because a scenario handled without one is a scenario somebody
        watched happen.

        THE SCAN OPENS EVERY CITED TEST FILE, because a matrix that names a test
        nobody can open is the defect it exists to prevent; all seven are
        present. One of its negative controls caught a search that was reading a
        generated Prisma client -- `escalationRules` in another domain's column
        list -- and the search was narrowed to the subject-request path file by
        file. -->

- [x] S10.14 Add runtime feature-flag inventory and expiry alerts. Evidence:
      stale flags fail the release-readiness check.

      <!-- S10.14 PARENT: a-f all shipped and each re-read in this session. Six
      kit entry points, 246 tests, six scans with 76 negative controls between
      them.

      THE THREAD THAT RUNS THROUGH ALL SIX IS ONE ABSENCE. S10.14.a found that
      nothing in this estate emits a flag evaluation. .b that no declaration site
      can hold an expiry, and that the six sites which declare flags are not the
      two that answer them. .c that four of the ten integrity conditions are
      therefore undetectable rather than clean. .d that the only durations in the
      estate resolve to no instant, and that the one runbook step about flags is
      false in all three of its clauses. .e that the release gate blocks on two
      real conditions and cannot test three. And .f that the removal lifecycle's
      own precondition -- a verified cohort observation -- can never be met, for
      the reason .a wrote down first.

      **THE ESTATE'S FLAG MACHINERY IS NOT ABSENT. IT IS UNOBSERVED.** Every
      piece is real: sha256 bucketing, tenant allow/denylists, a kill switch,
      `unknown_flag` with `partial: true`, a waiver chain that sums its
      instalments, a gate policy type that makes a fabricated pass untypeable.
      What is missing throughout is the one thing that would let any of it be
      checked -- a producer -- and its absence is what turns each careful
      mechanism into a claim nobody can grade.

      The evidence line reads "stale flags fail the release-readiness check".
      They now do: `feature-flag-release-readiness-scan` runs the gate over a
      deliberate stale flag every time and exits non-zero if it passes. And the
      real estate's verdict is `block`, on six posture-changing environment gates
      with no owner and one flag read in production that no site declares. -->
  - [x] S10.14.a Discover declared flags from registry/code/config/provider and
        runtime-evaluated flags from telemetry without treating dynamic values
        as source truth. Evidence: flag inventory.

        <!-- S10.14.a: shipped as
        `libs/oshun/workbench-kit/src/feature-flag-inventory.ts`
        (`FlagInventory`, `flagInventoryFaults` with 17 fault kinds,
        `duplicateDeclarationFaults`, `ESTATE_FLAG_SOURCES`,
        `ESTATE_FLAG_INVENTORY` — 24 flags), 28 tests in
        `feature-flag-inventory.spec.ts`,
        `libs/oshun/workbench-kit/tools/feature-flag-inventory-scan.mjs` (nx
        target `feature-flag-inventory-scan`,
        `pnpm run verify:operations:v1-feature-flag-inventory`, 10 negative
        controls), and the registrations a kit entry point needs.

        AN INVENTORY IS A CLAIM ABOUT WHAT EXISTS, AND THIS ONE ONLY LOOKS WHERE
        IT ALREADY KNOWS. Of the five sources S10.14.a names, the estate reads
        one:

        | source    | producer                       | reader | yields |
        | --------- | ------------------------------ | ------ | ------ |
        | registry  | **two** literals, no shared source | yes | 11     |
        | code      | the two literals themselves    | **no** | —      |
        | config    | the deployment environment     | **no** | 13     |
        | provider  | **none**                       | no     | 0      |
        | telemetry | **none**                       | no     | 0      |

        NOTHING EMITS A FLAG EVALUATION. `routes/feature-flags.ts` evaluates and
        returns; it logs nothing, records nothing and reports nothing to
        analytics. So **the one source that could find a flag nobody declared has
        no producer**, and its silence is indistinguishable from an estate with
        no undeclared flags. There is no flag provider either — LaunchDarkly
        appears in this repository only in Shakti prose about deployment
        strategies and in a `provider?: 'local' | 'launchdarkly' | 'unleash'`
        field on a built type.

        AND CONFIG HOLDS THIRTEEN GATES NO REGISTRY LISTS AND NO OWNER CLAIMS.
        `OSHUN_BFF_ENABLE_TEST_HARNESSES` exposes harness routes on a running
        service; `OSHUN_DEV_AUTO_VERIFY_EMAIL` treats a signup's email as
        verified without the subject proving it; `OSHUN_DEV_SEED_ARETE_HABITS`
        writes seed data into a running estate; `OSHUN_TELEMETRY_SILENT` silences
        the instrument every other cell of S10 depends on; four adapters choose
        which implementation of a domain loads; and `OSHUN_ENV` is read in
        eighteen non-test files, which makes it the most consulted flag in the
        estate. **The registry knows what the home screen looks like; nothing
        knows whether the service is accepting unverified signups.**

        TWO OF THE THIRTEEN HAVE NO PRODUCTION READER AT ALL.
        `OSHUN_BFF_ALLOW_DEV_TOKENS` and `OSHUN_BFF_FORCE_SIGNED_TOKENS` — both
        permission gates, one of them named for the safety property rather than
        the relaxation — are read only by the auth route test that deletes and
        restores them around a case. Whatever they once allowed, the only thing
        that still remembers them is the test that guards them.

        TWO REGISTRIES, ELEVEN KEYS EACH, NO SHARED SOURCE. They agree today;
        nothing makes them agree tomorrow, and their FIELD sets already differ —
        only the mobile one carries a `description`, only the BFF one carries an
        `experiment` — so a reader of either gets a partial answer and cannot
        tell which half is missing.

        WHAT THE ESTATE GETS EXACTLY RIGHT, and it is this cell's own rule: **a
        dynamic value cannot define a flag.** An override is looked up by a
        DECLARED definition's key, so an override naming an unknown flag is
        ignored, and a requested key nothing declares comes back `unknown_flag`
        with `partial: true`. The registry is careful too — eleven keys, each
        with an owner, a default and tags, five carrying an experiment with a
        stable sha256 bucketing function.

        DECISIONS. A SOURCE EITHER DECLARES OR WITNESSES, AND TELEMETRY ONLY
        WITNESSES — that is what "without treating dynamic values as source
        truth" is as a check rather than a sentence. A FLAG WITNESSED AND NEVER
        DECLARED IS THE FINDING, NOT AN ERROR, because it is the only original
        result an inventory can have. A SOURCE WITH NO PRODUCER IS REFUSED ONCE,
        in the source register, rather than once per flag — which is why a
        declared flag nothing witnessed is only a `repair` here. CONFIG IS A
        DECLARING SOURCE, because an environment variable a deploy sets and the
        code branches on is a flag by every property that matters, and excluding
        it for having no `key` field is how the dangerous ones stay uncounted.

        WHAT IT DOES NOT CLAIM. It does not establish that the 13 config gates
        SHOULD be in the registry — some may be deploy-time build switches that
        never vary in production. It establishes that nothing has decided, and
        that the surface which reviews flags cannot be asked about them. -->

  - [x] S10.14.b Reconcile key/type/owner/purpose/capability/audience/default/
        environments/targeting/start/review/expiry/rollback/removal task and
        code/config references. Evidence: completeness report.

        <!-- S10.14.b: shipped as
        `libs/oshun/workbench-kit/src/feature-flag-completeness.ts`
        (`FLAG_ATTRIBUTES` — the fifteen the cell names plus the two reference
        classes, `reconcileAttribute` with 9 verdicts, `flagCompletenessReport`,
        `flagCompletenessScore`, `flagCompletenessFaults` with 19 fault kinds,
        `ESTATE_DECLARATION_SITES`, `ESTATE_FLAG_COMPLETENESS`), 59 tests in
        `feature-flag-completeness.spec.ts`,
        `libs/oshun/workbench-kit/tools/feature-flag-completeness-scan.mjs` (nx
        target `feature-flag-completeness-scan`,
        `pnpm run verify:operations:v1-feature-flag-completeness`, 15 negative
        controls), and the four registrations a kit entry point needs.

        **THE FLAG THAT DECIDES AND THE FLAG THAT IS DESCRIBED ARE NOT THE SAME
        FLAG.** S10.14.a found two registries. There are six sites, and the two
        that a running reader actually consults are the two with the fewest
        fields:

        | site                                                     | shape                        | keys | reaches a rendering decision |
        | -------------------------------------------------------- | ---------------------------- | ---- | ---------------------------- |
        | `apps/oshun/bff/.../tenant-feature-flags.ts`             | `Record<string, Definition>` | 11   | no                           |
        | `apps/oshun/mobile/src/experimentation/feature-flags.ts` | `Definition[]`               | 11   | no                           |
        | `apps/oshun/web/src/experimentation/feature-flags.ts`    | `Definition[]`               | 11   | no                           |
        | `apps/oshun/mobile/src/config/runtime.ts`                | `Record<string, boolean>`    | 7    | **yes — 4 call sites**       |
        | `apps/oshun/web/src/config/runtime-context.tsx`          | `Record<string, boolean>`    | 0    | **yes — 1 call site**        |
        | `libs/oshun/shell-core/src/feature-experiments.ts`       | `Definition[]`               | 6    | one internal caller          |

        THREE EXPORTED `useFeatureFlag(key: string, fallback = false): boolean`
        FUNCTIONS EXIST AND THE IMPORT PATH IS THE ONLY THING THAT SAYS WHICH
        SOURCE ANSWERS. Two are built over the registries — the mobile
        `FeatureFlagService` on a five-minute TTL and the web `hooks.ts` — and
        **neither has a single non-test importer**. The one the product imports
        is `@/config/runtime-context`, over the env map. So the whole pipeline
        the registry serves (tenant overrides, allow/denylists, kill switch,
        sha256 bucketing, the evaluation route) reaches nothing: an operator
        setting a tenant override for `quickActionsTray` changes the BFF's answer
        and does not change the home screen.

        AND THE SECOND-ORDER RULE THAT MAKES IT A CHECK: **AN ABSENT VALUE AND AN
        ABSENT FIELD ARE THE SAME WORD AND DIFFERENT REPAIRS.** "This flag has no
        expiry" and "no flag can have an expiry" both print as `expiry: missing`,
        and one is an owner's minute while the other cannot be done at all until
        a type changes. **Ten of the sixteen attributes are unholdable by every
        site in the estate** — type, capability, environments, start, review,
        expiry, rollback, removal-task, code-references, config-references —
        searched for under every plausible field name across all six files, zero
        hits. **All five lifecycle attributes are among them, so S10.14.d
        (alert before review/expiry), .e (fail a release on expired) and .f
        (close after cleanup) each have no field to read.**

        MEASUREMENTS THAT CORRECT S10.14.a. Its note says the two registries
        "agree today". They do not, and the reason is the cell's own lesson: it
        compared KEY SETS, and **the key is the one attribute records cannot
        disagree on — a difference in it produces two flags rather than one
        conflict**. Re-read attribute by attribute across all three registries:
        `activityAchievements` is owned by `activity-team` in the BFF and the web
        registry and by `shell-team` in the mobile one, so **the answer to "who
        turns this off in an incident" depends on which file the operator
        opens**; `quickActionsTray` is tagged `navigation` in two and `home` in
        the third.

        PURPOSE IS PRESENT TWICE AND CANNOT BE RECONCILED BY COMPARISON. Nine of
        the eleven shared flags carry two differently worded descriptions, and at
        least one pair states different behaviour: `notificationsDeduplicationHints`
        is "De-duplicate similar notification titles in the inbox" on web
        and "Show deduplication hints in notification center" on mobile — a
        content change and a UI affordance. Differing prose may describe the same
        thing and identical prose is a copy, so equality answers neither
        question; the verdict says so instead of picking one. **And the one
        registry that evaluates has no `description` field at all.**

        WHAT THE READERS NAME AND NOTHING DESCRIBES. `mocapCaptureStudio` and
        `livingSceneComposer` are read on the home tab and the mocap screen and
        exist only in the env map, whose type holds a key and a default — so
        thirteen of their attributes are not blank, they are unrecordable.
        `studio.study` is read by `isStudioStudyNavVisible` as
        `featureFlags['studio.study'] === false`, which makes its default ON, and
        **no site in any namespace declares it**. Meanwhile four of the five
        experiments — every one at `rolloutPercentage: 0` — are described in
        three registries and read by nothing.

        BOTH RUNTIME SITES ACCEPT A KEY FROM OUTSIDE. `EXPO_PUBLIC_FF_<ANYTHING>`
        becomes a flag with no membership test, and `toFeatureFlagKey`
        lowercases before camel-casing, so `EXPO_PUBLIC_FF_homeLayoutV2=true`
        creates `homelayoutv2` and the real flag keeps its default. **The estate
        contains the correct rule and applies it on one path only**: the BFF
        returns `unknown_flag` with `partial: true` for exactly this case.

        THE SCORE REPORTS TWO NUMBERS AND NEVER SUMS THEM, because one total
        lets a schema gap be closed by filling a field in a file no reader opens.

        TRAP, caught by a negative control firing on the UNMUTATED run: the scan
        searching for its own subjects found `feature-flag-inventory.ts` and
        `feature-flag-completeness.ts` and reported four experiments as having
        readers. **A register that counts its own family invents a defect** — the
        fix is a path prefix, and the control is what made it visible. -->

  - [x] S10.14.c Detect undeclared, duplicate/conflicting, unused,
        always-on/off, unknown state, expired, missing owner/purpose/removal,
        unsafe default, and contradictory nested flags. Evidence: integrity
        tests.

        <!-- S10.14.c: shipped as
        `libs/oshun/workbench-kit/src/feature-flag-integrity.ts`
        (`INTEGRITY_CONDITIONS` — the ten the cell names, split to thirteen where
        it compounds them, `CONDITION_EVIDENCE` classifying each by the evidence
        it needs, `integrityRunFaults` with 14 fault kinds, `gateDefaultFaults`,
        `invariantFlagFaults`, `discardedStateFaults`, `grantsWhenUnset`,
        `withholdingValues`, `synonymsOf`, `ESTATE_BOOLEAN_GATES`,
        `ESTATE_INTEGRITY_RUN`), 43 tests in `feature-flag-integrity.spec.ts`,
        `libs/oshun/workbench-kit/tools/feature-flag-integrity-scan.mjs` (nx
        target `feature-flag-integrity-scan`,
        `pnpm run verify:operations:v1-feature-flag-integrity`, 16 negative
        controls), and the four registrations a kit entry point needs.

        **A CONDITION ABOUT A RUN CANNOT BE DETECTED IN A DECLARATION, AND THE
        TWO HAVE THE SAME NAME.** The cell lists ten conditions as if they were
        one kind of thing. Eight are properties of a RECORD and you find them by
        reading a file. Four — unused, always-on, always-off, unknown-state — are
        properties of a RUN, and S10.14.a established that nothing in this estate
        emits a flag evaluation. One, expired, needs a date S10.14.b measured no
        declaration site can hold. **So a detector that reads
        `defaultValue: true, active: true, no experiment` and reports "always-on"
        has found something real and named it wrongly**: nothing in the
        DEFINITION can make it vary is a different claim from it has been on in
        the live system, and the first is silenced by adding an experiment block
        while the flag stays on. Every run condition here therefore carries a
        DECLARATION SIBLING under its own name —
        `invariant-by-construction`, `no-reading-expression`,
        `state-discarded-at-the-client` — and the run condition returns
        `undetectable`, not `clean`. **A DETECTOR WITH NO OBSERVATION SOURCE
        REPORTS EXACTLY WHAT A CLEAN ESTATE REPORTS**, which is this cell's
        single easiest wrong answer.

        SEVENTEEN BOOLEAN COMPARISONS OVER FOURTEEN KEYS, AND SEVEN OF THE KEYS
        ARE IN NO INVENTORY. S10.14.a counted thirteen config gates; a sweep
        finds seven more capability gates, four of which expose a test harness
        (`OSHUN_ENABLE_TEST_HARNESSES`, one word from the BFF gate and on the
        other tier), a proxy bypass (`…_STUDIO_ISIS_LANE_E2E_BYPASS`), an
        operator preview surface, and a header-policy override. **The condition
        S10.14.a said only telemetry could find — a flag nobody declared — is
        found by reading the source, seven times.**

        POLARITY ALONE DECIDES NOTHING; POLARITY TIMES WHAT IS GRANTED DECIDES
        EVERYTHING. `OSHUN_AUTONOMY_AUTOCYCLE !== '0'` starts an autonomous cycle
        driver unless the variable is exactly `'0'`.
        `OSHUN_DEV_SEED_SESSION_ENABLED === 'false'` gates a route that signs up
        or logs in a fixed account and sets both session cookies, and it is
        enabled unless the variable is exactly `'false'`.
        `OSHUN_TELEMETRY_SILENT !== 'true'` has the **same** polarity and is
        entirely safe, because what an unset variable grants there is an
        instrument. The check carries both halves or it is a style rule.

        AND EACH OFF-SWITCH ACCEPTS ONE SPELLING, AND THEY ARE DIFFERENT ONES.
        `AUTOCYCLE` accepts `'0'`; `SEED_SESSION_ENABLED` accepts `'false'`. An
        operator who learned `=0` from the first leaves the second granting.
        This estate has a parser that accepts `false`, `0`, `no` and `off` —
        `parseBooleanString` in the mobile runtime — and **not one environment
        gate uses it**.

        NESTING IS AN `&&`, SO NOBODY CAN BE TOLD ABOUT IT.
        `OSHUN_TELEGRAM_GROUNDER_TEST_SCENARIOS` does nothing unless
        `OSHUN_BFF_ENABLE_TEST_HARNESSES` is also on; the dependency lives in a
        conjunction in `telegram/webhook.ts` and in no registry. The estate has
        the right shape and uses it in exactly one place — shell-core experiments
        carry `requiredFlags` and `isExperimentEligible` refuses when one is off
        — except that `assignConsumerShellExperiment` checks the override
        **before** eligibility, so an override assigns a variant for an
        experiment whose prerequisites are off, under a `reason` that names where
        the value came from and not the constraint it broke.

        ONE KEY, SIX BINDINGS, AND ITS NAME IS NONE OF THEM.
        `EXPO_PUBLIC_E2E_DISABLE_AUTOFILL` is bound to `E2E_AUTOMATION_ENABLED`,
        `E2E_HOME_TEST_HOOKS_ENABLED`, `E2E_PROFILE_TEST_HOOKS_ENABLED`,
        `E2E_TARA_TEST_HOOKS_ENABLED`, `E2E_ACTIVITY_TEST_HOOKS_ENABLED` and
        `E2E_SHOW_PASSWORD_TEXT` — the last renders the sign-in password as text.
        A seventh reader is the only one that honours the name, and it binds no
        identifier, which is why a binding census is not a reader census. **The
        disagreement is between READERS and a detector that compares declarations
        cannot see it.**

        UNKNOWN STATE IS NOT A GAP IN WHAT THE ESTATE KNOWS. The BFF returns a
        per-flag `source`, a `reason` from nine values, the experiment
        assignment, an `errors` array and `partial`. **Both clients parse
        `{ flags }` and drop the rest**, and each one's QA surface,
        `getAllFlags()`, returns `{ value, overridden }` with no source. A
        requested key the registry does not declare comes back as an error the
        client discards and the screen shows the default.

        TWO TRAPS, BOTH CAUGHT BY A NEGATIVE CONTROL FIRING ON THE UNMUTATED RUN.
        **An operator is not a polarity**: the first version read `!==` as
        "grants when unset", and `if (X !== 'true') return null;` is an `equals`
        gate written inside-out — the polarity of a call site is the operator
        combined with what the branch does, which is the same shape as the
        unsafe-default rule itself. And **a register that searches for its own
        subjects finds itself**, this time because `grep -h` had stripped the
        path the workbench-kit filter needed, so the register's own `source:`
        literals came back as call sites and gave two gates an invented second
        polarity. Keep the path; filter on it.

        AND A THIRD OF THE SAME SHAPE, from the toolchain rather than a control:
        **a register of source text is indistinguishable, to a text-matching
        gate, from source that does the thing.** Recording each gate's
        comparison verbatim made this module read as one that consults the
        environment directly, and the repository's `direct-process-env`
        conventions ratchet refused the commit -- correctly, on the evidence
        available to it. The `process.` prefix carries nothing the `at` field
        does not already give, so the register drops it. -->

  - [x] S10.14.d Alert owner/backups before review/expiry and escalate expired
        flags with affected routes/capabilities/cohorts and removal runbook.
        Evidence: clock/routing tests.

        <!-- S10.14.d: shipped as
        `libs/oshun/workbench-kit/src/feature-flag-expiry-alerts.ts`
        (`resolveDeadline`, `alertsDue`, `isReachable`, `escalationFaults` with
        12 fault kinds, `runbookStepFaults`, `ESTATE_EXPERIMENT_DEADLINES`,
        `ESTATE_STOPPING_CONDITIONS`, `ESTATE_ESCALATION`,
        `ESTATE_KILL_SWITCH_STEP`), 40 tests in
        `feature-flag-expiry-alerts.spec.ts`,
        `libs/oshun/workbench-kit/tools/feature-flag-expiry-alert-scan.mjs` (nx
        target `feature-flag-expiry-alert-scan`,
        `pnpm run verify:operations:v1-feature-flag-expiry-alerts`, 14 negative
        controls), and the four registrations a kit entry point needs.

        **A DURATION IS NOT A DEADLINE UNTIL SOMETHING STARTS IT.**
        `apps/oshun/mobile/src/experimentation/ab-framework.ts` -- a site
        S10.14.b's six did not include, because it declares EXPERIMENTS rather
        than flags -- carries `startDate: string | null`,
        `endDate: string | null` and `maxDurationDays: number`. All three
        experiments have `startDate: null`, `endDate: null`, and a real,
        deliberately differentiated limit: **28, 21 and 14 days from nothing**.
        `resolveDeadline` refuses to substitute `now` for the missing origin.
        That substitution is available, type-checks, and makes every duration
        resolve -- to a deadline always `durationDays` in the future however late
        you ask, so **the alert never fires and the flag never looks late**. Same
        shape as S10.9's grace window anchored to the event it was supposed to
        outlive: the quantity is real, the anchor is wrong, and the check goes
        unreachable rather than failing. The clock is a parameter throughout.

        AND THE MEASUREMENT THAT MAKES IT BITE: **THE STOPPING CONDITION THAT
        COUNTS PEOPLE IS ENFORCED AND THE ONE THAT COUNTS DAYS IS NOT.**
        `generateAnalysisReport` takes `durationDays` and `minSampleSize` as
        ADJACENT PARAMETERS. One becomes
        `hasMinSample = variants.every((v) => v.sampleSize >= minSampleSize)`.
        The other is copied into the report twice and interpolated into
        `**Duration**: N days`, and compared to nothing. `maxDurationDays` is
        read by nothing at all -- its interface, three literals, and no other
        line in the repository. So the two ways an experiment could end sit three
        lines apart in one object and only one is a check; and with every rollout
        at zero (S10.14.c) nobody is bucketed, so the enforced one can never be
        met either. **An experiment that can neither reach its sample size nor
        run out of time does not end.**

        A LABEL IS NOT AN ADDRESS. `owner: 'design-team'` resolves to nothing:
        the five files that mention a team string are the flag and experiment
        literals themselves, and no directory, rota, channel map or contact list
        in the repository maps one to a destination. **The scan checks this by
        looking for a SIXTH file** -- which is what a directory would be -- so it
        is a live check and not one that can only pass. And BACKUPS, S10.14.d's
        second recipient class, have no field at any site, so the alert would go
        to one unreachable name and one absence would silence it.

        THE PAYLOAD. Of routes/capabilities/cohorts, two are `unexpressible` on
        S10.14.b's measurement -- nothing links a flag key to the routes that
        branch on it, and `domainScope` names domains rather than capabilities --
        so no owner can supply them, only a type change. Cohorts exist.

        **A RUNBOOK STEP IS A SET OF CLAIMS ABOUT THE ESTATE, AND EVERY CLAUSE OF
        THE ONLY ONE THERE IS IS FALSE.** `docs/runbooks/shell-outage.md:88`
        reads: *"Kill-switch a recent flag: if a feature flag in
        `release-taxonomy.ts` was advanced in the last hour, set its cohort to 0%
        via the flag-admin API and observe bootstrap recovery."* Three claims,
        each checked by the method its kind implies:

        - A LOCATION. `release-taxonomy.ts` declares **no feature flags**; its
          four `flagged` occurrences are ProvenanceBundle moderation states and a
          `flag_reason` telemetry attribute.
        - AN OPERATION. The flag-admin API is
          `/v1/admin/studio/experimentation-flags` and its only two routes are a
          `GET` list and a `POST .../evaluate` -- **both reads**. The sole way to
          move a cohort is `OSHUN_TENANT_EXPERIMENT_SCOPES`, an environment
          variable, which is a redeploy.
        - A PRECONDITION. **Nothing in the estate records when a flag changed**,
          so the operator cannot evaluate the clause the step opens with.

        An operator following this step during an incident cannot complete its
        first clause. And no runbook in the estate covers RETIRING a flag at all
        -- the one flag step there is covers killing one mid-incident, which is
        the opposite operation.

        THE SCAN QUOTES, SO THE SCAN CHECKS THE QUOTATION. A register whose
        content is three refutations rots differently from one whose content is
        counts: the file stays, the step keeps its title, the words change, and
        the register goes on refuting a sentence nobody wrote. Each quoted clause
        is searched for in the runbook, and each verdict is re-derived rather
        than trusted -- the location by reading the file, the operation by
        enumerating the route's verbs, the precondition by searching for a
        changed-at field. The scan also prints a POSITIVE CONTROL: given an
        origin, the same 28 days yields an instant and two due alerts. **The
        arithmetic is not what is missing.** -->

  - [x] S10.14.e Fail release readiness on expired or critical incomplete flags
        and prevent waived expiry from becoming indefinite. Evidence: deliberate
        stale flag test.

        <!-- S10.14.e: shipped as
        `libs/oshun/workbench-kit/src/feature-flag-release-readiness.ts`
        (`FLAG_READINESS_CONDITIONS`, `flagReadinessVerdict`,
        `flagReadinessFaults` with 10 fault kinds, `DELIBERATE_STALE_FLAG`,
        `specimenFaults`, `FLAG_WAIVER_REQUIREMENTS`, `waiverRequirementFaults`,
        `ESTATE_READINESS_VERDICT`), 35 tests in
        `feature-flag-release-readiness.spec.ts`,
        `libs/oshun/workbench-kit/tools/feature-flag-release-readiness-scan.mjs`
        (nx target `feature-flag-release-readiness-scan`,
        `pnpm run verify:operations:v1-feature-flag-release-readiness`, 11
        negative controls), and the four registrations a kit entry point needs.

        **BOTH HALVES OF THIS CELL WERE ALREADY SOLVED IN THIS KIT, FOR A
        DIFFERENT SUBJECT.** `plugins.ts` types `GatePolicy.onStale`,
        `onNotConfigured` and `onFailed` as `NonPassingVerdict` — and says why:
        *"`pass` and `warn` are absent by construction. A policy that could map
        an unconfigured gate to `pass` is the fabricated success the
        honest-capability invariant exists to prevent, and the only reliable
        place to stop it is the type of the field somebody would have to set."*
        **A green gate that had no measurement is untypeable.** And
        `gate-waivers.ts` already walks a supersession chain with `renewalChain`,
        summing the instalments against the gate's own limit, because *"eight
        consecutive forty-eight-hour waivers are a two-week exemption that no
        single approval ever authorised"* — which is exactly "prevent waived
        expiry from becoming indefinite", already written and tested.

        **AND NEITHER HAS EVER BEEN POINTED AT A FLAG.** `gate-definitions.ts`
        contains the word "flag" zero times; no `GATE_TYPES` member names one; no
        gate in the registry takes a flag as its subject. So S10.14's own
        evidence line — "stale flags fail the release-readiness check" — names
        the one subject the release machinery has never been asked about, and the
        check passes every release by never having been given anything to fail
        on. This cell therefore BINDS rather than reimplements: it records the
        four requirements standing between a flag expiry and the waiver
        machinery, naming the shipped mechanism for each, because **a second
        implementation of a rule is a second place for it to differ.**

        **A GATE'S PASS MEANS NOTHING UNTIL SOMETHING HAS FAILED IT**, which is
        why the cell's evidence line is a METHOD rather than a behaviour. A
        readiness check that has never refused a release is indistinguishable
        from one whose predicate is `true`, and reading it cannot tell them
        apart. So `DELIBERATE_STALE_FLAG` ships as a specimen — eight days past
        an expiry, with no owner — and **the scan runs the gate over it every
        time and exits non-zero if it passes.** It carries TWO defects on
        purpose, so that a gate which quietly lost either check still fails on
        the other and looks fine only to a test that asserts both.

        THE SPECIMEN HAD TO BE MANUFACTURED, AND THAT IS THE SECOND FINDING. The
        estate cannot exhibit an expired flag — not because its flags are fresh
        but because S10.14.b measured that none can carry a date and S10.14.d
        that the only durations there are resolve to no instant. **"No expired
        flags" is true here the way "no unicorns escaped" is true.** Three of
        the five conditions come back `indeterminate` — a word this estate's own
        vocabulary already has — and reporting them as `pass` is precisely the
        substitution `NonPassingVerdict` makes untypeable one file over.

        THE VERDICT ON THE REAL ESTATE IS `block`, AND NOT VACUOUSLY. Two
        conditions the estate CAN exhibit are dirty, both established by the
        cells before this one: **six posture-changing environment gates with no
        owner** (S10.14.c measured that no environment gate names one anywhere,
        and these are the ones whose value changes what the service will accept
        — including the autonomy driver and the session seeder), and
        **`studio.study`, read by a navigation predicate and declared by no site
        in any namespace** (S10.14.b). The scan recounts the first list from
        S10.14.c's register rather than restating it, so the two cannot drift.

        **A CHECK THAT CANNOT FIRE IS THE DEFECT THIS CELL IS ABOUT, AND I WROTE
        ONE.** The first draft of `release-passed-on-an-unexhibitable-condition`
        asked whether the overall verdict was `pass` while something was
        unexhibitable — and every member of `NON_PASSING_VERDICTS` outranks
        `pass`, so one unexhibitable condition makes an overall `pass`
        structurally impossible. It was not a strict check; it was a check that
        reports nothing. Replaced by
        `release-clean-only-where-it-could-be-dirty`, which asks the reachable
        question one repair away: every condition the estate CAN exhibit is
        clean, and the rest could not be tested. A test now proves the old form
        was unreachable, so the reasoning survives the fix. -->

  - [x] S10.14.f Track code/data/schema/contract/test/docs/telemetry cleanup and
        close/remove registry entry after verified cohort observation. Evidence:
        removal lifecycle tests.

        <!-- S10.14.f: shipped as
        `libs/oshun/workbench-kit/src/feature-flag-removal.ts`
        (`REMOVAL_CLASSES`, `REMOVAL_STATES`, `cohortWasObserved`,
        `nextRemovalState`, `removalPlanFaults` with 11 fault kinds,
        `unenforcedSchemaFaults`, `ESTATE_PERSISTED_OVERRIDE`,
        `ESTATE_REMOVAL_PLAN`, `ESTATE_SCHEMA_ENFORCEMENT`), 41 tests in
        `feature-flag-removal.spec.ts`,
        `libs/oshun/workbench-kit/tools/feature-flag-removal-scan.mjs` (nx target
        `feature-flag-removal-scan`,
        `pnpm run verify:operations:v1-feature-flag-removal`, 10 negative
        controls), and the four registrations a kit entry point needs.

        **A REMOVAL IS A CLAIM THAT NOTHING IS LEFT, AND THE ONE COPY YOU CANNOT
        DELETE IS ON A DEVICE YOU DO NOT OWN.** The cell names seven cleanup
        classes and the estate has an eighth. `apps/oshun/web/src/experimentation/feature-flags.ts`
        persists its overrides to `localStorage` under `oshun-ff-overrides`;
        `loadOverrides()` reads the blob back as
        `new Map(Object.entries(parsed))` and **checks no key against any
        registry**; and `resolve(key)` consults the overrides BEFORE the remote
        value and before the default. So a flag's override outlives its
        declaration, on hardware the estate cannot reach, at the top of the
        precedence order — and if the key is ever reused, a decision somebody
        made about the old feature silently applies to the new one, for that
        person, forever. The mobile service holds its overrides in memory and has
        a `clearOverrides()`, which is what makes the web copy the finding rather
        than a generality.

        **A BLAST RADIUS COMPUTED BY NAME IS NOT A BLAST RADIUS.** Measured:
        `deepLinkHydration` appears in **nine** non-generated files and is the
        flag in **three** — the other six are about deep-link hydration the
        FEATURE. `crashReporting` appears across five unrelated product domains
        as a user preference. A removal plan built from a search sends its reader
        to edit files that have nothing to do with the flag, so every site
        carries HOW it was established and a name-matched one is refused.

        **EXACTLY ONE OF THE SEVEN CLASSES IS ENFORCED BY A MACHINE, AND ONLY FOR
        SIX KEYS.** `ConsumerShellFeatureFlagId` is a union type, so removing
        `deepLinkHydration` fails to compile at
        `requiredFlags: ['deepLinkHydration', 'currentDomainPersistence']` and
        narrows five `Record<ConsumerShellFeatureFlagId, boolean>` positions with
        it. The eleven registry flags are `key: string` and the seven env-map
        flags are `Record<string, boolean>` keys: removing one of those compiles
        everywhere, and `isRuntimeFeatureEnabled` returns the caller's fallback
        rather than saying it is gone.

        **AND THE FAMILY CLOSES WHERE IT OPENED.** S10.14.f's own precondition is
        "after verified cohort observation", and S10.14.a established that
        nothing in this estate emits a flag evaluation. So no cohort has been
        watched, `cohortWasObserved` is false for every flag, and **by the
        estate's own rule no flag may ever be removed**. `nextRemovalState`
        exists precisely to report that: a plan correctly stopped at
        `cleanup-verified` produces NO fault — it has claimed nothing it cannot
        support — so a reader of the fault list sees a clean removal going
        nowhere. Only something that tries the door finds it has no handle. One
        producer opens it and nothing else has to change; a test asserts that
        too.

        The plan shipped is for `homeLayoutV2`, the easiest case in the estate:
        three registries declare it and S10.14.b measured that no production
        expression reads it. It still cannot be removed, and its single refusal
        is the copy on somebody else's device.

        TRAP, caught by the scan's own control on its first run: the plan's three
        registry citations were tidy one-line SUMMARIES of multi-line literals —
        quotations that could not be found in the files they cited. **An ellipsis
        is a summary wearing a quotation's clothes.** All quotes are now verbatim,
        the scan re-reads every one of them in the file it names, and a test
        refuses an ellipsis in any of them. -->

### S11 — Shared verification and release harness

- [x] S11.1 Parameterize the Tara route-integrity test. Evidence: missing,
      duplicate, forbidden, and unowned routes fail.
      <!-- S11.1 CLOSED with a–g. Eight kit entry points, 409 tests, 51 negative
      controls across seven scans, and one result that the parent's evidence
      clause did not anticipate: on the host that mounts the workbench, **none
      of the four defects it names can be detected today** — not because the
      detectors are missing, but because the inputs are.

      **A ROUTE IS TRUE AT FOUR ALTITUDES — DESCRIBED, MOUNTED, REACHABLE,
      ADVERTISED — AND THEY DO NOT NEST** (a). Each adjacent pair is witnessed
      coming apart in `apps/oshun/web`: `/domains/tara` is mounted and undescribed,
      the 87 `/studio/isis` pages are described and unreachable behind one empty
      array, and `studio.study` hides a nav entry while the route answers. The
      test being parameterized compares two DESCRIBED facts, so it has no
      evidence side at all.

      Then: **A ROUTE TREE IS NOT A TREE OF ROUTES** (b) — proven by
      reproducing the framework's own normalization exactly, 772 patterns
      against 772, which took one correction no reading of the tree suggests.
      **A MISSING ROUTE IS A REQUEST THAT NOW GETS SOMETHING ELSE** (c) — five
      absorbing patterns in three classes, one of them unbounded. **TWO ROUTES
      COLLIDE WHEN A REQUEST COULD GO TO EITHER, AND THE COORDINATE IS USUALLY
      THE STAGE** (d) — `/metis/upload` answered three times and reachable once.
      **A BOUNDARY IS A CONJUNCTION AND ONLY THE TERMS THE CALLER CANNOT SET ARE
      THE BOUNDARY** (e) — one guard resting on one of four terms, one "gate"
      resting on none. **A ROUTE'S METADATA SLOT IS FULL AND NONE OF IT IS ABOUT
      US** (f) — 651 of 720 pages carry metadata and all seven of its fields are
      a crawler's. And **THE TWO SYNTHETIC DOMAINS CARRY THE METADATA THE REAL
      ONE DOES NOT** (g).

      Three findings in the estate worth acting on independently of the harness:
      **two shipped Tara surfaces** (`programs/[programId]`,
      `sources/[sourceId]`) are absent from the route map its own IA, hub and
      command palette read; **six of the nineteen `OSHUN_APP_LINK_PATHS` claims
      resolve to nothing**, `/home` at any depth, while both tests guarding that
      register compare it to a copy of itself; and the estate's one unbounded
      absorber, `/workspace/[[...path]]`, is empty today and that emptiness is
      the only reason a missing-route check means anything under it. -->
  - [x] S11.1.a Define test inputs for app/source roots, framework/proxy/
        middleware routes, route descriptors, host, audience, owner, capability,
        lifecycle, allowed/forbidden patterns, redirects, and feature flags.
        Evidence: harness contract.

        <!-- S11.1.a: shipped as
        `libs/oshun/workbench-kit/src/route-integrity.ts` (`ROUTE_ALTITUDES`,
        `ROUTE_INPUT_PROVENANCES` + `ALTITUDES_ESTABLISHED_BY`,
        `ROUTE_INPUT_CLASSES` + `ALLOWED_ALTITUDES_BY_CLASS`,
        `ROUTE_INPUT_DISCRIMINATIONS`, `EVIDENCE_COVERAGE`,
        `parseRouteInputSupply`, `ROUTE_INTEGRITY_CHECKS`,
        `resolveCheckReadiness`/`resolveAllCheckReadiness`,
        `routeInputSupplyFindings` with 6 fault kinds,
        `separationSupportFindings`, `OSHUN_WEB_ALTITUDE_SEPARATIONS`,
        `OSHUN_WEB_ROUTE_INPUT_SUPPLY`, `TARA_ROUTE_MAP_SPEC_SUPPLY`), 52 tests
        in `route-integrity.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-integrity-inputs-scan.mjs` (nx
        target `route-integrity-inputs-scan`,
        `pnpm run verify:operations:v1-route-integrity-inputs`, 8 negative
        controls), and the registrations a kit entry point needs.

        **A ROUTE IS TRUE AT FOUR ALTITUDES — DESCRIBED, MOUNTED, REACHABLE,
        ADVERTISED — AND THEY DO NOT NEST.** The tempting reading is a ladder,
        each altitude contained in the one below, so that an input at any of them
        is a weaker version of the same fact. `apps/oshun/web` refutes it at
        every adjacent pair, in the direction that makes containment false, and
        each refutation is a live surface:

        - **Described ⊅ mounted.** `apps/oshun/web/src/app/domains/tara/` is a
          directory with seven children and **no `page.tsx` of its own**. No
          source file describes `/domains/tara`; the framework routes it through
          the `[domainId]` sibling — absent from the 726 static routes, matched
          by `^/domains/([^/]+?)(?:/)?$`. **Eleven of the proxy's eighteen legacy
          redirect targets are in this class**, so a source-tree check reports
          eleven broken redirects that all work.
        - **Mounted ⊅ reachable.** `apps/oshun/web/src/app/studio/isis/` holds
          **87 first-level segments, every one with its own `page.tsx`**.
          `isAaaOnlyRoute` is a length check plus a negated `includes` against
          `STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS`, which is `Object.freeze([])`, so
          every non-empty segment is AAA-only and `resolveStudioBoundary` returns
          `hard-block-404` for an ordinary session. **87 pages, zero surfaces,
          and the difference is one empty array.**
        - **Reachable ⊅ advertised.** `studio.study` is the estate's only
          route-adjacent flag and `isStudioStudyNavVisible` uses it to remove a
          **navigation entry**. The function's own doc says what that does not
          do: "Route resolution … and breadcrumbs stay aware of `/studio/study`
          regardless of this decision, so a deep link still renders." Correct
          behaviour, and it means a flag input says what is offered, never what
          is open.

        **WHAT THAT MAKES OF THE TEST BEING PARAMETERIZED.**
        `apps/oshun/web/src/components/studio/StudioTaraWorkbenchRouteMap.spec.ts`
        compares `entry.implemented` — a hand-maintained boolean — against
        `existsSync(pageFileFor(entry.path))`. **Both sides are DESCRIBED.** It
        never reaches the framework, the proxy, or the flags, so both witnesses
        are invisible to it: `/domains/tara` would grade missing and is not, any
        of the 87 Isis pages would grade present and is not reachable. Graded by
        the shipped contract, `TARA_ROUTE_MAP_SPEC_SUPPLY` comes out
        `source-only-supply` and all four checks come out non-runnable — so
        parameterizing it is not the substitution of a constant; the check has no
        evidence side, and eleven of the twelve input names exist to give it one.

        **AN INPUT THAT CANNOT TAKE TWO VALUES CANNOT MAKE A CHECK FAIL.** The
        parent evidence clause promises that unowned routes fail. Ownership here
        is `domains.json` — 58 domains, each with an `owner`, **all 58
        `@GreyChimp`** — generating a CODEOWNERS that opens `* @GreyChimp`. The
        check is unfalsifiable twice over, so the contract reports it `vacuous`
        rather than green.

        **AGAINST THIS ESTATE ALL FOUR CHECKS ARE UNSUPPLIED, EACH FOR A
        DIFFERENT MISSING CLASS.** `missing` and `duplicate` want a route
        descriptor: `RouteDescriptor` carries seven of the twelve names at once
        and **`parseRouteDescriptor` has no production caller** — every one of
        its nine call sites is a spec, an archetype, a `__fixtures__` module or a
        tool. `forbidden` wants a host and an audience: **no production file
        under `apps/oshun/web/src` imports either enum**. `unowned` wants a
        capability and a lifecycle: the 6714-row capability inventory has **zero
        rows under `apps/oshun/web`**, because the inventory's `workspaceRoots`
        are the seven initiative domains and its manifest calls this app the
        `externalProxyHost`. The same scoping leaves the committed
        `routes.json` with 125 rows across eight hosts and **none for the host
        that mounts the workbench** — whose only complete enumeration is the
        gitignored `.next/app-path-routes-manifest.json`, which is why the
        contract has an `evidence-not-regenerable` verdict at all.

        **AND THE COVERAGE AXIS THE ESTATE FORCES.** Reachability here is real
        and small: `middleware-studio-boundary.test.ts` imports the exported
        `proxy` and calls it with a `NextRequest`, over **four** `/studio/isis`
        segments (one deliberately not on disk) of the 87. Discriminating and
        sampled are orthogonal, so the register carries both, and `forbidden`
        grades `evidence-sampled` once its absent inputs are supplied — routes
        nobody looked at must not pass by not being looked at.

        DECISIONS. **A DESCRIPTOR IS WHAT YOU CHECK, NOT WHAT YOU CHECK WITH**:
        `declared`, `source-tree` and `source-literal` all top out at
        `described`, enforced in the parser, so no amount of reading source
        produces an evidence side. Two of S11.1.a's twelve names are compounds
        and both are split — framework/proxy/middleware because the framework
        enumerates, the proxy decides and the matcher decides whether the proxy
        is consulted; allowed/forbidden because the allow-list is empty and
        enforcing while the 31-name catalog enforces nothing and misses 56 of the
        87 segments on disk. **A CLASS REGISTERED AS UNPRODUCED IS NOT A CLASS
        THE REGISTER FORGOT**, so `unproduced-class` and `unregistered-class` are
        separate faults with separate repairs. Readiness is ordered by REPAIR —
        an absent input before a misplaced altitude before a blunt one — and the
        two silent verdicts, `altitude-collapsed` and `vacuous`, are named
        because a check in either state runs, reports green, and has refuted
        nothing.

        TRAPS, both caught by the module's own machinery on its first runs. The
        parser refused **my own first register**: it placed the matcher and both
        pattern lists at `reachable` on the reasoning that they decide
        reachability, and reading a frozen array is not making a request. Then
        the scan refuted **my own "nothing imports the proxy"** — a grep run from
        the wrong working directory — when
        `apps/oshun/web/src/__tests__/middleware-studio-boundary.test.ts` does
        exactly that, which upgraded the estate's reachability supply from a
        runtime observation to a proxy evaluation and changed the sample from 2
        to 4. And a third: **the register's own prose tripped its own absence
        check** — writing "`parseRouteDescriptor` has no production caller" into
        the export-map rationale made a bare-name `git grep` report that sentence
        as the producer it denies, so the caller pattern now requires the open
        paren, because a file that NAMES a symbol is not a file that calls it.

        Also repaired here, because it blocked this entry point: the export-map
        register in `scripts/v1-workbenches/generate-workbench-kit-export-map.mjs`
        had gone five entry points stale (S10.14.b–f), so
        `evidence/v1-workbenches/workbench-kit-export-map.json` carried **10
        violations** and `public-api.spec.ts` was red on this branch. All six
        rows are now registered and the map regenerates to **182 entry points, 0
        violations**. -->

  - [x] S11.1.b Extract route discovery and normalization without Tara path/
        role/capability constants. Evidence: Tara and synthetic domain
        snapshots.

        <!-- S11.1.b: shipped as
        `libs/oshun/workbench-kit/src/route-discovery.ts` (`SEGMENT_KINDS` +
        `SEGMENT_ARITY`, `classifySegment`, `RouteConvention` with
        `NEXT_APP_ROUTER`/`NEXT_PAGES_ROUTER`, `MetadataRouteFile`,
        `normalizeRoutePath`, `matchRoutePath`, `discoverRoutes`,
        `collisionFindings` with 6 fault kinds, `routeSetDifference`,
        `shapeOfDeclaredPath`, `parseRouteSnapshot`, and four snapshots —
        `TARA_ROUTE_SNAPSHOT` plus three synthetics), 42 tests in
        `route-discovery.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-discovery-scan.mjs` (nx target
        `route-discovery-scan`,
        `pnpm run verify:operations:v1-route-discovery`, 9 negative controls),
        and the registrations a kit entry point needs.

        **A ROUTE TREE IS NOT A TREE OF ROUTES.** The Tara original is four
        lines — `routePath.replace('/studio/', '')` joined to `page.tsx` — and
        every one of the three things wrong with it survives the removal of the
        constants, because none of them is about Tara:

        - **A FILE THAT IS NOT A ROUTE.** 845 directories under
          `apps/oshun/web/src/app` yield 720 pages and 39 handlers; 19 files are
          `layout`/`loading`/`error`/`not-found`/`template` and eleven
          directories are private. The sharpest case is not in that tree at all:
          **six apps here have a `src/pages` directory that looks exactly like a
          Pages Router tree and is not one** — arete/web, demeter/web,
          hathor/workbench, isis/web, sophia/workbench, yemaya/studio-web, **256
          route-shaped files**, and the only thing that says so is the ABSENCE
          OF A `next.config` IN AN ANCESTOR, a file that is not in the tree being
          walked. BASE.1.d already paid for this: 329 routes before the gate,
          125 after.
        - **A SEGMENT THAT IS NOT IN THE URL.** `(workspace)` is a route group
          the framework deletes, and its own manifest records the mapping:
          `/(workspace)/workspace/[[...path]]/page` → `/workspace/[[...path]]`.
        - **A URL THAT IS NOT IN THE TREE.** Three optional catch-alls make
          whole subtrees infinite, so **the mounted set is unbounded and only
          the PATTERN set is finite** — "is this path mounted" is a match, never
          a lookup, and no `existsSync` can answer it.

        **A ROUTE SET HAS TWO ENDS, AND A CHECK THAT WALKS ONE OF THEM CANNOT BE
        SURPRISED.** The Tara spec iterates the map and asks the filesystem about
        each entry; it never iterates the filesystem. Measured 2026-08-15:
        **thirteen `page.tsx` files ship under `/studio/tara-workbench` and the
        route map declares eleven.** The two it does not know about are
        `programs/[programId]` and `sources/[sourceId]` — and the sibling
        `concepts/[conceptId]` IS declared, so this is not a convention against
        detail routes, it is two of three collections missing theirs. That map's
        own header calls it the single source of truth that "navigation, the
        studio hub, and (P12.2) the command palette all read", so two shipped
        surfaces are absent from the IA, the hub, and the palette.

        **THE MEASUREMENT THAT SETTLES WHETHER THE NORMALIZATION IS RIGHT.**
        There is a second implementation of it in this repository — the
        framework's — and it writes its answer down. Run over the real
        `apps/oshun/web/src/app` tree, **901 files across 845 directories, this
        module produces 772 URL patterns and `app-path-routes-manifest.json`
        contains the same 772**, with nothing in either direction. Getting there
        took one correction no amount of reading the tree would suggest: the
        first run agreed on 759 and missed thirteen, all of them files that are
        routes while sitting BESIDE `page.tsx` rather than instead of it — ten
        `opengraph-image`/`twitter-image` files, `sitemap.ts` served at
        `/sitemap.xml`, and the two reserved internal routes. That is
        `MetadataRouteFile`, and it is the first rule one level further down: a
        file that is not a route, until it is.

        DECISIONS. **A SLOT PAGE IS NOT A ROUTE**, unlike a group's child — the
        first draft gave `parallel-slot` a group's arity and
        `stories/[storyId]/@sidebar/page.tsx` was then reported as a duplicate
        URL, an invented collision. **TWO KEYS PER ROUTE**, because `/x/[id]` and
        `/x/[slug]` are different URL patterns and the same route: keeping only
        the pattern invents a collision, keeping only the shape cannot say which
        name to fix. **A RENAMED PARAMETER IS A NAMING FAULT AND NOT A MISSING
        ROUTE**, so `routeSetDifference` compares shapes — reporting it as both
        missing and extra sends a reader to create a file that exists. **THE
        HOST SUPPLIES THE FILE LIST** rather than the module reading a disk,
        which is what lets a synthetic domain be a literal instead of a fixture
        directory, and what makes "Evidence: Tara and synthetic domain
        snapshots" one code path over four inputs. Tara's tree uses only two of
        the eight segment kinds, so the three synthetics carry the rest: a group,
        a slot, an optional catch-all, a private directory, a non-route file and
        a foreign extension; one snapshot per collision class; and a Vite
        lookalike that reproduces the ancestor miscount.

        TRAP, and it is the one this repository has recorded before: the first
        draft used a **raw NUL byte** as the collision-key separator, which made
        the entire source file BINARY to `grep` — the mandatory adversarial stub
        scan then returned zero hits for exactly the same reason a clean file
        does. It is now `KEY_SEPARATOR = '\u0000'`, written as an escape, with
        the reason recorded beside it.

        **AND THE SAME BYTE HAD ALREADY EATEN THIS LEDGER.** Checking for it
        found a raw NUL inside the S10.2 note two thousand lines above -- the
        one that recorded the problem and said "33 files repo-wide still have
        this and are invisible to grep" -- so this 60,000-line file has itself
        been binary to `grep` ever since. Measured now: **43 tracked
        `.ts`/`.tsx`/`.mjs` files carrying 79 raw NUL bytes**, every occurrence
        a separator inside a string or template literal. All 79 are replaced
        with the `\u0000` escape, which is byte-identical at runtime and
        restores those files to the repo-wide searches the quality standards
        depend on. Every rewritten file was re-parsed with the TypeScript
        compiler -- 43 files, 0 syntax errors -- and the affected suites re-run.
        `libs/oshun/product-graph` has two failures (a manifest staleness hash
        and a signal count) which reproduce identically against the pre-change
        files and are unrelated to this work. -->

  - [x] S11.1.c Detect source-described versus effectively mounted missing
        routes. Evidence: deliberate missing-route test.

        <!-- S11.1.c: shipped as
        `libs/oshun/workbench-kit/src/route-mounting.ts` (`ABSORPTION_BOUNDS`,
        `MOUNT_VERDICTS`, `isAbsorbing`, `exemplarPath`, `mountFindings`,
        `PATH_VERDICTS` + `pathFindings`, `absorptionZones`,
        `undetectableRegions`, `withRouteRemoved`, `parseAbsorberRecord`,
        `OSHUN_WEB_ABSORBERS`, `OSHUN_WEB_REGISTER_PATHS`,
        `OSHUN_WEB_APP_LINK_CLAIMS` + `deadAppLinkClaims`), 24 tests in
        `route-mounting.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-mounting-scan.mjs` (nx target
        `route-mounting-scan`, `pnpm run verify:operations:v1-route-mounting`,
        7 negative controls), and the registrations a kit entry point needs.

        **A MISSING ROUTE IS A REQUEST THAT NOW GETS SOMETHING ELSE, AND ONLY
        SOMETIMES NOTHING.** Deleting a page has two outcomes and a detector that
        asks "does this path still resolve" scores them backwards. If nothing
        else matches, the request 404s: loud, monitored, reported. If a dynamic
        pattern above it matches, the request returns **200 with a different
        component**, and the surface is gone without a single error. The second
        is worse and it is the one a naive check calls healthy — so the question
        is not whether a pattern exists but **whether it refuses**.

        **FIVE PATTERNS IN `apps/oshun/web` ANSWER FOR MORE THAN ONE PATH, IN
        THREE CLASSES.** `/domains/[domainId]`, `/app/[surface]/[[...path]]` and
        `/d/[domain]/[[...path]]` each check their parameter against a named
        allow-list and call `notFound()` — **bounded-locally**, so a deletion
        under them stays visible. `/api/study/[...path]` validates nothing and
        returns the upstream's status — **bounded-elsewhere**, a real third class
        because the refusal exists and is not this repository's.
        `/workspace/[[...path]]` is **unbounded**: thirty-six lines, no
        validation, no `notFound`, any suffix joined into `resumePath` and the
        same `MultiPanelWorkspace` rendered, so `/workspace/anything/at/all` is a
        200. It is the one region of this estate where a deleted route cannot be
        detected as missing — and it contains **zero static routes today**, which
        is the only reason the check means anything there. Reported as a ZONE
        rather than as a clean bill of health for its contents, because the next
        page added under `/workspace/` is born inside it.

        **AT THE PATTERN LEVEL THIS ESTATE IS CLEAN AND THE WHOLE GAP LIVES AT
        THE PATH LEVEL.** 772 described against 772 mounted, nothing either way.
        Across every concrete path the app's registers name — the proxy's
        `PUBLIC_PATHS`, its eighteen `LEGACY_REDIRECTS` targets, and the
        navigation constants — 51 distinct paths: **44 answered by their own
        route, 4 absorbed by `/domains/[domainId]`, 3 that are prefix constants
        and not routes at all**. The four absorbed ones are `/domains/tara`,
        `/domains/veritas`, `/domains/nyx` and `/domains/arete`, and the
        absorption is **deliberate** — `DOMAIN_META` names all six navigable
        domains and an unknown id is `notFound()`. That is the whole reason the
        classes exist: a designed landing page and a swallowed deletion have the
        identical shape from outside, so a detector cannot tell them apart
        without being told which absorbers are allowed.

        **AND THE REGISTER WHOSE ERRORS LEAVE THE REPOSITORY.**
        `OSHUN_APP_LINK_PATHS` declares which URLs the native app claims, served
        to Apple and Google by the two `.well-known` handlers. Nineteen patterns,
        and **six have nothing behind them**: `/home`, `/share`, `/app`,
        `/domains` and `/v3` have no exact route, and `/v6/*` is a wildcard over
        a subtree with zero children. `/home` is the unambiguous one — no exact
        route, no children at any depth — and `WEB_SHELL_ROUTE_PATHS.home` is
        `'/'`, so the claim names a path the shell register itself spells
        differently. It is not entirely unhandled: `useDeepLinkHydration` maps
        `'/home'` to `'/'` in a **client-side** table beside `/explore`,
        `/activity`, `/library` and `/profile` — the only one of the five with no
        server route. So a visitor with the app installed gets a screen and a
        visitor without it gets a 404 served before any client code runs, which
        is S11.1.a's altitude lesson costing something outside the repository.

        **BOTH TESTS THAT GUARD THAT REGISTER COMPARE IT TO A COPY OF ITSELF.**
        `deep-link-associations.ts:111` builds the document as
        `paths: [...OSHUN_APP_LINK_PATHS]`;
        `deep-link-association-files.test.ts:41` asserts
        `expect(new Set(detail?.paths ?? [])).toEqual(new Set(OSHUN_APP_LINK_PATHS))`
        — `[...X]` equals `X` — and line 23 asserts the constant contains four
        of its own members. `deep-link-association-routes.test.ts` asserts the
        served document contains `['/d/*', '/domains/*', '/assistant/*', '/v3/*',
        '/v6/*']`, one of which covers an empty subtree. The register has exactly
        two consumers: the builder that copies it and the tests that check the
        copy. **Nothing anywhere asks whether any of the nineteen URLs resolves.**
        Same shape as the Tara route-integrity spec S11.1.b measured, in a second
        place, guarding a claim made to two app stores.

        DECISIONS. **THE DELIBERATE REMOVAL IS A FUNCTION**, not a hand-edited
        fixture: `withRouteRemoved` is called by the tests and by the scan, so
        the control is the same code path as the claim, and it **throws when the
        pattern was not there** — a control that silently removed nothing would
        pass for the same reason a working detector does. There are **two**
        missing-route tests because one is not enough: the same deletion must
        come out `described-not-mounted` where nothing absorbs and
        `described-but-absorbed` where something does, and a detector that passes
        the first and fails the second scores the silent case as healthy. **AN
        EXEMPLAR PATH IS `__param__`**, deliberately ugly, so an absorber that
        matches it matches because of its shape and not a coincidence of naming;
        an optional catch-all contributes nothing to its exemplar, which is the
        shortest path it accepts and the one most likely to collide with a static
        route above it. **THE ZONE IS REPORTED EVEN WHEN EMPTY**, because an
        empty unbounded zone is a property of the estate that the next commit can
        change. -->

  - [x] S11.1.d Detect duplicate effective method/path/host, alias collision,
        case/normalization collision, and redirect cycle. Evidence: duplicate
        fixture tests.

        <!-- S11.1.d: shipped as
        `libs/oshun/workbench-kit/src/route-collisions.ts` (`ROUTE_COORDINATES`,
        `EffectiveRoute` + `RouteOutcome`, `COLLISION_KINDS`,
        `collisionFindings`, `aliasCollisionFindings`, `followRedirects` +
        `chainFindings`, `parseProxyStages`, `OSHUN_WEB_PROXY_STAGES`,
        `OSHUN_WEB_METIS_UPLOAD_RULES`, `OSHUN_WEB_COLLAPSED_CHAINS`), 28 tests
        in `route-collisions.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-collisions-scan.mjs` (nx target
        `route-collisions-scan`,
        `pnpm run verify:operations:v1-route-collisions`, 8 negative controls),
        and the registrations a kit entry point needs. The case/normalization
        collision detector is S11.1.b's `collisionFindings`, which already
        reports `case-collision`, `duplicate-url` and `parameter-name-conflict`
        over a discovered tree; this cell adds the three the file tree cannot
        produce on its own.

        **TWO ROUTES COLLIDE WHEN A REQUEST COULD GO TO EITHER, AND THE
        COORDINATE THAT DECIDES IT IS USUALLY THE STAGE.** A middleware is not a
        redirect table. `apps/oshun/web/src/proxy.ts` is **seven ordered stages**
        over one request — rate limit, release scope, legacy redirects, canonical
        shell alias, authentication, the §24.11 studio boundary, the Metis upload
        alias — and each may answer and return. Two rules that would both fire do
        not both fire: the earlier wins and the later is **unreachable**, which
        is different from unused because nothing can test it.

        **THE ESTATE'S ONE ALIAS COLLISION IS THREE DEEP.** `/metis/upload` has
        three implementations of what happens to it:

        1. **Stage 2, release scope** — `resolveDeferredDomainFromRoute` matches
           on the prefixes `['', '/domains', '/d', '/api']`, so the **bare
           prefix** makes `/metis/upload` itself match the root `/metis`, and the
           gate returns a **307 to `/release-scope/metis`**.
        2. **Stage 7, `metisUploadAliasResponse`** — a **308 to `/metis/byom`**.
        3. **The page.** `apps/oshun/web/src/app/metis/upload/page.tsx` is a real
           mounted route whose whole body is `permanentRedirect('/metis/byom')`.

        **Only the first runs.** Metis is one of the two deferred domains and the
        gate is first on purpose — its own doc says why: "whether a room ships in
        this release is not a permission question, and answering it first keeps
        the refusal identical for everyone." So the other two are **dormant**
        rather than dead: the deferral is temporary by construction ("their route
        trees, components, and services all stay in the tree"), and both wake on
        the same day. They agree on `/metis/byom` today. **Nothing keeps that
        true, because no request reaches either of them.**

        **AND THE CHAIN THE ESTATE COLLAPSES ON PURPOSE.** `/news` and `/reader`
        are legacy aliases for `/domains/veritas`, and `veritas` is the other
        deferred domain — so read off the tables the chain is two hops,
        `/news` → `/domains/veritas` → `/release-scope/veritas`. The proxy
        resolves the **target's** deferral inside the redirect branch and emits
        one 307 with the reason written down: a 301 "would bake a deferred URL
        into a browser's cache". **A cycle detector over the redirect TABLE would
        report `/news` clean and would also be blind to the two hops it
        collapses**, because the graph that matters is the graph of stages.

        What the four detectors find on this estate: duplicate effective
        method/path/host **0** (19 redirect rules over 726 static routes, and no
        two handlers share a path — 39 real handlers, 15 GET-only, 21 POST-only,
        3 multi-method); alias collision **1**; case/normalization collision
        **0**; redirect cycle **0**.

        DECISIONS. **A DUPLICATE AND A SHADOW ARE DIFFERENT FINDINGS**, because
        the repairs differ — a duplicate is deleted, a shadowed rule is deleted
        OR moved earlier — and the finding is reported against the LATER rule,
        the one a reader has to decide about. **METHOD AND HOST ARE
        COORDINATES**: a GET and a POST at one path are two routes, and one path
        on two hosts is two routes unless an edge merges them (this app's one
        merge, the `/v1/:path*` rewrite to the BFF, has no local shadow — there
        is no `/v1` tree in the web app). **A CYCLE IS REPORTED BY THE REPEAT AND
        NOT BY EXHAUSTION**, so a long legitimate chain and a loop are told
        apart. **AND THE WALKER SORTS BY STAGE**, which is what makes it agree
        with the middleware instead of with the table. -->

  - [x] S11.1.e Detect host/audience/boundary forbidden routes including source-
        mounted but middleware-blocked or inadvertently enabled surfaces.
        Evidence: forbidden fixture tests.

        <!-- S11.1.e: shipped as
        `libs/oshun/workbench-kit/src/route-boundaries.ts` (`TERM_CONTROL` +
        `CALLER_REACHABLE`, `BoundaryTerm`/`BoundaryGuard`, `loadBearingTerms`,
        `BOUNDARY_FAULTS` + `boundaryFindings`, `selfDeclaredGateFindings`,
        `SURFACE_VERDICTS` + `surfaceFindings`, `parseBoundaryGuard`,
        `OSHUN_WEB_BOUNDARY_GUARDS`), 23 tests in `route-boundaries.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-boundaries-scan.mjs` (nx target
        `route-boundaries-scan`,
        `pnpm run verify:operations:v1-route-boundaries`, 8 negative controls),
        and the registrations a kit entry point needs.

        **A BOUNDARY IS A CONJUNCTION, AND ONLY THE TERMS THE CALLER CANNOT SET
        ARE THE BOUNDARY.** A guard reads as strong in proportion to how many
        conditions it names, and that is exactly backwards: every conjunct the
        requester controls is a condition they satisfy on the way in.

        **THE HOST AND AUDIENCE HALF HAS NO INPUT.** S11.1.a measured `HOSTS` and
        `AUDIENCES` as naming six each with no production importer under
        `apps/oshun/web/src`, so **zero** routes in this app declare either. The
        boundary half is all that can run.

        **ONE LOAD-BEARING TERM OUT OF FOUR.**
        `shouldBypassStudioIsisBoundaryForE2E` turns off the §24.11 hard block —
        the only thing between an ordinary session and 87 AAA-only pages — and it
        names four conditions:

        | term | who sets it |
        | ---- | ----------- |
        | `pathname.startsWith(STUDIO_ISIS_PREFIX)` | the caller |
        | `searchParams.get('__oshunStudioIsisLaneE2E') === '1'` | the caller |
        | `process.env.OSHUN_ENABLE_STUDIO_ISIS_LANE_E2E_BYPASS === 'true'` | **the deploy** |
        | `isLocalhostRequest(request)` | derived from the Host header |

        The fourth reads `request.nextUrl.hostname`, which a Next middleware
        derives from the request's Host header, and `next.config.mjs` pins no
        host — its only host-shaped setting is `allowedDevOrigins` for the dev
        server. So **three of the four arrive with the request and one does
        not.** The bypass is closed in production, and it is closed by one term.
        That is not a criticism of the guard; it is the number a reader needs,
        because four conditions look like four independent reasons and there is
        one. S10.14.c had already recorded this flag as granting a privilege and
        absent from the inventory; what is new here is which of its terms a
        caller controls.

        **AND ONE "GATE" WITH NO UNCONTROLLED TERM AT ALL.**
        `apps/oshun/web/src/app/studio/generation/living-scene/page.tsx` is in
        the proxy's `PUBLIC_PATHS`, so it is reached without a session. It
        renders `data-living-scene-route-entitlement-gate="route"` — the route
        asserting that it **is** the gate — and builds its whole access context
        from six `x-oshun-*` request headers (`tier`, `living-scenes-enabled`,
        `living-scene-templates`, `living-scene-frozen-workflow-classes`,
        `living-scene-locales`, `locale`). The proxy copies inbound headers
        verbatim — `new Headers(request.headers)` plus a request id and a locale
        — and a search of its 666 lines for `x-oshun` returns **nothing**: none
        of the six is set, stripped, or validated on the way in. It is the only
        route in the app that reads an `x-oshun-*` header.

        **THE SAME HEADER NAME IS DISTRUSTED ONE LAYER DOWN.**
        `libs/shared/bff-kit/src/entitlements.ts` reads `x-oshun-tier` and gates
        it on `!isProductionRuntime()`, with the reason written beside it: "in
        production a client can no longer self-assert 'pro'." Two layers read one
        header and one of them believes it. **What that buys a caller is
        bounded** — the template list is still intersected with
        `APPROVED_LIVING_SCENE_TEMPLATE_IDS`, `x-oshun-living-scenes-enabled`
        defaults to enabled so it can only close the surface, and the generation
        itself goes through the BFF where the header is production-gated. So this
        is **a gate that is not one, rather than a demonstrated escalation**, and
        the register says so in those words.

        Coverage, measured: **503 mounted `/studio/*` routes, 88 under the
        boundary prefix, 415 the boundary says nothing about**; and zero of the
        31 catalogued AAA capability names appear at any path outside
        `/studio/isis/`, so there is no name-shaped leak.

        DECISIONS. **A HEADER-DERIVED TERM IS ITS OWN CONTROL**, separate from
        `caller-set`, because it READS as server state and is not:
        `isLocalhostRequest` looks like a fact about where the process runs and
        is a fact about the request being examined, and filing it under
        `server-state` would count the bypass as resting on two terms instead of
        one. **`single-load-bearing-term` IS A FINDING AND NOT AN ERROR**,
        because a guard resting on one deployment-set term is a normal correct
        shape and what is not normal is believing it rests on four. **A
        SELF-DECLARED GATE IS A SEPARATE SUBJECT** from a guard in a pipeline,
        because the assertion is the reason to look. **AND THIS MODULE DOES NOT
        DECIDE WHETHER A BOUNDED GUARD IS STRONG ENOUGH** — that is a judgement
        about threat and it is the host's; it reports how many of the reasons are
        reasons. -->

  - [x] S11.1.f Detect missing/unknown owner, capability, contract/version,
        audience, lifecycle, and deprecation/migration metadata. Evidence:
        ownership fixture tests.

        <!-- S11.1.f: shipped as
        `libs/oshun/workbench-kit/src/route-metadata.ts`
        (`ROUTE_METADATA_CLASSES` — eight, both compounds split —
        `METADATA_RESOLUTIONS` + `CAN_REPORT_UNKNOWN`, `MetadataSupply`,
        `METADATA_FAULTS` + `metadataFindings`, `MetadataFieldCensus` +
        `slotFindings`, `parseMetadataSupply`, `OSHUN_WEB_ROUTE_METADATA`,
        `OSHUN_WEB_METADATA_FIELDS`, `OSHUN_WEB_CANONICAL_MISMATCHES`), 22 tests
        in `route-metadata.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-metadata-scan.mjs` (nx target
        `route-metadata-scan`, `pnpm run verify:operations:v1-route-metadata`,
        7 negative controls), and the registrations a kit entry point needs.

        **A ROUTE'S METADATA SLOT IS FULL, AND EVERY FIELD IN IT IS ADDRESSED TO
        A MACHINE THAT IS NOT US.** The framework gives every page a metadata
        export and this estate fills it: **651 of 720 pages export a `metadata`
        object**, six more compute one with `generateMetadata`. Its fields,
        counted exhaustively: `title` 651, `description` 648, `alternates` 620,
        `robots` 35, `openGraph` 15, `twitter` 4, `keywords` 1 — a crawler's
        brief and a link preview's, without exception. Not one of the eight
        classes appears in any of them, and **no page exports a home-grown route
        constant either**: a sweep for a screaming-case export over every page
        returns nothing, and the only route segment config in use is `dynamic`,
        on four pages. The nearest thing to an audience declaration is
        `robots: { index: false }`, on **36** pages — the only per-route
        statement about who may see a surface, and it is addressed to a search
        engine.

        That pairing is the finding. Eight zeroes on their own would leave a
        reader unable to tell "nobody writes route metadata" from "everybody
        writes route metadata and none of it is this", and the repairs differ.

        **A DEFAULT IS NOT AN ANSWER; IT IS THE ABSENCE OF ONE WEARING AN
        ANSWER'S CLOTHES.** Owner is the single class that resolves for every
        route, and it resolves because `.github/CODEOWNERS` opens with
        `* @GreyChimp` — a catch-all over every path. A checker that scored
        resolutions reports owner **720 of 720** and is measuring a wildcard.
        **"Unknown owner" is not a state this estate can be in**, which is why
        `MetadataResolution` separates `defaulted` from `declared` and why the
        finding is `resolution-is-a-default` rather than a pass.

        Of the eight: **one resolves by default and cannot say "nobody"; one —
        capability — has a 6714-row register with zero rows under
        `apps/oshun/web`; and six have no producer at all.** The types exist in
        the kit (`HOSTS`, `AUDIENCES`, `LIFECYCLES`, `SemanticVersion`,
        `redirectTo`) and `parseRouteDescriptor`, which is where a route would
        carry them, has no production caller.

        DECISIONS. **`contract` AND `contract-version` ARE SEPARATE** because the
        descriptor carries the second and names the first nowhere — the estate
        can state a version of a thing it never names. **`deprecation-notice` AND
        `migration-target` ARE SEPARATE** because `lifecycle: 'deprecated'` and
        `redirectTo` are independent, and the announcement without the exit is a
        real state; the estate does move callers — eighteen legacy redirects and
        two canonical prefixes — and it does so in the proxy, where the route
        being left behind says nothing about where its callers went.
        **`alternates.canonical` IS THE ONE FIELD ANOTHER ROUTE COULD
        CONTRADICT**: 614 declare one and **exactly one differs from its own
        path** — `/workspace/[[...path]]` declaring `/workspace`, which is
        correct, because an optional catch-all renders one page for an unbounded
        set of URLs and saying so is what the field is for. A second
        disagreement would be a route calling itself a duplicate of another, and
        nothing else in this estate can express that.

        TRAP, caught by the scan on its own first run: this register's census
        counted `robots` with a whole-file grep (**39**) where a census counts a
        top-level metadata field (**35**), and `index: false` counts **36** —
        three different questions collapsed into one number. All three are now
        recorded separately and the scan recounts each from the tree. -->

  - [x] S11.1.g Produce source/actionable failure diff and run Tara plus two
        archetype fixtures. Evidence: parameterized suite.

        <!-- S11.1.g: shipped as
        `libs/oshun/workbench-kit/src/route-suite.ts` (`DIFF_ACTIONS`,
        `DiffEntry` with its `blocks` leverage count, `parseDiffEntry` +
        `diffFindings`, `SuiteDomain`/`SuiteCell`, `runSuite`, `suiteDiff`,
        `checkCoverage`, and the three domains `TARA_DOMAIN`,
        `STORY_DESK_DOMAIN`, `INCIDENT_CONSOLE_DOMAIN`), 21 tests in
        `route-suite.spec.ts`,
        `libs/oshun/workbench-kit/tools/route-suite-scan.mjs` (nx target
        `route-suite-scan`, `pnpm run verify:operations:v1-route-suite`, 6
        negative controls), and the registrations a kit entry point needs.

        **THE TWO SYNTHETIC DOMAINS CARRY THE METADATA THE REAL ONE DOES NOT.**
        `STORY_DESK_ROUTES` and `INCIDENT_CONSOLE_ROUTES` are the only route
        descriptors in this repository — **seven between them** — and every one
        states an owning capability, a contract version, a lifecycle, an audience
        set, a host, a scope binding, a data classification, a redirect target
        and a flag list, each built through `parseRouteDescriptor` under
        `expectOk`, which is what makes them descriptors rather than object
        literals shaped like descriptors. Tara — the quality exemplar, the real
        domain, the one this whole ledger is measured against — states a path, a
        purpose, a surface, an `ownerRole` and a boolean. **Run the same four
        checks over all three and the fixtures are the only ones that can
        answer.**

        That is not a defect in the fixtures and not a scolding of Tara. It is
        what a parameterized suite is FOR: the archetypes are what a supplied
        domain looks like, and running them beside the real one is the only way
        to see that the real one is not supplied. A suite over Tara alone reports
        four unsupplied checks with nothing to compare them against.

        **THE MATRIX, MEASURED.** No check is runnable on any of the three, and
        the domains block on **different** classes — which is the whole reason
        the cell asks for three:

        | | missing | duplicate | forbidden | unowned |
        | --- | --- | --- | --- | --- |
        | tara | `framework-route` | `framework-route`, `redirect` | + `host`, `audience` | + `capability`, `lifecycle`, `owner` |
        | story-desk | + `app-source-root` | `framework-route` | (no host/audience gap) | `owner` only |
        | incident-console | + `app-source-root` | `framework-route` | (no host/audience gap) | `owner` only |

        Tara has a file tree and a register and no metadata; the archetypes have
        complete metadata and no tree. The mirror image, exactly. And the two
        archetypes block identically, which is the control: they share no route
        path, so a difference between them would be a harness bug rather than a
        fact about either.

        **A FAILURE DIFF IS ACTIONABLE ONLY IF IT NAMES SOMETHING SOMEBODY CAN
        OPEN.** "Route X is missing" is a fact; "X is declared at
        `StudioTaraWorkbenchRouteMap.ts` and no file under
        `app/studio/tara-workbench` produces it" is a repair, because it names
        both ends and the reader knows which one to change. Every entry carries a
        SOURCE that must look like a repo-relative path — `parseDiffEntry`
        refuses "the route map" or "the proxy", a name for a thing rather than a
        place — and a REMEDY that must describe a change. Over the three domains
        the diff has **25 entries**, and the three at the top are the same class
        for each domain: `framework-route`, **unblocking 4 of 4 checks apiece**,
        against twenty-two that unblock one.

        DECISIONS. **THE OUTPUT IS A MATRIX AND NOT A VERDICT**, because a
        boolean reports the same green for a domain that passes every check and
        one that can run none. **ONE ENTRY PER DOMAIN PER BLOCKING CLASS**, not
        per failing cell, because a domain that supplies a route descriptor
        unblocks two checks at once and a diff listing checks would send its
        reader round one repair twice. **THE LEVERAGE COUNT IS A FACT ABOUT THE
        WHOLE MATRIX** and is computed before any entry is built. -->

- [ ] S11.2 Parameterize a live-data/full-journey Playwright scaffold. Evidence:
      fixtures are opt-in and visibly labeled.
  - [x] S11.2.a Define workbench fixture for host/base URL, actor/tenant/roles,
        capability probes, data reset/seed/read-back, critical routes/journeys,
        state selectors, and evidence output. Evidence: scaffold interface.

        <!-- S11.2.a: shipped as
        `libs/oshun/workbench-kit/src/journey-scaffold.ts` (`SCAFFOLD_SIDES`,
        `SCAFFOLD_SLOTS` + `SCAFFOLD_SLOT_SPECS`, `INSTALLATION_SHAPES` +
        `SHAPE_DECLINE_GRAIN` + `DECLINABLE_SHAPES`, `INTERPOSITION_SITES` +
        `SITE_APP_OBSERVABILITY` + `APP_INVISIBLE_SITES`, `LABEL_CHANNELS` +
        `CHANNEL_REQUIRES_APP_KNOWLEDGE` + `labelChannelsAvailableFor`,
        `READ_BACK_PATHS`, `ABSENCE_POLICIES`, `parseScaffoldSlotSupply`,
        `SCAFFOLD_FAULTS` with 8 kinds, `scaffoldSupplyFindings`,
        `scaffoldSupplyCensus`, `SCAFFOLD_JOURNEYS` +
        `resolveJourneyReadiness`/`resolveAllJourneyReadiness`,
        `TARA_E2E_SCAFFOLD_SUPPLY`, `CENTRAL_E2E_SCAFFOLD_SUPPLY`,
        `OSHUN_FIXTURE_LABEL_REACH`), 114 tests in `journey-scaffold.spec.ts`,
        `libs/oshun/workbench-kit/tools/journey-scaffold-scan.mjs` (nx target
        `journey-scaffold-scan`, `pnpm run verify:operations:v1-journey-scaffold`,
        12 negative controls), and the five registrations a kit entry point
        needs.

        **A SUBSTITUTE IS INSTALLED ON ONE SIDE OF THE GLASS AND LABELLED ON THE
        OTHER.** The parent's evidence clause — "fixtures are opt-in and visibly
        labeled" — reads as one demand with two adjectives. It is two demands on
        two different parties, and each fails here for a structural reason rather
        than by oversight:

        - **Opt-in is a property of the INSTALLATION SHAPE, not of the intent.**
          Playwright has exactly one shape a test can decline: the option fixture
          `[default, { option: true }]`, settable by `test.use()`. A `page`
          override, an `auto` fixture, an `afterEach`, a project `use:` block, a
          `globalSetup` and a `webServer` env var are installed for every test in
          their scope and no test can refuse them.
          `apps/tara/web/e2e/fixtures.ts` contains **both shapes within thirty
          lines of each other**: `skipAxe` arrives from
          `libs/shared/testing/src/playwright/axe.ts` as
          `[false, { option: true }]` and any spec may switch the accessibility
          scan off, while the **seven `context.route` handlers** that decide what
          the product is shown are installed by overriding `page` itself, so all
          **ten spec files** under `e2e/tests` receive them and none can refuse.
          The declinable one is the accessibility check; the undeclinable one is
          the fixture data.
        - **Visible labelling is a property of the INTERPOSITION SITE, not of the
          product's willingness.** `route.fulfill` replaces the response BENEATH
          the application, which has no fact available to it saying otherwise.
          This estate's badge — `FixtureBadge` in `CapabilityBanner.tsx`,
          rendering `data-fixture-badge` / `data-fixture-source` /
          `data-fixture-version` — is driven by a `fixtureSource` **prop**, so it
          can only ever announce a substitution the application performed itself,
          and a scaffold's substitutions are precisely the ones it cannot see.
          `labelChannelsAvailableFor` DERIVES that from the observability table
          and the app-knowledge table rather than asserting it.

        **AND THE ESTATE PROVES IT FROM THE OTHER SIDE**, which is why the
        conclusion is structural and not a complaint. There IS a working fixture
        label in this product, on a mounted route: `/operator/metis` asks the BFF
        for OneRoster with `timeoutMs: 3000`, returns `getMetisOperatorFixture()`
        when nothing comes back, and renders
        `data-operator-metis-oneroster-source`, typed `'fixture' | 'bff'` and set
        to the first by the fixture and the second by `adaptOneRosterStatus`. It
        works because the substitution is the page's OWN. **The badge built for
        the general case is unreachable** — `apps/oshun/web/src/components/
        workbench/` has zero importers outside its own directory — **and the
        annotation built for one specific fallback is live.**

        **A READ-BACK THROUGH THE CHANNEL THAT WROTE IT CONFIRMS THE WRITE AND
        NOTHING ELSE.** `apps/tara/web/e2e/auth.setup.ts` plants a session with
        `page.addInitScript` into `localStorage`, then asserts
        `expect.poll(() => localStorage.getItem(key)).toBe(JSON.stringify(
        persistedAuthState))` — the exact value it just wrote, out of the exact
        store it wrote it to. The suite's only real login,
        `'authenticate as free user'`, is `setup.skip(true, ...)`.

        Measured 2026-08-15, **the two exemplar scaffolds fail oppositely**,
        which is why both are registered. Tara substitutes the actor, the roles
        and the seed — all three invisible to the app, none labelled — so
        `authenticated-navigation` grades **fabricated**. `testing/e2e`
        substitutes nothing and grades **unverified**, because an absent
        `TEST_USER_EMAIL` makes its setup log "creating empty auth state", write
        the storage state and return **green**, and its `globalSetup`
        `console.warn`s a failed health check and continues. **Neither suite
        fills tenant, capability-probe, data-reset or data-read-back**, so
        neither of S11.2.g's two exemplar journeys is runnable on either — and
        **neither has an opt-in switch at all**: no `PLAYWRIGHT_LIVE`, `E2E_LIVE`,
        `USE_FIXTURES` or any sibling appears in either tree, so "opt-in" has no
        mechanism to be true of yet. -->

  - [x] S11.2.b Provision unique nonfixture tenant/account/data through real
        service boundaries and verify source values before UI use. Evidence:
        setup/teardown tests.

        <!-- S11.2.b: shipped as
        `libs/oshun/workbench-kit/src/journey-provisioning.ts`
        (`PROVISIONED_SUBJECTS` × `PROVISIONING_ACTS`, `BOUNDARY_KINDS`,
        `AUTHORITY_KINDS` + `AUTHORITY_ORIGINS`, `IDENTITY_SOURCES`,
        `READ_BACK_MODES`, `RESIDUE_KINDS`, `parseProvisioningStep`,
        `resolveProvisioningChain`/`resolveAllProvisioningChains` over five
        `CHAIN_VERDICTS`, `PROVISIONING_FAULTS` with 9 kinds,
        `provisioningFindings`, `cleanupPathFindings`, `provisioningCensus`,
        `authorityAsymmetries`, `OSHUN_PROVISIONING_REGISTER`,
        `OSHUN_AUTHORITY_SUPPLY`, `OSHUN_CROSS_PROCESS_CONTROLS`,
        `OSHUN_CLEANUP_PATHS`), 109 tests in `journey-provisioning.spec.ts`
        organised as the SETUP / VERIFY / TEARDOWN questions the evidence clause
        asks, `libs/oshun/workbench-kit/tools/journey-provisioning-scan.mjs` (nx
        target `journey-provisioning-scan`,
        `pnpm run verify:operations:v1-journey-provisioning`, 14 negative
        controls), and the five registrations a kit entry point needs.

        **EVERY PROVISIONING ACT IS DECIDED IN THE SERVER'S PROCESS, AND THE
        HARNESS'S ONLY LEVER IS A REQUEST.**

        **A GRANT IS ISSUED, NOT REQUESTED — AND THIS ESTATE HAS TWO REAL
        BOUNDARIES WITH NO LINK BETWEEN THEM.** `POST /v1/tenants/onboard` is a
        genuine tenant provisioner: `originGuard`, `csrfGuard`,
        `collectionsAuthGuard`, contract validation, and
        `tenantOnboardingStore.onboardDurably`. It admits an actor only through
        `hasTenantOperatorScope` — `admin:*` or `admin:workspace:tenant-console`.
        And `buildAuthSession`, the single function through which every signup
        and every login issues a token, writes `scopes: ['domain:*']` as a
        literal taking no input — **the only `scopes:` literal in
        `customer-auth-store.ts`**. So no account this estate can create can
        create a tenant, and the gap is not a missing permission model but a
        missing edge.

        **AN AUTHORITY YOU CAN MINT IS NOT AN AUTHORITY YOU WERE GRANTED.**
        `createDevAuthToken` is `` `dev.${toBase64Url(JSON.stringify(payload))}` ``
        — unsigned, so any scope set can be written by hand. The estate closes
        that correctly: `areDevTokensAllowed` returns `false` whenever
        `NODE_ENV`, `OSHUN_ENV` or `RUNTIME_ENV` is `production`, refusing the
        token `401 dev_tokens_disabled`. Which is exactly why a provisioning
        helper built on one is a trap — it works on a developer's machine and the
        first environment that matters refuses it with a status the journey reads
        as a product defect.

        **A SETUP THAT FALLS BACK IS NOT A SETUP.**
        `apps/oshun/web/src/app/api/dev/seed-session/route.ts` is the only
        account provisioner a test can call and it is real: it posts
        `/v1/auth/signup` to the BFF over HTTP and sets both session cookies. Its
        email defaults to the constant `'e2e@oshun.local'` and a `409` falls back
        to `/v1/auth/login` with the same fixed password — which its own doc
        calls idempotent, and which makes a **unique** account unobtainable
        through it.

        **FOUR CONTROLS ARE SET ON THE WRONG SIDE.** (1) The seeder assigns
        `process.env.OSHUN_DEV_AUTO_VERIFY_EMAIL = 'true'` in the **Next**
        process and `customer-auth-store.ts` reads it in the **BFF** process,
        across the `fetch` in `forwardJsonRequest` — the line is inert, and the
        behaviour it claims to cause belongs to `apps/oshun/bff/project.json`,
        whose dev target sets the variable on the server's command line. (2) The
        plan is `resolveDevSignupPlan()` reading `OSHUN_DEV_SIGNUP_PLAN` from the
        BFF's environment, so a harness needing a `premium` actor cannot ask for
        one. (3) Whether an immediate deletion erases is decided at boot by a
        Redis URL **and** an attestation signer; without both the request is
        honestly left `scheduled` for `GRACE_PERIOD_DAYS = 30`. (4)
        `allowFixtures()` is `!isProductionRuntime()`, so outside production the
        tenant roster concatenates three fixture tenants onto the onboarded ones
        **with no field telling them apart**.

        THE MATRIX, computed: **three of five subjects are provisionable end to
        end** (account, session, domain-record). `tenant/create` is
        `authority-self-asserted` and `tenant/destroy` is `no-boundary`, because
        `TenantOnboardingStore` has `onboardDurably` and **no removal of any
        kind** — its only deletion vocabulary is `deletedActorDigests`, a DSAR
        fence on the operator who onboarded. `authority/create` is `no-boundary`.
        **No subject but `session` can be reset**, and the harness's own cleanup
        — the `CLEANUP_TEST_DATA` branch of
        `testing/e2e/setup/global-teardown.ts` — announces
        `'Cleaning up test data...'` over a body that is one comment. -->

  - [x] S11.2.c Keep fixture providers/data/intercepts opt-in per test with
        persistent visible badge and metadata. Evidence: fixture mode tests.

        <!-- S11.2.c: shipped as
        `libs/oshun/workbench-kit/src/fixture-mode.ts` (`FIXTURE_CLASSES` +
        `CLASS_INSTALLED_IN`, `FIXTURE_GATES` + `GATE_DECIDER` + `GATE_GRAIN` +
        `GATE_MAY_FIRE_IN_PRODUCTION` + `TEST_SETTABLE_GATES`, `ANNOUNCEMENTS` +
        `ANNOUNCEMENT_REACHES_RENDERER` + `canFeedLabelContract` +
        `labelContractFeeds`, `parseFixtureGateSupply`, `FIXTURE_MODE_FAULTS`
        with 8 kinds, `fixtureModeFindings`, `fixtureModeCensus`,
        `resolveOptInReadiness`/`resolveAllOptInReadiness` over six
        `OPT_IN_READINESS` states, `OSHUN_FIXTURE_GATE_SUPPLY`,
        `OSHUN_LABEL_CONTRACT_FEEDS`, `OSHUN_REQUEST_CHANNEL`), 109 tests in
        `fixture-mode.spec.ts`,
        `libs/oshun/workbench-kit/tools/fixture-mode-scan.mjs` (nx target
        `fixture-mode-scan`, `pnpm run verify:operations:v1-fixture-mode`, 14
        negative controls), and the five registrations a kit entry point needs.

        **THE LABEL CONTRACT IS COMPLETE, AND ITS FIRST FIELD IS THE ONE NOBODY
        IN THIS ESTATE CAN SUPPLY.** Half of this cell was already built and
        built well: S7.3.c's `FixtureLabelling` carries seven fault kinds,
        `REQUIRED_LABEL_SURFACES` insists the badge leaves the banner for the
        working context, and it refuses a label without a source, without a
        version, dismissible, or signalled by colour alone. It begins
        `readonly fixtureActive: boolean` — and **nothing outside
        `capability-banner.spec.ts` has ever constructed one**, so not one of the
        seven has ever been asked about a real surface. The estate says why in
        its own words: `useCapabilityProbe` sets the origin from
        `transport.token === null ? 'unauthenticated-probe-response' :
        'authenticated-probe-response'` under the comment *"The origin is a fact
        about the REQUEST, decided here and not inferred from the answer: a 200
        says nothing about whether a credential was sent."* Exactly so — and the
        same sentence forbids the client ever naming `fixture-file`,
        `demo-dataset` or `developer-override`. **Seven of the nine
        `LIVE_CLAIM_ORIGINS` have no producer, and the three that name a
        substitution are among them.**

        **"OPT-IN" PRESUPPOSES ONE SWITCH, AND THIS ESTATE HAS FIVE GATES ON ONE
        CLASS OF THING.** Seventeen `*_FIXTURE` constants in `domain-stubs.ts`:
        **10** through `guardedFixtureRoute` (`503 not_configured` in
        production); **2** through `jsonRoute`, which has no condition at all and
        therefore answers in production; **2** behind `allowFixtures()` =
        `!isProductionRuntime()`, both of which **concatenate** onto live rows
        with no field telling them apart; **1** behind
        `input.includeFixture ? ARETE_OFFERINGS_FIXTURE.offerings : []` — the
        only gate *shaped* to take an outside answer, handed
        `includeFixture: allowFixtures()`; and **2** behind `if (!pg)`, which
        serve whenever Postgres is unreachable, in any environment, precisely
        when a reader most needs telling. Five gates, four deciders — the
        deployment, nobody, a dependency's health, and one line of in-process
        code passing the deployment's answer along — and **not one of them is the
        request**.

        **THE ONLY THING THAT CROSSES ALL THREE PROCESSES IS THE REQUEST.** A
        provider is chosen in the service, data in the renderer's layer, an
        intercept in the Playwright process. The channel already exists and is
        used heavily — **the BFF reads 45 distinct `x-oshun-*` request
        headers** — and not one is about fixtures.

        WHAT IS ALREADY RIGHT, AND PROVES THE REST BUILDABLE: `/operator/metis`
        falls back to `getMetisOperatorFixture()` when the BFF returns nothing
        and renders `data-operator-metis-oneroster-source`, typed
        `'fixture' | 'bff'`. It works because the **renderer** made the choice.
        And **its forty-four siblings are the same shape** — `getAreteHabits`
        asks `/v1/arete/habits` and returns `getAreteHabitsFixture()` exactly as
        `getMetisOperator` does, with no field to say so. The difference is not a
        mechanism, a boundary or a permission: one of them wrote the fact down.

        Counted by how each gate is **wired** rather than by its shape (the one
        `caller-parameter` gate is production-safe because its argument is
        `allowFixtures()`), **49 of the estate's 69 substitutable subjects can
        answer a production request, and none of the 49 leaves a trace.** All
        three classes grade short of opt-in per test: provider
        `deployment-scoped`, data `undeclinable`, intercept `run-scoped`. -->

  - [x] S11.2.d Fail when interception, hard-coded fallback, demo seed, empty
        success, or build-time guess masquerades as live. Evidence: anti-mock
        negative tests.

        <!-- S11.2.d: shipped as `libs/oshun/workbench-kit/src/anti-mock.ts`
        (`MASQUERADE_CLASSES` + `ABSENCE_CLAIMED`, `VANTAGE_POINTS`,
        `EVIDENCE_KINDS` + `EVIDENCE_VANTAGE` + `EVIDENCE_IS_SHAPE_ONLY` +
        `POSITIVE_TRACE_EVIDENCE`, `ANTI_MOCK_CHECKS` + `antiMockCheck`,
        `parseEvidenceSupply`, `resolveAntiMockReadiness`/
        `resolveAllAntiMockReadiness` over five `ANTI_MOCK_READINESS` states,
        `ANTI_MOCK_FAULTS` with 6 kinds, `antiMockFindings`, `antiMockCensus`,
        `OSHUN_ANTI_MOCK_EVIDENCE`, `OSHUN_INVERTED_INSTRUMENTS`), 76 tests in
        `anti-mock.spec.ts`,
        `libs/oshun/workbench-kit/tools/anti-mock-scan.mjs` (nx target
        `anti-mock-scan`, `pnpm run verify:operations:v1-anti-mock`, 13 negative
        controls), and the five registrations a kit entry point needs.

        **EVERY ONE OF THE FIVE MASQUERADES IS A CLAIM ABOUT WHAT DID NOT
        HAPPEN, AND A RESPONSE ONLY RECORDS WHAT DID.** An interception means no
        request left the browser; a hard-coded fallback means no query ran; a
        demo seed means nobody created that row in this run; an empty success
        means nothing was asked; a build-time guess means nothing has been
        observed since the build. Not one leaves a positive mark in the artifact
        a test looks at — the 200 is a real 200, the body is well-formed, the
        schema validates, the screenshot is correct. So an anti-mock check cannot
        be a predicate over the response; it has to be a predicate over the
        response **and a record of what should have happened**, which is exactly
        what S11.2.b's read-back and S11.2.c's announcement exist to produce.

        **THE SAME OBSERVATION IS A DEFECT AND A CORRECT ANSWER, AND ONLY WHAT
        YOU PUT THERE TELLS THEM APART.** This ledger asks for both directions
        inside one section: S11.2.d wants an empty success masquerading as live
        to FAIL, and **BASE.7.d** wants a legitimate empty result exercised with
        the UI verified not to substitute sample content — so an empty answer
        must also stand. The two responses are byte for byte the same. A check
        that fires on emptiness fails the second requirement to satisfy the
        first, so `empty-success` is refutable by `provisioned-read-back` and by
        nothing else.

        FOUR OF THE SIX INSTRUMENTS EXIST HERE, AND EVERY ONE IS POINTED
        ELSEWHERE. `response.serverAddr()` returns `null` for a fulfilled route
        and an address for a served one — the exact positive trace an
        interception erases — and is **called nowhere**, while
        `fromServiceWorker()` is called **twelve times** across ten files under
        `apps/oshun/web/e2e` and every one asserts `toBe(true)`: the correct
        assertion for an offline-shell test and the exact opposite of an
        anti-mock one. `waitForRequest`/`page.on('request')` appear in
        **fourteen** files there and **one** across the two exemplar journey
        suites. S1.13.d's whole runtime fixture boundary —
        `SYNTHETIC_SEGMENT_PREFIX`, `findSyntheticValues` with declared depth and
        node bounds, `assertNoSyntheticValues` throwing `SyntheticValueError`,
        and no `stripSynthetic` because a half-fixture value is one nobody can
        reason about — has **no consumer under `apps/` at all**, and not one of
        the sixty-nine substitutable subjects S11.2.c counted carries the prefix.
        The response annotation has one producer, `/operator/metis`, which
        neither exemplar suite visits.

        AND THE TWO IT DOES NOT HAVE: a **provisioned read-back** before UI use
        (S11.2.b measured zero in both suites, and it is the only instrument that
        can grade `empty-success`), and a **build stamp** — no build id, commit
        sha or bundle timestamp reaches any response from `apps/oshun/web` or
        `apps/oshun/bff`.

        SO OF THE FIVE, **exactly one is refutable in a journey suite today** —
        `interception`, and on the weaker of its two instruments: `waitForRequest`
        proves a request left, `serverAddr()` would prove who answered, and the
        suites produce the first. `hard-coded-fallback` and `build-time-guess`
        grade `instrument-elsewhere`; `demo-seed` and `empty-success` grade
        `instrument-unused`.

        TRAP, caught by the scan on its first run: this register said ten
        `fromServiceWorker()` call sites because I had read a `head -10`. The
        real count is twelve, across ten files. **A count taken from a truncated
        listing is a count of the listing.** -->

  - [x] S11.2.e Provide reusable authenticated navigation, direct-link, network/
        console capture, save/read-back, job wait, gate/review/publish, failure,
        and cleanup helpers. Evidence: example journeys.

        <!-- S11.2.e: shipped as
        `libs/oshun/workbench-kit/src/journey-helpers.ts` (`HELPER_FAMILIES` +
        `HELPER_PROMISE` + `ACCEPTABLE_SIGNALS`, `SIGNAL_KINDS` +
        `SIGNAL_ESTABLISHES_A_FACT` + `ABSENCE_SIGNALS`,
        `TIMEOUT_DISPOSITIONS`, `parseHelperSupply`, `resolveHelperReadiness`/
        `resolveAllHelperReadiness` over five `HELPER_READINESS` states,
        `HELPER_FAULTS` with 6 kinds, `helperFindings`, `helperCensus`,
        `OSHUN_JOURNEY_HELPER_SUPPLY`, `OSHUN_SHARED_WAITS`), 66 tests in
        `journey-helpers.spec.ts`,
        `libs/oshun/workbench-kit/tools/journey-helpers-scan.mjs` (nx target
        `journey-helpers-scan`, `pnpm run verify:operations:v1-journey-helpers`,
        13 negative controls), and the five registrations a kit entry point
        needs.

        Read the parent's list once and every entry is the same shape: do a
        thing, then wait until it has happened. **So the reusable part is not the
        doing. It is the waiting, and a helper is only as good as the signal it
        stops on.**

        **A SIGNAL WHOSE TRUTH CONDITION IS AN ABSENCE IS SATISFIED SOONEST BY
        THE THING THAT NEVER HAPPENED.** `waitForLoadState('networkidle')`
        resolves when there has been no network activity for a moment — a fact
        about the page having stopped asking, not about it being ready. A page
        whose requests were all fulfilled from the test process settles sooner
        than one that made round trips, and a page that asked for nothing is idle
        immediately, so **the estate's default readiness wait is fastest exactly
        when S11.2.d's masquerades are present.** It cannot tell "everything
        arrived" from "nothing was requested", because those are the same
        observation. It is reached from **23 call sites** across the Tara suite's
        page objects, **eight of them in `auth.page.ts`**.

        **A WAIT THAT SWALLOWS ITS TIMEOUT IS A SLEEP**, and
        `apps/tara/web/e2e/pages/base.page.ts` states the distinction twice in
        one file: `isVisible()` wraps a `waitFor` in a `try` and returns `false`
        on throw — **correct**, it is a predicate and both answers are answers —
        while `waitForLoadingComplete()` does
        `loadingSpinner.waitFor({ state: 'hidden', timeout: 30000 }).catch(() => {})`
        under the comment "Loading spinner might not exist, which is fine", so it
        returns whether the spinner went away or thirty seconds elapsed with it
        still turning; `waitForToastDismiss()` is the same shape at ten. The
        author knew the distinction — the predicate proves it — and two of the
        three waits are on the wrong side of it.

        **A RECORDER THAT WRITES NOWHERE OBSERVED AND DID NOT RECORD.** Both
        capture call sites subscribe with `page.on(...)` and hold what they hear
        in a local, and S11.2.a measured that neither suite calls
        `testInfo.attach` at all.

        WHAT THE TWO EXEMPLAR SUITES ACTUALLY WAIT ON, counted: `toBeVisible`
        **683**, `toHaveURL` **61**, `networkidle` **26**, and one call site
        apiece for `page.on('console')`, `page.on('pageerror')`,
        `page.on('request')` and `waitForResponse`. `expect.poll` and
        `waitForRequest`: **zero**. **Four call sites in total observe anything
        the network did**, against 744 that watch the DOM and the address bar.

        THE MATRIX: **two of nine families establish their promise** —
        `direct-link` (and only because `page.goto`'s own default load event does
        the work, not because anything chose it) and `failure-surface`. One waits
        on an absence, three are weaker than their promise, and **three —
        `job-wait`, `gate-publish` and `cleanup` — are served by nothing at
        all**, which is consistent with S11.2.b: no subject but a session can be
        removed, so a cleanup helper would have nothing to call. -->

  - [x] S11.2.f Capture screenshots, traces, console, requests, test data refs,
        correlation IDs, state, and exact command per step/failure. Evidence:
        evidence bundle.

        <!-- S11.2.f: shipped as
        `libs/oshun/workbench-kit/src/evidence-bundle.ts` (`EVIDENCE_ARTIFACTS` +
        `ARTIFACT_ANSWERS`, `CAPTURE_OCCASIONS` + `CAPTURED_ATTEMPTS` +
        `capturedAttemptFor` + `ATTEMPT_DESCRIBES_THE_FAILURE`, `ARTIFACT_SINKS`
        + `SINK_SURVIVES_THE_RUN` + `SINK_IS_UNIQUE_PER_ATTEMPT`,
        `parseCapturePolicy`, `resolveEvidenceReadiness`/
        `resolveAllEvidenceReadiness` over five `EVIDENCE_READINESS` states,
        `EVIDENCE_FAULTS` with 7 kinds, `evidenceFindings`, `evidenceCensus`,
        `OSHUN_RETRY_BUDGETS`, `TARA_CAPTURE_POLICY`, `CENTRAL_CAPTURE_POLICY`,
        `OSHUN_OUT_OF_BAND_ARTIFACTS`), 81 tests in `evidence-bundle.spec.ts`,
        `libs/oshun/workbench-kit/tools/evidence-bundle-scan.mjs` (nx target
        `evidence-bundle-scan`, `pnpm run verify:operations:v1-evidence-bundle`,
        14 negative controls), and the five registrations a kit entry point
        needs.

        Eight artifacts, and the temptation is to grade them present or absent.
        Presence is the easy half and it is not the half that goes wrong.

        **AN ARTIFACT OF THE RETRY IS NOT AN ARTIFACT OF THE FAILURE.** Both
        exemplar suites configure `trace: 'on-first-retry'` beside
        `screenshot: 'only-on-failure'`, and those two settings capture two
        different executions of the same test — the screenshot during the attempt
        that failed, the trace during the attempt **after** it, with a fresh
        browser and a fresh clock. When the failure is flaky the trace is of a run
        that passed; when it is deterministic it is of a second, identical-looking
        failure that is nonetheless a different one. **Nothing in either bundle
        says which artifact came from which attempt**, so a reader lines up a
        screenshot and a trace and reasons about a run that never happened.

        **AND THE SAME SETTING CAPTURES NOTHING WHERE MOST DEBUGGING HAPPENS.**
        Both configs declare `retries` as a ternary on the CI environment
        variable — two when set, zero when not. Outside CI there is no first
        retry, so `on-first-retry` never fires: on a developer's machine the Tara
        suite produces **no trace and no video**, by configuration, and **exactly
        one of the eight artifacts describes the failure** — the screenshot.
        `capturedAttemptFor` derives that from the occasion and the budget rather
        than asserting it, and `on-retry` is the only occasion whose answer the
        budget changes.

        **AN IDENTIFIER YOU DID NOT CHOOSE ARRIVES TOO LATE TO NAME THE THING
        THAT FAILED.** The BFF reads `x-correlation-id` and `x-trace-id`, so the
        join exists. Four files under `testing/e2e/tests/api` read one back off a
        **response**, and **not one test in the estate sets one on a request** —
        so a navigation that timed out, a route fulfilled from the test process,
        and every failure before the answer have no id at all. And the four
        readers are all `if (requestId) { expect(typeof requestId).toBe('string'); }`
        — **guarded by the value they assert about**, so a server sending no
        header passes exactly as one sending it does.

        **A FIXED PATH IS NOT AN ADDRESS WHEN SEVEN PROJECTS SHARE IT.**
        `base.page.ts` exposes `screenshot(name)` writing
        `test-results/screenshots/${name}.png`, outside `testInfo` so the report
        never sees it, at a path derived from the caller's label alone — while the
        Tara config declares **seven projects** over `fullyParallel: true`, five
        running the same specs on different browsers.

        THE MATRIX, in CI: Tara captures **2 of 8** artifacts and `testing/e2e`
        **5 of 8**, and in both suites **exactly one describes the failure**.
        Console, request, test-data-ref, application-state and
        reproduction-command are uncaptured in Tara; in `testing/e2e` the three
        that are captured are all `lost-with-the-run`. Neither suite reads
        `testInfo.status`, so nothing anywhere runs differently when a test fails.

        TRAP, caught by the scan on its first run: the check for a request-side
        correlation header matched the header NAME, which found the four readers
        and reported the repair as already done. **A setter appears as an object
        key and a reader as a subscript; the colon is the whole difference.** -->

  - [ ] S11.2.g Run content-authoring and operations-console exemplar journeys
        with fixtures disabled. Evidence: two passing live suites.

        <!-- S11.2.g LEFT UNCHECKED ON PURPOSE, with the gate that says why:
        `libs/oshun/workbench-kit/src/journey-readiness.ts` (`RUN_ENVIRONMENTS` +
        `AUTHORITY_ENVIRONMENTS`, `fixtureFreeEnvironments`,
        `fixtureFreeEnvironmentsIfUnconditionalGatesWereFixed`,
        `tenantReachableEnvironments`, `runnableEnvironments`,
        `liveRunContradiction`, `LIVE_PRECONDITIONS` + `PRECONDITION_OWNER` +
        `PRECONDITION_ASKS`, `resolvePrecondition`, `gateVerdict`,
        `resolveExemplarReadiness`/`resolveAllExemplarReadiness`,
        `readinessCensus`, `repairLeverage`), 47 tests in
        `journey-readiness.spec.ts`,
        `libs/oshun/workbench-kit/tools/journey-readiness-scan.mjs` (nx target
        `journey-readiness-scan`,
        `pnpm run verify:operations:v1-journey-readiness`, 13 negative
        controls), and the five registrations a kit entry point needs.

        **The two suites cannot be written, and the reason is not a missing
        feature.** Nothing here fabricates them: the deliverable is the gate that
        computes their readiness from the six preceding cells' own registers, and
        it refuses.

        **"WITH FIXTURES DISABLED" AND "PROVISIONED THROUGH REAL BOUNDARIES" ARE
        SATISFIABLE IN DISJOINT ENVIRONMENTS.** Both halves are derived:

        - `fixtureFreeEnvironments()` reads S11.2.c's gate register and returns
          **nothing**. Of the estate's sixty-nine substitutable subjects,
          **forty-nine serve in production** — two through `jsonRoute`, which
          evaluates no condition at all, and forty-seven behind a dependency's
          silence, which no environment suppresses. "Fixtures disabled" is not a
          state a test cannot enter; it is a state this estate has no environment
          for.
        - `tenantReachableEnvironments()` reads S11.2.b's register and returns
          **non-production only**: creating a tenant needs `admin:*` or
          `admin:workspace:tenant-console`, the only route to either is an
          unsigned `dev.` token, and `areDevTokensAllowed` refuses those wherever
          the runtime is production.

        Both exemplar journeys require a tenant, so the intersection is empty —
        **the closer the estate is brought to "fixtures disabled", the further it
        is from "provisioned through real boundaries."** And the counterfactual
        is computed too: repair the unconditional gates and the fixture-free
        environment becomes **production**, which is exactly where the tenant
        provisioner closes. **The second blocker survives the first repair.**

        THE GATE, run: **0 of 2 journeys runnable, 0 of 7 preconditions met, and
        all seven cells own a blocker** —
        `an-environment-satisfies-both` (S11.2.g),
        `fixtures-declinable-per-test` (S11.2.c: provider deployment-scoped, data
        undeclinable, intercept run-scoped), `subjects-provisionable` (S11.2.b:
        tenant authority-self-asserted), `no-unlabelled-substitution` (S11.2.a),
        `masquerades-refutable` (S11.2.d: four of five unrefutable),
        `helpers-establish-their-promise` (S11.2.e: job-wait, gate-publish and
        cleanup absent), and `failure-leaves-evidence` (S11.2.f: 1 of 8 artifacts
        outside CI). `repairLeverage` reports every cell unblocking exactly two,
        which is itself the finding: **S11.2.g is not one repair away from
        anything.**

        The scan re-reads both halves of the contradiction from source every run,
        and **fails if any suite ever claims to be a live exemplar journey while
        the gate still refuses** — a file claiming live in an estate that cannot
        be live is S11.2.d's masquerade at the scale of a whole suite.

        TRAP, caught by the gate on its first run: `resolveProvisioningChain`
        names its answer `verdict`, and reading `.readiness` returned `undefined`
        for every subject — filtering nothing out and listing four blockers where
        there is one. **The right conclusion for the wrong reasons, which is the
        failure mode a composed gate is most prone to.** -->

- [ ] S11.3 Upgrade the axe harness and rule policy to WCAG 2.2 AA. Evidence:
      tests cover the new 2.2 criteria applicable to workbenches.

      <!-- S11.3 a–f ARE ALL CLOSED. THE PARENT IS LEFT UNCHECKED ON PURPOSE,
      because its evidence line is a measurable claim and the measurement is
      1 of 6.

      Six kit entry points, 249 tests, 83 negative controls:
      `axe-policy` (a), `wcag22-coverage` (b), `scan-matrix` (c),
      `a11y-findings` (d), `a11y-waivers` (e), `semantic-assertions` (f), each
      with a spec, a scan, an nx target, a `verify:operations:*` script and the
      five registrations.

      WHY THE PARENT DOES NOT CLOSE. Of the six criteria WCAG 2.2 adds at A and
      AA, S11.3.b's map reports **1 covered** — 2.5.8 Target Size, the only one a
      scanner over a still document can reach, which is exactly and
      independently why it is the only one axe has a rule for. **2 have the
      technique aimed elsewhere** (2.4.11 and 2.5.7: the estate drives a real
      browser, opens a Popover, compares bounding boxes and walks operations for
      a non-drag path — against a gallery of nineteen primitives). **3 have no
      instrument of their shape at all** (3.2.6, 3.3.7, 3.3.8 are quantified over
      more than one document, and every accessibility instrument in the estate
      takes one page and returns a verdict about it).

      AND THE UPGRADE ITSELF IS WORTH ONE RULE. S11.3.a measured the six declared
      tags at 56, 3, **0**, 2, **0** and 1 rules: the 2.1 upgrade bought two
      rules, the 2.2 upgrade bought one, and `wcag21a` and `wcag22a` name nothing
      at all. The one rule 2.2 buys ships with its enabled flag off, so naming
      the tag is what runs it — and the site that asks for everything is the only
      one that misses it.

      WHAT WOULD CLOSE IT, IN ORDER. (1) Aim the S7.21.f driven controls at the
      workbench applications: that moves 2.4.11 to covered with **no new
      technique**, and the counterfactual is computed rather than asserted.
      (2) Declare the three registers nobody produces — which elements are help
      mechanisms, which flows are multi-step processes, which steps authenticate,
      and which operations are performed by dragging. Those are product
      decisions and not repository edits, which is why S11.3.b files them as
      `input-has-no-producer` rather than as work. (3) Build the multi-document
      instrument 3.2.6 needs; the estate already visits the pages and compares
      none of them.

      THREE LIVE DEFECTS THE FAMILY MEASURED, none of which is about 2.2:
      **1.4.2 Audio Control and 3.3.2 Labels or Instructions are Level A, are
      scanned, are reported, and cannot fail any gate in the estate** — their
      only rules top out at `moderate` and every gate fails on `serious`
      (S11.3.d). **The predicate that runs after 620 tests has never been shown
      to reject anything**: `libs/shared/testing/src/playwright/` holds `axe.ts`,
      `index.ts` and no spec. **The product suites read the accessibility tree
      zero times** while making 29 assertions about the attributes that feed it
      (S11.3.f). -->

  - [x] S11.3.a Pin axe/browser versions, WCAG 2.0/2.1/2.2 A/AA tags, rules,
        experimental/manual flags, impact policy, and allowed exclusions.
        Evidence: harness configuration review.

        <!-- S11.3.a: shipped as `libs/oshun/workbench-kit/src/axe-policy.ts`
        (`POLICY_DIMENSIONS` + `DIMENSION_ASKS`, `PIN_STRENGTHS` +
        `PIN_STRENGTH_RANK` + `isReproducible`, `OSHUN_ENGINE_BINDINGS`,
        `AxeCatalogue`, `rulesSelectedByTags`, `RuleSelection` +
        `rulesSelectedBy` + `declaredTags`, `criteriaLostToFlags`, `tagYields`,
        `MEASURED_SELECTION`, `OSHUN_AXE_HARNESSES`, `POLICY_FAULTS` +
        `policyFaults`, `gateCoverageFaults`, `dimensionCensus`,
        `policyHeadline`), 55 tests in `axe-policy.spec.ts`,
        `libs/oshun/workbench-kit/tools/axe-policy-scan.mjs` (nx target
        `axe-policy-scan`, `pnpm run verify:operations:v1-axe-policy`, 17
        negative controls), and the five registrations a kit entry point needs.

        THE VEIN: **A TAG IS A QUERY, NOT A POLICY.** The estate upgraded to
        WCAG 2.2 by adding two tags to two helpers, which reads like a policy
        change and is a change of search terms against a table shipped by
        somebody else. Run the query against the installed engine and the six
        declared tags are worth **56, 3, ZERO, 2, ZERO and 1** rules —
        fifty-nine of the sixty-two come from the WCAG 2.0 terms alone, the 2.1
        upgrade is worth two rules and the 2.2 upgrade one, and **two of the six
        terms name nothing at all**. `wcag21a` is the sharper half because every
        site declares it: its one rule is `label-content-name-mismatch` for
        **2.5.3 Label in Name, Level A**, and axe ships it `experimental`, which
        axe's own default `tagExclude` drops.

        **AND THE ONE RULE 2.2 BUYS SHIPS SWITCHED OFF.** `target-size` carries
        `enabled: false`, and axe consults that flag ONLY when the include list
        is empty, so naming the tag is what turns it on. Which gives the result
        that decides the cell: **the site asking for EVERYTHING is the only one
        that misses the 2.2 rule.** `apps/oshun/tenant-admin/e2e/fixtures.ts`
        calls `.analyze()` with no tags, gets 89 rules including 30
        best-practice ones, and does not run `target-size`, because with an
        empty include list the enabled flag applies again. **Asking for more got
        less.**

        `rulesSelectedByTags` reproduces axe's `matchTags` and was checked
        against **five selections measured in a real Chromium — 61, 62, 1, 0, 89
        — and matched 5/5**; the scan re-derives all five every run, so an
        engine bump re-opens the measurement instead of quietly falsifying it.
        It also **refuses an empty tag list because the engine does**
        (`runOnly.values must be a non-empty array`, thrown inside the page) —
        the first version of the suite asserted the opposite, which read as
        obvious and was false.

        SECOND: **THE GATE AND THE HELPER ARE NOT THE SAME HARNESS.** 539 of 560
        web specs and 81 of 82 admin specs end in the automatic `afterEach` from
        `libs/shared/testing/src/playwright/axe.ts`, which declares the 2.1 set;
        the 2.2 set is reached by **54 explicit calls in 22 files**. "The
        harness is WCAG 2.2 AA" is a true sentence about a helper and a false
        one about the gate.

        THIRD: **THE DIMENSION NOBODY STATES HAS A PRICE PAYABLE IN CRITERIA.**
        One of eight sites states a flag policy and it is the ledger rather than
        a gate; what the other seven inherit removes the ONLY rule the engine
        has for three live A/AA criteria — **1.2.1** (deprecated
        `audio-caption`), **1.3.4** (experimental `css-orientation-lock`) and
        **2.5.3** — and all three then come back `inapplicable`, the same answer
        a scan gives for a criterion nothing on the page could have violated.

        FOURTH: **AN EXCLUSION WITH NO TERMS IS THE MOST USED CONTROL HERE** —
        `test.use({ skipAxe: true })` **247 times across 241 files**, a boolean
        with no owner, reason or expiry; beside it `TEMPORARILY_DISABLED_RULES`
        (`color-contrast` 1.4.3 AA, `scrollable-region-focusable` 2.1.1 A) with
        the word "temporarily" and no date.

        FIFTH, which the word "pin" is about: **A PIN IS A CLAIM ABOUT A RUN AND
        NO RUN HERE RECORDS WHAT IT USED.** Every result carries
        `testEngine.version` and a `testEnvironment` and all eight sites discard
        both. **Three ranges decide the engine** — catalogue `^4.11.4`,
        `@axe-core/playwright@4.11.3 → ~4.11.4`, `axe-playwright@2.2.2 →
        ^4.10.1` — and two live in packages this repository does not edit. The
        browser is worse: `devices['Desktop Chrome']` sets a user agent and a
        viewport, not a build, and this machine has two Chromium builds
        installed. **0 of 7 dimensions are exact at every site.**

        DECISIONS. SELECTION IS THREE MODES AND NOT A NULLABLE TAG LIST, because
        axe has `tag`, `rule` and absent and they are not variations on one
        another — modelling `withRules` as no-tags reported the estate's
        one-rule contrast probe as running 89. THE LOST-CRITERIA LIST TAKES THE
        SELECTIONS IN FORCE AS AN ARGUMENT, so 2.5.8 is not counted as lost when
        the two helpers recover it by naming its tag, and scope is S7.21.f's A/AA
        list so four AAA criteria do not inflate it. AND SILENCE ABOUT THE
        BROWSER IS NOT A FAULT AGAINST A HARNESS, because no harness in any
        estate states a browser build; it is answered by `run-not-recorded`,
        which is the repair that exists.

        TRAPS, both caught by the scan on its own register: the admin helper's
        reach read "2 files" and is 3, and the story lane is configured in TWO
        files — tags in `.storybook/preview.ts`, invocation and report policy in
        `.storybook/test-runner.ts` — so a row naming one leaves the other
        unowned and `sources` is plural. -->

  - [x] S11.3.b Map WCAG 2.2 focus-not-obscured, dragging movements, target
        size, consistent help, redundant entry, and accessible authentication to
        automated/manual/journey checks. Evidence: criterion coverage map.

        <!-- S11.3.b: shipped as
        `libs/oshun/workbench-kit/src/wcag22-coverage.ts` (`NEW_AT_2_2` +
        `newCriteria`, `EvidenceShape` + `CRITERION_SHAPES`, `CHECK_METHODS` +
        `methodsFor`, `CRITERION_INPUTS`, `SUBJECT_LAYERS` + `PRODUCT_LAYER`,
        `NON_AXE_INSTRUMENTS` + `instrumentsFromHarnesses` + `canExpress`,
        `COVERAGE_VERDICTS` + `coverageOf` + `coverageMap`, `COVERAGE_FAULTS` +
        `coverageFaults`, `automatedReach`, `coverageCensus`,
        `coverageHeadline`), 41 tests in `wcag22-coverage.spec.ts`,
        `libs/oshun/workbench-kit/tools/wcag22-coverage-scan.mjs` (nx target
        `wcag22-coverage-scan`, `pnpm run verify:operations:v1-wcag22-coverage`,
        14 negative controls), and the five registrations.

        **THE METHOD FOLLOWS FROM THE SHAPE OF THE EVIDENCE, NOT FROM HOW HARD
        THE CRITERION FEELS.** Two questions settle it: how much has to be in
        evidence at once — one document in one state, one across several, or
        more than one document — which separates automated from journey; and
        whether the instrument can produce a definite PASS or only a definite
        FAIL with a residue, which is what `manual` is actually for. Nothing in
        the file assigns a method by hand. The six split **1 automated / 5
        journey, 4 also manual**, and the automated one is **2.5.8** — which is
        the one axe has a rule for, arrived at by a second derivation that never
        consults axe. The scan fails if the two ever disagree.

        THE VEIN: **THE ESTATE ALREADY OWNS THE TECHNIQUE FOR THE HARDEST THREE
        AND POINTS IT AT THE LAYER WITH NO PRODUCTS IN IT.** S7.21.f drives a
        real browser, opens a Popover and compares bounding boxes to answer
        2.4.11 (`focus:not-obscured-by-an-overlay`); walks every operation for a
        non-drag path to answer 2.5.7; measures a target floor for 2.5.8. All
        three are real, driven, and run against a bundled gallery of nineteen
        primitives at `?subject=…`. The workbench applications — where the
        drawer that covers the focused field actually lives — get a static
        scanner whose rule set contains one of the three. **The instrument is
        not missing; it is aimed one layer below the products**, and the
        counterfactual is computed: aim it at the applications and 2.4.11
        becomes `covered` with no new technique, while **2.5.7 does not move**,
        because its input has no producer — a second obstacle and a different
        job.

        SECOND: **ITERATING IS NOT RELATING.** Six web routes and seventeen
        admin workspaces are visited under an accessibility test, and both are
        `for (const … ) { test(…) }` — N verdicts about N pages. 3.2.6 is a
        claim about a **pair**, and no instrument here produces a verdict whose
        subject is more than one document. The pages are already being visited;
        nothing compares two of them, and no rule set ever will.

        THIRD: **A DELEGATION NOBODY CATCHES IS AN ABOLITION.** S7.21.f
        dispositions 3.2.6 as delegated-to-the-host ("a property of the site,
        not of any one page") and 3.3.7/3.3.8 as not-exercised ("no notion of a
        step", "no authentication step"). **This is the layer they were handed
        to**, and across the whole V1 estate those three criterion numbers
        appear in exactly one module and its spec — the one that handed them up.
        S7.21.f wrote that sentence about props; it arrives here one layer later
        about criteria.

        FOURTH: **THE INPUT IS PART OF THE MAP.** Four of six need the estate to
        declare something first — which elements are help mechanisms, which
        flows are multi-step, which steps authenticate, which operations are
        performed by dragging — and **none of the four has a producer**. S7.20's
        help registry looks exactly like the producer for the first and is not:
        it keys help CONTENT by capability, route, operation, field, block,
        gate, error, state and contract, and 3.2.6 is about placement.

        AND THE ONE THAT IS COVERED IS COVERED ONLY FOR WHAT THE ENGINE COULD
        DECIDE: 2.5.8's inline/essential/user-agent exceptions leave an
        `incomplete` residue, and S11.3.a measured that seven of eight sites
        read no incomplete results at all — so `residueUnread` is a column
        beside the verdict rather than folded into it.

        DECISIONS. THE VERDICT REPORTS THE FIRST OBSTACLE RATHER THAN A SCORE,
        because "no instrument exists", "one exists and is aimed elsewhere" and
        "one is aimed right with nothing to hand it" are three different jobs.
        MANUAL IS ADDED ON TOP OF THE MACHINE AND NEVER INSTEAD.

        TRAP, caught by the scan's own controls: **a criterion number is a
        substring of a version number** — searching for `3.2.6` finds
        `Phase 69.3.2.6`, and `\b` does not help because the preceding character
        is a dot. **And the fix is not symmetric**: a dot to the right is usually
        a full stop, so guarding both sides alike rejected `see 2.5.8.`, the
        commonest way a reference is written. Two controls, one per side. -->

  - [x] S11.3.c Scan route/state/theme/density/viewport/locale matrix after
        stable load and each relevant overlay/conflict/error/destructive state.
        Evidence: scan runner tests.

        <!-- S11.3.c: shipped as `libs/oshun/workbench-kit/src/scan-matrix.ts`
        (`SCAN_AXES`, `SCAN_STATES`, `AxisSupply` + `OSHUN_AXIS_SUPPLY`,
        `STATE_RELEVANCE_PRODUCER`, `CONFOUNDS`, `INTERACTING_PAIRS`,
        `planCells` + `fullFactorialSize` + `pairCovered`, `ScanRecord` +
        `coverageOf`, `SuiteRows`, `MATRIX_FAULTS` + `matrixFaults`,
        `matrixHeadline`), 39 tests in `scan-matrix.spec.ts`,
        `libs/oshun/workbench-kit/tools/scan-matrix-scan.mjs` (nx target
        `scan-matrix-scan`, `pnpm run verify:operations:v1-scan-matrix`, 18
        negative controls), and the five registrations.

        THE VEIN: **EVERY AXIS IS SETTABLE FROM A TEST AND FIVE OF THE SIX ARE
        NEVER SET.** Counted across every accessibility-named spec in web and
        admin: **zero** set a theme, **zero** set a locale, **zero** set a
        density, **zero** carry the `@mobile` tag the Playwright config uses to
        reach the second viewport — and that config's own comment says each spec
        executes on exactly one project. Route is varied and the rest sit at
        whatever the default is, so the scan set is a **line**. Nothing is out of
        reach: theme and locale are `localStorage`, density is an attribute,
        viewport is a runner project. **No missing capability, no blocked
        environment — the matrix is a matrix nobody enumerated.**

        WHAT THAT COSTS IN THE ESTATE'S OWN DECLARED VALUES: the design tokens
        ship **four themes** (`light`, `dark`, `highContrastLight`,
        `highContrastDark`) — two built for the users most likely to need a
        contrast check — and no accessibility scan runs under any but the
        default. The launch locales are **eight** and **two are right-to-left**;
        `applyLocaleToDocument` sets `documentElement.dir` from the locale, so an
        RTL rendering is one init script away, and **no accessibility scan has
        ever run right-to-left**. Against the estate: **16 routes, 3264 cells at
        full factorial, 57 in the covering plan, 0 located by evidence.**

        SECOND: **A CELL IS COVERED BY A SCAN WHOSE COORDINATES ARE KNOWN, AND NO
        SCAN HERE RECORDS ITS OWN.** The sharpest line of it — the web signoff
        suite scans `/` **twice**, once at load and once with the notifications
        drawer clicked open and `aria-modal` asserted, two genuinely different
        states, and **the two records are byte-identical**, because a route
        (recoverable only from the test title) is all either carries.

        THIRD: **A CONSTANT YOU DID NOT CHOOSE IS A CONFOUND.**
        `apps/oshun/web/e2e/fixtures.ts` emulates `reducedMotion: 'reduce'`
        suite-wide for a good and documented reason — entrance fades sampled
        mid-transition give false contrast failures — and the correct repair
        **pinned an axis** at the value most users are not in, across 539 specs.
        The admin fixtures emulate nothing, so the two suites scan **opposite
        values of one condition** and neither declares it.

        FOURTH: **ROWS ARE NOT CELLS.** The admin key-workspaces table is
        **17 rows over 10 distinct paths**, and `/analytics` is the subject of
        **seven** — rows differing only in which test id they wait for before
        scanning. `runAdminAccessibilityScan` is called with no `include`, so it
        reads the whole document every time and the seven are one scan behind
        seven different waits. `scopedToTheRow` is the field that decides it, and
        it is why the same shape is **not** a fault in the web suite, which does
        scope (`include: ['[data-shell-root]']`).

        THE REDUCTION, which is the part a runner lives or dies on: the full
        product is thousands of scans, so `planCells` covers every axis value
        once and every **interacting pair** once, and each pair carries the
        **mechanism** by which its two conditions can disagree. A pair with no
        mechanism is not in the plan — that is the difference between a covering
        array and a bill somebody quietly stops paying — and `theme x locale` is
        left out with the reason written down. `route x state` is outside the
        reduction, because a state that exists and is never entered is the gap
        the cell is about. **Which states a route has is an input with no
        producer**, so "this route has no error state" and "nobody looked at its
        error state" are currently the same reading.

        TRAPS: a check for a recorded coordinate matched two attachments in
        `search-scan-density-accessibility.spec.ts` whose "density" is the
        density of **search results**, not the UI axis — **the name argument is
        not a coordinate**, so the attach call's name is stripped and only the
        payload is searched. And a control asserting the state pairs were
        uncovered went stale the moment the register recorded the one overlay the
        estate really does scan; it now asserts the opposite, with a second
        control showing they close only because ONE route declares ONE state —
        which separates a weak plan from a missing input. -->

  - [x] S11.3.d Fail on new unwaived AA violations and require rule/selector/
        state/screenshot/DOM snippet/source owner in findings. Evidence:
        deliberate regression.

        <!-- S11.3.d: shipped as `libs/oshun/workbench-kit/src/a11y-findings.ts`
        (`FINDING_FIELDS` + `FIELD_PURPOSE` + `FIELD_PRESENCE` +
        `OSHUN_FINDING_FIELDS`, `GatePredicate` + `OSHUN_GATES` + `gateFails` +
        `gateThreshold`, `severityTable`, `gateAdmits`,
        `unenforceableCriteria`, `OSHUN_DELIBERATE_REGRESSIONS` +
        `STRICT_GATE` + `runRegression`, `FINDING_FAULTS` + `findingFaults`,
        `findingsHeadline`), 44 tests in `a11y-findings.spec.ts`,
        `libs/oshun/workbench-kit/tools/a11y-findings-scan.mjs` (nx target
        `a11y-findings-scan`, `pnpm run verify:operations:v1-a11y-findings`, 12
        negative controls), and the five registrations.

        THE VEIN: **AN IMPACT IS THE CHECKER'S SEVERITY JUDGEMENT AND A LEVEL IS
        THE STANDARD'S, AND EVERY GATE HERE IS KEYED ON THE FIRST AND NAMED FOR
        THE SECOND.** Every gate fails on `serious`+`critical` and every one is
        called a WCAG 2.1/2.2 AA gate. Against axe's own rule→check→impact
        chain, **5 of the 62 rules the conformance selection runs can never
        produce a violation above `moderate`** — `aria-deprecated-role`,
        `form-field-multiple-labels`, `html-xml-lang-mismatch`,
        `no-autoplay-audio`, `server-side-image-map` — so a real, detected,
        printed violation of any of them **passes every gate in the estate.**

        AND FOR TWO LEVEL A CRITERIA THAT IS THE ONLY RULE THERE IS: **3.3.2
        Labels or Instructions** and **1.4.2 Audio Control**. A page that
        autoplays audio with no way to stop it is scanned, found, printed and
        passes — which S11.3.b's coverage map reports as `covered`, because
        **coverage and enforceability are different questions.** The severity
        table is re-derived from the installed engine every run and the scan
        fails if the answer moves.

        SECOND: **"NEW" NEEDS A REFERENT AND AN ABSOLUTE GATE IS NOT ONE — BUT
        IT IS NOT NOTHING EITHER.** Nothing baselines an accessibility result.
        Inside the fail set that is *stricter* than a baseline: zero is the
        strictest referent there is. **Below** the threshold there is neither a
        gate nor a baseline, so a new AA violation at `moderate` is admitted once
        and admitted forever.

        THIRD, the proof the cell asks for: **A GATE THAT HAS NEVER FAILED IS A
        GATE NOBODY HAS TESTED.** `libs/shared/testing/src/playwright/` contains
        `axe.ts` and `index.ts` and **no spec** — the predicate that runs after
        620 tests has never been shown to reject anything. So the six
        **deliberate regressions** here are executable against a reproduction of
        that predicate, `?? 'moderate'` and all; two controls prove it can
        reject, and **three document a defect by passing a Level A violation on
        purpose**. The scan fails if any stops behaving as recorded — including
        if somebody repairs the gate, which is the outcome that should force this
        register rewritten.

        FOURTH: **A FIELD THE ENGINE ALREADY RETURNS IS NOT A MISSING FIELD, IT
        IS A DISCARDED ONE.** axe returns `node.html` on every violating node and
        both formatters drop it; it survives only where seven specs attach the
        whole result as JSON through a helper each defines separately. **2 of the
        6 fields reach the reader**, 2 are in artifacts, and `state` and
        `source-owner` are nowhere — the second because S11.1.a measured the
        ownership file as a single catch-all, so a lookup would answer, and
        answer the same thing every time.

        DECISIONS. A REGRESSION RECORDS WHAT THE GATE DOES AND NOT WHAT IT
        SHOULD, which is the only way one can document a defect rather than
        assert it away. THE UNGRADED VIOLATION IS POSED TO BOTH THE ESTATE'S GATE
        AND A STRICT ONE, so the estate's answer reads as a choice. AND A
        CRITERION IS UNENFORCEABLE ONLY WHEN EVERY ONE OF ITS RULES IS ADMITTED —
        4.1.2 has an admitted rule and a critical one, so it is not on the list.

        TRAP: the scan first built the severity table from the estate's *widest*
        selection, which is the untagged 89-rule default, and reported 24
        admitted rules — most of them best-practice with no criterion at all.
        **"The landmark helper cannot fail the build" is not the same finding as
        "WCAG 1.4.2 cannot fail the build"**, and only the second is a
        conformance claim, so the table is built from the widest *tag-mode*
        selection. -->

  - [x] S11.3.e Define waiver schema with false-positive proof or temporary
        exception, scope, owner, approver, expiry, remediation, and retest.
        Evidence: expiry tests.

        <!-- S11.3.e: shipped as `libs/oshun/workbench-kit/src/a11y-waivers.ts`
        (`APPLICATION_POINTS` + `waivable`, `WAIVER_FIELDS` + `FIELD_ASKS`,
        `EXCLUSION_KINDS` + `KIND_CLAIMS` + `KIND_OBLIGATIONS` +
        `NEVER_EXPIRES`, `FindingScope` + `A11yWaiver`, `standingAt` +
        `authorisesOn`, `GATE_WAIVER_FIT`, `OSHUN_EXCLUSIONS`, `WAIVER_FAULTS` +
        `waiverFaults`, `waiverCensus`, `waiverHeadline`), 36 tests in
        `a11y-waivers.spec.ts`,
        `libs/oshun/workbench-kit/tools/a11y-waivers-scan.mjs` (nx target
        `a11y-waivers-scan`, `pnpm run verify:operations:v1-a11y-waivers`, 11
        negative controls), and the five registrations.

        THE VEIN: **A WAIVER IS A DECISION ABOUT A FINDING, AND EVERY EXCLUSION
        IN THIS ESTATE IS APPLIED BEFORE THERE IS ONE.** `skipAxe` prevents the
        scan; `disableRules` stops the rule running; the story parameter skips
        the story. Across **247 opt-outs in 241 files** there is nothing to
        attach a waiver TO, because nothing was computed — and a proof, an owner
        and a date are all statements *about a finding nobody has*. **The repair
        is not a longer `skipAxe`**: it is to move the exclusion to the other
        side of the scan — run it always, then suppress a NAMED finding on terms
        — with the consequence nobody will enjoy: the day those 247 become
        suppressions, the estate learns what they were hiding. `waivable` is one
        line and **0 of the estate's 4 mechanisms pass it.**

        SECOND: **THE SCHEMA ALREADY EXISTS IN THIS LIBRARY AND IT DOES NOT FIT,
        FOR THE REASON THAT MAKES IT GOOD.** S2.11's `gate-waivers.ts` has
        request-vs-grant, scope, digested evidence, risk, a validity window, a
        review date, revocation and renewal — and its **first** requirement is a
        `runId`, under a comment saying *"a waiver against a gate rather than a
        result is an exemption, and it will outlive every reason it was granted
        for."* That is exactly what an accessibility exclusion is. It also wants
        an `inputDigest` (the input is a rendered page nobody hashes) and a
        tenant-shaped scope (a finding is rule × selector × route × state).
        **5 of its 9 fields fit and 4 do not**, named one by one, so "reuse
        S2.11" is a plan with a list rather than a gesture.

        THIRD, and why there are **three** kinds and not two: **A CLAIM ABOUT THE
        INSTRUMENT IS NOT A WAIVER.** The estate's one well-formed exclusion,
        `JSDOM_UNEVALUABLE_RULES`, says jsdom resolves no computed colours and
        names the Playwright lane as what settles contrast instead. That must
        never acquire an expiry — **a deadline on a fact about a runtime is a
        date that arrives and changes nothing** — and, equally, classing a real
        debt as an instrument limit is how one stops expiring.

        FOURTH: **AN EXPIRY IS A DATE AND A WORD IS NOT ONE.**
        `TEMPORARILY_DISABLED_RULES` turns off `color-contrast` (1.4.3 AA) and
        `scrollable-region-focusable` (2.1.1 A), and "temporarily" is the entire
        temporal content.

        DECISIONS. THE CLOCK IS AN ARGUMENT, so the cell's named evidence —
        expiry tests — is a test and not a wait; the boundary is inclusive on the
        day, because a date is a day and not an instant. AN UNREADABLE DATE
        RETURNS `unbound` AND NOT `active`. THE STATUS VOCABULARY IS S2.11's,
        imported rather than restated. AND THE MISSING-EXPIRY FAULT IS DRIVEN BY
        THE OBLIGATIONS, not by a separate rule — the first version asked every
        kind that is not an instrument limit for an expiry, which flagged a
        false-positive claim for a date it does not owe.

        TRAP, caught by this scan against its own sibling: **A MENTION IN PROSE
        IS NOT A USE.** Counting lines gave **248** `skipAxe` opt-outs, and one
        was a sentence in `studio-route-render-smoke.spec.ts`'s doc comment
        explaining that the file uses one. S11.3.a's register said 248 and now
        says **247**; both cells now filter comment lines and report the same
        number. -->

  - [x] S11.3.f Add custom semantic assertions where axe cannot prove names/
        states/relationships, announcements, complex widget patterns, or visual
        obstruction. Evidence: custom test index.

        <!-- S11.3.f: shipped as
        `libs/oshun/workbench-kit/src/semantic-assertions.ts`
        (`ASSERTION_SUBJECTS` + `SUBJECT_ASKS`, `WHY_AXE_CANNOT` +
        `WHY_READS_AS` + `WHY_EXPLAINED`, `OBSERVATION_CHANNELS` +
        `SETTLEMENTS` + `CHANNEL_SETTLEMENT` + `settles`, `CustomAssertion` +
        `OSHUN_CUSTOM_ASSERTIONS`, `ASSERTION_FAULTS` + `assertionFaults`,
        `assertionCensus`, `assertionHeadline`), 34 tests in
        `semantic-assertions.spec.ts`,
        `libs/oshun/workbench-kit/tools/semantic-assertions-scan.mjs` (nx target
        `semantic-assertions-scan`,
        `pnpm run verify:operations:v1-semantic-assertions`, 11 negative
        controls), and the five registrations.

        THE VEIN: **A CUSTOM SEMANTIC ASSERTION THAT READS THE DOM IS CHECKING
        THE INPUT, AND EVERY ONE OF THE ESTATE'S PRODUCT-LEVEL ASSERTIONS DOES.**
        Counted across every accessibility-named spec in web and admin: **29**
        `toHaveAttribute('aria-…')` assertions, **50** role queries, and **zero**
        reads of the accessibility tree. An `aria-labelledby` is what the author
        wrote; the accessible name is what the user agent computed from it, and
        one pointing at an element not in the document produces a widget with
        **no name at all** while the attribute assertion passes.

        ONE LAYER DOWN THE ESTATE ALREADY OWNS THE INSTRUMENT: S7.21.f opens a
        CDP session, calls `Accessibility.getFullAXTree`, and carries a fault
        named **`property-written-in-the-dom-that-the-tree-does-not-carry`** —
        precisely the case the twenty-nine cannot see, *by construction*,
        because they read the side of the seam the fault is about. It contracts
        **13** widget patterns, observes announcements through a live-region
        probe, and compares bounding boxes for obstruction — all against a
        bundled gallery. Same shape as S11.3.b; the contribution here is
        sharper: **a named fault the estate can detect, has an instrument for,
        and cannot detect at the layer the fault is about.**

        SECOND: **A CUSTOM ASSERTION IS NEEDED MOST WHERE AXE REPORTS SUCCESS.**
        Three reasons the checker cannot prove something, and only one is
        visible. `no-rule` is honest. `could-not-decide` is axe's third outcome,
        and **37 of the 62 running rules have a check that can return it** — so
        part of this very index is published once per run and S11.3.a measured
        that **7 of 8 sites discard it**. `inapplicable-by-construction` is the
        worst: the rule exists and the attribute it would have judged was never
        rendered, so it reports the same word it uses for a page that could not
        have violated the criterion. **Two of the three read as green, and those
        are the two a custom assertion is FOR.**

        DECISIONS. THE SETTLEMENT TABLE IS THE ARGUMENT — a claim per
        (channel, subject) with a reason, because unexplained verdicts grade by
        assertion rather than by channel. **A CAVEAT IS NOT A PASS**: the 50 role
        queries compute a name in the test process rather than the browser and
        reach no relationship, so they are their own row and not coverage; and
        **no channel strictly settles a widget pattern**, because the protocol
        carries neither `aria-posinset` nor `aria-setsize` — a limit of the
        instrument, not of the estate. AND AN ENTRY'S SUBJECT IS NULLABLE,
        because of one row: **the estate's only product-level geometry calls use
        the channel that settles obstruction and measure a TOUCH TARGET**, so
        filing them under `visual-obstruction` because the channel matches would
        have reported the subject as covered by an assertion that never asks it.

        AGAINST THE ESTATE: **0 of 4 subjects settled at the product layer**, one
        reached only with a caveat, and three techniques that settle theirs
        aimed at the gallery. A control confirms that aiming the tree instrument
        at the products would settle one, with no new technique. -->

- [ ] S11.4 Add keyboard-only journey helpers. Evidence: no pointer calls in the
      critical keyboard suite.

      <!-- S11.4 LEFT UNCHECKED ON PURPOSE. Six children shipped; a–d and f are
      `[x]`, e is `[ ]` with its own gate. The parent is unchecked because its
      evidence line **has no subject and would not be sufficient if it had
      one**, and both halves of that were measured by its own children.

      **NO SUBJECT.** S11.4.d built the guard the line describes and then went
      looking for what to run it over: **nothing in this repository declares a
      keyboard suite** — no Playwright tag, no project, no grep-able marker.
      `keyboard-only` appears in three test titles, one journey-registry entry,
      some typed text and some prose, and **a title is not a selector**. So
      "the critical keyboard suite" names nothing that can be enumerated, and a
      claim about the pointer calls in it is a claim about an empty set.

      **NOT SUFFICIENT IF IT HAD ONE.** S11.4.e found the nearest thing to that
      scope — the estate's critical-journey register, eight distinct tests named
      by file and title — and ran S11.4.d's guard over it: **four of the eight
      do not hold.** One opens the palette with a chord and then **clicks five
      palette items**; `home-keyboard-focus-path` **presses no key at all**,
      asserting `tabindex` is `-1` and then `0`, which reads the attribute that
      decides the tab order instead of walking it; and two set focus by method
      call. The one test titled `keyboard-only` **passes the parent's evidence
      line exactly** — zero pointer calls, zero forced clicks, zero test ids —
      and calls `.focus()` once, which reaches anything at `tabindex="-1"`, in a
      closed subtree, or skipped by the tab order. **A test that satisfies this
      line can still have assumed the thing it exists to prove.**

      **AND THE HELPERS THE CELL ASKS FOR CANNOT ALL BE WRITTEN**, for reasons
      that are product gaps rather than test gaps: S11.4.c found **no keyboard
      path at all** for reorder, move or range selection, with five drag
      surfaces carrying no keyboard affordance anywhere in their file; S11.4.b
      found the one key family that matters for eight launch locales —
      composition — reachable by **neither** of the runner's text APIs, only by
      a CDP `Input.imeSetComposition`, which appears nowhere here; and S11.4.a
      found **one of seven** focus claims established. The gate in S11.4.e
      computes each of those from the owning cell's own register, so **it moves
      when a blocker clears** rather than when somebody edits it: today it
      reports **0 of 9 critical acts runnable keyboard-only**.

      What would close this parent is a declared suite, a keyboard path for the
      three interactions that have none, and a composition mechanism — none of
      which is a test somebody forgot to write. -->

  - [x] S11.4.a Provide focus-current/order/visible/not-obscured/within-region/
        restored assertions and focus history evidence. Evidence: helper tests.

        <!-- S11.4.a: shipped as
        `libs/oshun/workbench-kit/src/focus-assertions.ts` (`FOCUS_CLAIMS` +
        `CLAIM_ASKS`, `EVIDENCE_NEEDS` + `CLAIM_SHAPES`,
        `OBSERVATION_PATTERNS` + `PATTERN_DESCRIBED` + `establishes`,
        `CHANNEL_FOR` + `obstructionChannelSettles`, `OSHUN_FOCUS_EVIDENCE`,
        `FOCUS_ORDER_RESOLVER`, `FOCUS_FAULTS` + `focusFaults`, `focusCensus`,
        `focusHeadline`), 31 tests in `focus-assertions.spec.ts`,
        `libs/oshun/workbench-kit/tools/focus-assertions-scan.mjs` (nx target
        `focus-assertions-scan`,
        `pnpm run verify:operations:v1-focus-assertions`, 12 negative controls),
        and the five registrations.

        THE SEVEN SPLIT THREE WAYS ONCE YOU ASK WHAT EACH NEEDS OBSERVED: three
        are about **one instant** (current, visible, not-obscured), one relates
        **two** (restored), and three need **every step** (order, within-region,
        history).

        THE VEIN: **A SEARCH FOR AN ELEMENT IS NOT AN ASSERTION ABOUT AN ORDER.**
        The estate's strongest keyboard-reachability check presses Tab **up to
        eighty times** until the assistant composer reports itself focused, then
        asserts that it was reached. That establishes reachability — **2.1.1
        Keyboard** — and says nothing about **2.4.3 Focus Order**, which is a
        claim about the sequence. And the eighty is read out of the loop by the
        scan rather than remembered, because the point is that **nothing on the
        page produced it**: a surface with a longer focus order fails, and the
        message says the element was not reached rather than that the budget ran
        out.

        SECOND: **TAB IS A MOVE AND AN ASSERTION IS A CLAIM — 14 of the estate's
        19 Tab presses are moves**, followed by `type(…)` rather than
        `expect(…)`. If focus lands elsewhere the text arrives in the wrong field
        and the failure names neither focus nor order.

        THIRD, and it stings: **THE CORRECT RESOLVER EXISTS IN THIS REPOSITORY
        AND IS SERVED TO THE PRODUCT.**
        `apps/oshun/bff/src/studio/focus-order-store.ts` computes the real Tab
        traversal — positive `tabIndex` first ordered by index then document
        position, then `tabIndex` 0 in document order, negatives
        programmatic-only, disabled and hidden skipped, the positive-index
        anti-pattern flagged. Deterministic, tested, and exposed at
        **`/v1/admin/studio/focus-order`** for a designer to inspect a component
        with. `resolveFocusOrder` has **exactly two importers** — the route that
        serves it and the store that defines it — and **no suite uses it to check
        the product's own focus order.**

        FOURTH: **FOCUS HISTORY IS WHAT MAKES A FAILURE READABLE.** `toBeFocused`
        fails with the state at the end; an order fails because of the path. The
        estate records none.

        AGAINST THE ESTATE: 65 `toBeFocused()`, 19 Tab presses (5 asserted), 20
        `document.activeElement` reads, 3 `aria-modal` assertions, 2 hand-written
        returns, and **zero** checks of the focus indicator. **4 of 7 claims
        observed, ONE established, 0 behind a shared helper.**

        DECISIONS. The two claims that need a painted page are refused to every
        keyboard pattern and reported under **their own fault kind**, because
        "walk it better" and "read the painted page at all" are different
        repairs. A SINGLE ASSERTION CANNOT ESTABLISH CONTAINMENT — focus inside a
        region at two moments is consistent with focus having left and come back.
        AND A RETURN NEEDS A BEFORE: an assertion made only afterwards is
        asserting a constant somebody typed, which is what both of the estate's
        focus-restored assertions do.

        TRAP: the register said 4 `aria-modal` assertions and 21
        `activeElement` reads; the scan's prose filter counted 3 and 20 — one of
        each was a comment. Same class as S11.3.e's 248→247. -->

  - [x] S11.4.b Provide Tab/Shift-Tab, arrows, Home/End/Page, Enter/Space,
        Escape, type-ahead, application shortcuts, and composition-safe helpers.
        Evidence: key helper tests.

        <!-- S11.4.b: shipped as `libs/oshun/workbench-kit/src/key-helpers.ts`
        (`KEY_FAMILIES` + `FAMILY_ASKS` + `FAMILY_REQUIRES`,
        `INPUT_MECHANISMS` + `MEASURED_MECHANISMS` + `MEASURED_ON`,
        `canExercise` + `mechanismsFor`, `OSHUN_KEY_EVIDENCE`,
        `OSHUN_COMPOSITION_EXPOSURE`, `OSHUN_HELPER_BUDGETS`, `KEY_FAULTS` +
        `keyFaults`, `keyCensus`, `keyHeadline`), 27 tests in
        `key-helpers.spec.ts`,
        `libs/oshun/workbench-kit/tools/key-helpers-scan.mjs` (nx target
        `key-helpers-scan`, `pnpm run verify:operations:v1-key-helpers`, 12
        negative controls), and the five registrations.

        Seven of the eight families are `keyboard.press` with a different
        argument. **The eighth is not an argument at all.**

        THE VEIN: **A HELPER FAMILY IS DEFINED BY THE INPUT IT CAN PRODUCE, AND
        NEITHER OF THE RUNNER'S TEXT APIS CAN PRODUCE THE ONE THAT MATTERS.**
        Measured in a real Chromium on an input with every listener attached:

        - `keyboard.type('a')` → `press('Enter')`: `isComposing` **false**
          throughout, no composition event at all.
        - `keyboard.insertText('あ')` → `press('Enter')`: an `input` with the
          text and **no keydown for it whatsoever**, then `isComposing` false.
        - CDP `Input.imeSetComposition` → `press('Enter')`: `compositionstart`,
          `compositionupdate`, `input` with `isComposing: true`, and then
          **`keydown Enter` carrying `isComposing: true`.**

        Only the third produces the input a composition-safe helper exists to
        test. **`imeSetComposition` and `CompositionEvent` appear nowhere in this
        repository** — and the capability is not missing: the estate already
        opens CDP sessions in four places.

        WHAT THAT COSTS, COUNTED: **29 text-entry elements** (`<input>`,
        `<textarea>`) in web and admin carry an inline `onKeyDown` acting on
        Enter — among them the shell's universal search, the assistant composer,
        and Arete's coach, goals and journal inputs — and **`isComposing`,
        `keyCode === 229` and `compositionstart` appear ZERO times in product
        code.** For a user typing Japanese — **one of the eight launch locales** —
        pressing Enter to *confirm an IME candidate* fires every one of them: the
        search submits, the message sends, the half-composed tag is added. **The
        count is the EXPOSURE and not the defect count**, and the difference is
        the point: without the mechanism, a safe handler and an unsafe one are
        the same observation.

        SECOND: **THE ESTATE'S ONLY KEY HELPER IS A SEARCH, AND THERE ARE TWO OF
        THEM WITH TWO BUDGETS** — a local `tabTo({ maxTabs = 24 })` inside one
        spec, and an inline loop pressing Tab up to **80** times in another. Both
        are read out of their loops by the scan; **neither number came from a
        page.** 24 against 80 is not a disagreement about two surfaces — it is
        two people guessing.

        THIRD: **A TEXT API THAT SKIPS THE KEY EVENT CANNOT EXERCISE A KEY
        HANDLER**, which makes `insertText` exactly right for setting a field up
        and exactly wrong for testing anything listening for a key.

        AGAINST THE ESTATE: **6 of 8 families exercised** (tab 19, arrows 22,
        paging 6 with no Page key pressed anywhere, activate 148, dismiss 32,
        shortcuts 19), **1 behind any helper at all**, and that one lives inside
        a spec file. Type-ahead and composition are unexercised.

        DECISIONS. THE NARROWING IS BY OPENING TAG, not by file — a file
        containing an Enter handler and an input *somewhere* gives 55, and the
        first of those is a `<li role="button">` with no text entry and so no
        composition to be unsafe about. AND TYPE-AHEAD'S USE COUNT IS `null`
        RATHER THAN 0, because a call site doing it by hand is not separable from
        ordinary typing by any search — which is itself the argument for a
        helper. -->

  - [x] S11.4.c Provide semantic selection/range/additive, reorder/move/resize/
        trim/pan/zoom/connect, compare, command palette, review, and
        confirmation helpers without pointer APIs. Evidence: complex interaction
        examples.

        <!-- S11.4.c: shipped as
        `libs/oshun/workbench-kit/src/interaction-helpers.ts`
        (`COMPLEX_INTERACTIONS` + `KEYBOARD_IDIOM` + `SURFACE_REGISTER` +
        `enumerable`, `WORD_COUNTS_THAT_ARE_NOT_SURFACE_COUNTS`,
        `DRAG_VERDICTS` + `dragVerdict` + `OSHUN_DRAG_REGISTER` + `dragCensus`,
        `PATH_STATES` + `OSHUN_INTERACTION_EVIDENCE`, `INTERACTION_FAULTS` +
        `interactionFaults`, `interactionCensus`, `interactionHeadline`), 26
        tests in `interaction-helpers.spec.ts`,
        `libs/oshun/workbench-kit/tools/interaction-helpers-scan.mjs` (nx target
        `interaction-helpers-scan`,
        `pnpm run verify:operations:v1-interaction-helpers`, 11 negative
        controls), and the five registrations.

        THE VEIN: **YOU CANNOT WRITE A HELPER FOR A SET YOU CANNOT ENUMERATE.**
        For exactly one of the twelve — dragging — this cell builds the register,
        and it takes a JSX parser: walk every opening tag, track brace depth so a
        handler body full of `>` does not end it early, and ask whether *that*
        tag carries the affordance. For the other eleven **the word is not the
        thing**: "review" appears **9,779** times across the two products and
        "compare" **1,384**, and neither is a count of an affordance. **11 of 12
        interactions have no register of their surfaces** — the same shape
        S11.3.b found for 2.5.7, 3.2.6, 3.3.7 and 3.3.8, arriving one cell later
        with a name.

        AND THE ONE REGISTER EARNS ITS KEEP IMMEDIATELY. **S11.3.b filed 2.5.7's
        input — "which operations are performed by dragging" — as having no
        producer. `OSHUN_DRAG_REGISTER` is that input.** Nine product elements
        carry `draggable` or `onDragStart`: **1** has an `onKeyDown` on the same
        element, **3** have an arrow handler or a move control elsewhere in their
        file, and **5 have none of the three anywhere in the file they live in** —
        two in `WidgetSidebar`, one each in `TaraSceneEditor`,
        `AreteSevenHabitsSystem` and `AreteVisionBoard`.

        **THOSE FIVE ARE CANDIDATES AND NOT VERDICTS, AND THE DIFFERENCE IS THE
        POINT.** An alternative can live in a sibling component and a drag can be
        essential, so the verdict is **`needs-exercising`** — source has run out,
        not the question. What settles them is exercising the surface, which is
        the helper this cell asks for driven by the instrument S11.3.b measured
        as aimed at a gallery. **Before this register, a 2.5.7 review had nowhere
        to start.**

        SECOND: **A HELPER WITHOUT A PATH IS NOT A TEST GAP, IT IS A PRODUCT
        GAP.** The command palette has a path and no helper — Control+K, arrows
        and Enter, 9 keyboard drives written out at each site. A `WidgetSidebar`
        reorder has no path at all, and filing that as a missing helper would put
        it in the wrong queue.

        THIRD, measured and small: **range selection is never driven by a
        keyboard here.** `press('Shift+Arrow…')` appears **zero** times in the V1
        suites while two products declare `aria-multiselectable`, and additive
        and range selection are the two the cell names first.

        AGAINST THE ESTATE: **12 interactions, 2 with an enumerable surface set
        (both through the one register), 2 with a keyboard path, 0 with a
        helper.** The scan rebuilds the drag register from the products every run
        and fails if it and the module disagree. -->

  - [x] S11.4.d Detect pointer/touch/mouse calls, coordinate clicks,
        force-click, direct DOM evaluation, inaccessible test IDs, and
        accessibility-tree bypass in keyboard suites. Evidence: static/runtime
        guard.

        <!-- S11.4.d: shipped as
        `libs/oshun/workbench-kit/src/keyboard-guard.ts` (`BYPASS_KINDS` +
        `BYPASS_ASKS`, `DECIDABILITIES` + `DECIDABILITY` + `WHY_NOT_STATIC`,
        `MEASURED_CLICK_BEHAVIOUR`, `KEYBOARD_SUITE_DECLARATION` +
        `KEYBOARD_ONLY_MENTIONS`, `OSHUN_BYPASS_CENSUS`,
        `THE_ONE_KEYBOARD_ONLY_TEST`, `GUARD_FAULTS` + `guardFaults`,
        `guardCensus`, `guardHeadline`), 25 tests in `keyboard-guard.spec.ts`,
        and **`libs/oshun/workbench-kit/tools/keyboard-guard-scan.mjs`, which IS
        the static half of the guard** (nx target `keyboard-guard-scan`,
        `pnpm run verify:operations:v1-keyboard-guard`, 11 negative controls),
        plus the five registrations.

        THE VEIN: **`force: true` DOES NOT MAKE THE CLICK LAND — IT MAKES THE
        FAILURE SILENT.** Measured in a real Chromium on a button under a
        full-page overlay:

        - `locator.click()` **refused**, timing out on the receives-events
          actionability check; the target's handler never ran and the call threw.
        - `locator.click({ force: true })` dispatched a real mouse event at the
          target's coordinates and **the COVERING element received it**; the
          target's handler never ran and **the call resolved successfully**.

        So a forced click does not click the thing you named — it sends a real
        click at that thing's coordinates and whatever is on top gets it, then
        reports success. Each of the estate's **39** forced clicks is a call that
        **cannot fail for the one reason it was almost certainly added**, and can
        silently activate a different control. And that actionability check is
        **the estate's only automatic detector of an obscured control**, since
        S11.3.f measured 2.4.11 as having no instrument aimed at the products.

        SECOND: **IN THIS RUNNER, A POINTER CALL AND A COORDINATE CLICK ARE THE
        SAME CALL.** Measured: a click on an element at (50,60) sized 100×40
        dispatched a *trusted* event at exactly **(100,80)** — the centre of its
        box. There is no non-coordinate click in the API, so the cell's two items
        collapse into one.

        THIRD, about the evidence line itself: **"NO POINTER CALLS" IS NECESSARY
        AND NOT SUFFICIENT.** The one test in the estate titled *"keyboard-only
        members can reach the prompt input, send button, and voice toggle"* has
        **0 clicks, 0 forced clicks, 0 hovers, 0 mouse calls, 0 test ids** — it
        passes the parent's evidence line exactly — and calls **`.focus()`
        once**. A method call reaches anything at `tabindex="-1"`, anything in a
        closed subtree, anything the tab order skips: a keyboard-only test that
        sets focus that way **has assumed the thing it exists to prove**.

        FOURTH, which decides the guard's design: **THREE OF THE SIX ARE
        STATICALLY DECIDABLE AND THREE ARE NOT.** The guard runs three and
        deliberately **does not** report the 759 `evaluate` calls (most read
        `document.activeElement` or seed a store — a rule that reports them all
        is a rule somebody turns off) or call the 1,345 test ids inaccessible
        (that is a question about the element each resolves to). Both are
        reported as needing a runtime fixture, which is what "static/runtime
        guard" means.

        AND THE GUARD HAS NO SCOPE. **Nothing declares a keyboard suite** — no
        tag, no project, no marker. "keyboard-only" appears in three test titles,
        one journey-registry entry, some typed text and some prose, and a title
        is not a selector. **The parent's evidence line has no subject.**

        COUNTS: pointer 3554, coordinate 3546, force 39, evaluate 759, test id
        1345, tree bypass 20341.

        TRAPS, both caught here and fixed across all ten scans in this family:
        **(1) `execFileSync`'s default 1 MB buffer overflows on a common token —
        `locator(` returns 20,341 lines — and a `catch` that returns `[]` records
        the throw as a count of ZERO.** Every scan now sets a 64 MB buffer and
        rethrows anything but git-grep's exit-1 "no matches". **(2) A SCAN THAT
        SEARCHES THE REPOSITORY COUNTS ITS OWN PROSE** — fourth sighting: the
        register names the symbols it measures, so its own module, scan, nx
        target, ledger entry and generated export map all match. Every repo-wide
        search now excludes the cell's own artifacts by path. Both were found by
        controls, not by review. -->

  - [ ] S11.4.e Run critical create/edit/save/conflict/review/gate/bulk/publish/
        recover journeys from fresh page through only keyboard input. Evidence:
        journey suite.

        <!-- S11.4.e LEFT UNCHECKED ON PURPOSE, with the gate that says why:
        `libs/oshun/workbench-kit/src/keyboard-journeys.ts` (`CRITICAL_ACTS` +
        `ACT_NEEDS`, `OSHUN_JOURNEY_SURFACES` + `registerNames`,
        `CITATION_VERDICTS` + `citationVerdict` + `OSHUN_CITED_TESTS`,
        `PRECONDITIONS` + `PRECONDITION_OWNER` + `resolvePrecondition` +
        `resolveActReadiness` + `resolveAllActReadiness`, `JOURNEY_FAULTS` +
        `journeyFaults`, `journeyCensus`, `journeyHeadline`), 33 tests in
        `keyboard-journeys.spec.ts`,
        `libs/oshun/workbench-kit/tools/keyboard-journeys-scan.mjs` (nx target
        `keyboard-journeys-scan`,
        `pnpm run verify:operations:v1-keyboard-journeys`, 10 negative
        controls), and the five registrations.

        **The nine journeys cannot be written, and the reason is not a missing
        test.** THE GATE, run: **0 of 9 acts runnable, every one blocked by 4 or
        5 preconditions, and every precondition owned by a sibling cell.**

        THE VEIN: **A JOURNEY IS SOMETHING THE USER DOES AND THE REGISTER
        ENUMERATES PLACES THEY GO.** The estate keeps a *good* critical-journey
        register with a `keyboardOnly` coverage dimension —
        `apps/oshun/web/e2e/support/critical-web-journeys.ts` — **14 entries,
        every one carrying a keyboard-only citation** with a file, a title
        fragment and a sentence of evidence. It reads as complete. And its 14
        entries are **surfaces**: home, explore, library, activity, search, the
        assistant dock, notifications, profile, six domains. **Not one of the
        nine acts appears in it.** Coverage of every place is not coverage of any
        act, and a create that cannot be done by keyboard is invisible to a
        register organised by route.

        SECOND, and this is what the register makes possible: **A CITATION IS A
        CLAIM THAT CAN BE CHECKED.** S11.4.d built a guard and reported that
        nothing declared a keyboard suite for it to run over. **This register IS
        that scope** — 8 distinct tests, named by file and title. Run the guard
        over them and **4 of the 8 do not hold**:

        - `shell-keyboard-workflows.spec.ts` "Control+K command palette supports
          search, creation, agent, and recent workflows" opens the palette with a
          chord and then **clicks five palette items**
          (`locator('[data-item-id="…"]').click()`).
        - `home-keyboard-focus-path.spec.ts` "hidden shell controls stay out of
          the tab order until visible" — cited for the Home journey — **presses
          no key at all.** It asserts `tabindex` is `-1`, then `0`. That is
          S7.21.f's third trap in the keyboard family: `tabindex="0"` is what the
          author wrote; a control with it inside an `inert` subtree, inside
          `display:none`, or inside a closed dialog is reached by nothing.
        - `assistant-accessibility.spec.ts` (×1) and
          `tara-session-player-accessibility.spec.ts` (×6) **set focus by method
          call.**

        THIRD: **THE GATE REFUSES FOR REASONS THE SIBLINGS ALREADY MEASURED, AND
        NOT ONE IS ABOUT WRITING A TEST.** No helper for any of the 12 complex
        interactions; **no keyboard path at all for reorder, move or range
        selection** (5 drag surfaces with no keyboard affordance anywhere in
        their file); 1 of 7 focus claims established; the only key helper a Tab
        search local to one spec; and no scope for the guard. **A keyboard-only
        `reorder` journey is not unwritten work — it is work that cannot start
        until a product offers a path.**

        Every precondition is resolved by calling the owning cell's own register,
        so the gate moves when a blocker clears rather than when somebody edits
        this file. The scan re-parses the estate's register every run — including
        its **shared citation constants**, which a naive parse misses entirely,
        reporting every journey as uncited — and opens every cited test.

        TRAP, committed by this cell and caught by its own controls:
        `registerNames` used `includes` and reported the register as naming the
        **`save`** act, because a surface is called
        `library-**save**d-continuity`. **A substring is not a name** — the
        cell's own vein turned on the cell. -->

  - [x] S11.4.f Exercise supported browser/OS/editor/assistive shortcut
        collisions and alternate/remapped path. Evidence: collision tests.
        <!-- S11.4.f: shipped as
        `libs/oshun/workbench-kit/src/shortcut-collisions.ts` (+ `.spec.ts`, 37
        tests) with `tools/shortcut-collisions-scan.mjs` (15 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/shortcut-collisions`,
        `verify:operations:v1-shortcut-collisions`, and the
        `shortcut-collisions-scan` Nx target. Entry point 207 of 207,
        0 export-map violations.

        **THE VEIN: THIS ESTATE MAKES A SHORTCUT CLAIM THREE DIFFERENT WAYS, IN
        ONE PRODUCT.** `AccessibilityShell.tsx` **never calls
        `preventDefault`** — `scheduleShellShortcut` defers a tick, checks
        `event.defaultPrevented` and yields if anything else claimed the key, so
        the browser's action and the shell's both happen.
        `CommandPalette.tsx` **calls `preventDefault` immediately** on
        Cmd/Ctrl+K, which is where Chrome and Firefox focus the address bar for
        a search. `TaraSitPlayer.tsx` binds **bare `k`, bare Space and bare
        arrows on `window`**. Three policies, three owners, seven branches — and
        **nothing anywhere lets a user change or disable any of them**, which is
        the cell's second ask answered `REMAP_PATH = null`.

        **THE SECOND RULE, AND IT IS WHERE THE COLLISION ACTUALLY IS: A MODIFIER
        GUARD IS PART OF THE CHORD, AND TWO OF THE SHELL'S FOUR BRANCHES DO NOT
        WRITE ONE.** `Alt+<digit>` and `Alt+A` are both
        `event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey`.
        `Alt+/` is `event.altKey && event.key === '/'`, and the fourth branch is
        `event.key === 'Escape'`. **`Ctrl+Alt` is not a spare combination**: on
        Windows it is **AltGr**, which four of the eight launch locales
        (S11.3.c's) hold down to type `@`, `\`, `€` and `~`; on macOS it is
        **VoiceOver's own modifier**, held before every VO command. The
        combination the two guarded branches exclude, the other two admit.

        **THE THIRD, AND IT IS THE SAME SHAPE ONE LEVEL DOWN: A GUARD PROTECTS
        THE BRANCH IT IS WRITTEN INSIDE.** The player's handler is four
        branches and the `isInteractiveKeyboardTarget(e.target)` early return is
        written **inside the first one** (`TaraSitPlayer.tsx:419-423`).
        `ArrowLeft` and `ArrowRight` are tested five lines below it and
        **outside it** (`:424-429`), so an arrow pressed while the caret is in a
        text field **moves the caret and scrubs the audio**. The estate has
        **exactly one** text-target guard, it is **correct**, and it covers two
        of that handler's four keys — which is why `actsOn` takes the guard per
        **branch**: a register that took it per file would clear the two
        branches it does not cover.

        **AND THE SHELL'S FOURTH BRANCH IS BARE `Escape` CALLING `blur()` ON
        `document.activeElement`**, with no modifier guard and no target guard.
        Escape is how a user dismisses an IME candidate window; **S11.4.b
        measured** that the keydown which does it carries `isComposing: true`
        and `keyCode` 229, and that no product handler reads either. A Japanese,
        Korean or Chinese user backing out of a candidate list also loses the
        field.

        **DECISIONS.** **THE ARITHMETIC SAYS A GUARD ADMITS A COMBINATION, NOT
        THAT A USER PRODUCES ONE.** Whether `AltGr+K` types a character on any
        particular layout is a fact about that layout's third level and this
        register does not hold one — so a modifier is either `held-across-keys`,
        where admitting the combination is admitting the key, or
        `a-named-chord`, which collides only with a branch bound to that key.
        No finding claims a keycap the register cannot see. **WCAG 2.1.4 IS
        SCOPED BY `event.key` LENGTH**, because the criterion covers letters,
        punctuation, numbers and symbols and a `key` of length 1 is exactly one
        of those: three bare bindings produce **one** 2.1.4 fault, and the
        arrows and Escape are reported as collisions rather than as failures of
        a criterion that does not cover them.

        **THE SCAN COPIES NOTHING.** It cuts each branch out of its source by
        matching brackets, derives the required and excluded flags from the
        condition, derives the keys from the key comparisons — and the digit
        branch's five from the **length of its own `routes` array**, the thing
        that would change if a sixth tab were added — and reads the text guard
        from the branch **body**.

        **THE READING:** 22 faults; 5 of 7 branches collide; all four sources
        the cell names are reached; 1 text guard; no remap path in either
        product. **WHAT IS RIGHT AND WORTH SAYING:**
        `resolveShellRouteShortcutIndex` reads `event.code` before `event.key`,
        so `Alt+3` is the third **physical** digit rather than whatever
        character a layout puts there. -->

- [ ] S11.5 Add screen-reader announcement assertions. Evidence: async status
      and error messages are covered.

      <!-- S11.5 LEFT UNCHECKED ON PURPOSE. Five children shipped `[x]` (a–e);
      .f is `[ ]` because its verb is "run" and no reader can be driven here.
      The parent is unchecked because **its evidence line is a coverage claim
      and both halves of it were measured false by its own children.**

      **"ASYNC STATUS … COVERED" — S11.5.c: 11 of the 22 matrix cells are
      progress claims, and a progress claim is about what happened BEFORE the
      result replaced it.** No read of the final DOM contains one. The estate's
      shape is a ternary whose branches are two different nodes, so a progress
      message and its result are two insertions rather than one region speaking
      twice, and **whether the first was ever spoken is unobservable without the
      change observer S11.5.a found nobody aims at a live region.**

      **"ERROR MESSAGES … COVERED" — S11.5.b: 1 of 5 validation fixtures can
      run, and it is the one nobody has written.** There is **no validation
      summary in either product** against 507 submit handlers; a control error
      needs the computed description, which only the AX tree carries and no
      suite asks for; a correction is a SUPPRESSION and a resolution is a
      DEPARTURE, and **a live region is deaf to departures by default**
      (`aria-relevant` = "additions text").

      **AND THE THROUGH-LINE OF THE WHOLE FAMILY: THE ESTATE ALREADY CONTAINS
      SEVEN CORRECT MECHANISMS, EVERY ONE DOCUMENTED WITH ITS INVARIANTS, AND
      ALMOST NOTHING CALLS THEM.** `LiveRegion` (0 shipping callers, and its doc
      states the announcement rule verbatim), `useLiveAnnouncer` (1),
      `useErrorAssociation` (0), `useFocusTrap` (0), `useRovingTabIndex` (0),
      the shell's `announce` (0 importers, mounted app-wide, fails silent),
      `Popover` and `ModalDialog` (0). Meanwhile **808 hand-rolled live
      elements**, **54 hand-rolled dialogs**, and **five separate announcer
      implementations answering one question three ways**.

      What would close this parent is not a test: it is routing the product
      through the mechanisms it already has, a validation summary, and one
      change observer aimed at a live region. Where the estate IS good — 175 of
      180 failure regions announced assertively, two of three virtualized
      surfaces answering position by two different mechanisms, and a route
      announcer the framework provides free — the children say so. -->

  - [x] S11.5.a Instrument accessible name/description/error/message, live
        region role/politeness/atomic/relevant, focus, DOM order, language, and
        visibility without replacing real AT smoke tests. Evidence: assertion
        library.
        <!-- S11.5.a: shipped as
        `libs/oshun/workbench-kit/src/announcement-instruments.ts` (+ `.spec.ts`,
        32 tests) with `tools/announcement-instruments-scan.mjs` (12 negative
        controls, exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/announcement-instruments`,
        `verify:operations:v1-announcement-instruments`, and the
        `announcement-instruments-scan` Nx target. Entry point 208 of 208,
        0 export-map violations.

        **THE VEIN: AN ANNOUNCEMENT IS A CHANGE OBSERVED, AND EVERY ASSERTION IN
        THIS ESTATE READS A STATE AFTER THE FACT.** A live region is announced
        because it changed **while something was watching**. A region inserted
        into the document together with its first message was not there to
        change; on most browser and reader pairings a polite one is silent, and
        **every test that checks the text is present passes**. So the fact
        register carries a **thirteenth** entry the cell does not name —
        `present-before-the-change` — needing a capability
        (`change-observation`) that no attribute read provides.

        **THE ESTATE ALREADY WROTE THAT RULE DOWN.**
        `apps/oshun/web/src/components/workbench/primitives/accessibility.tsx`
        exports a `LiveRegion` whose doc says it in as many words — "THE
        CONTAINER EXISTS BEFORE THE MESSAGE DOES, and getting that backwards is
        the single most common way a live region announces nothing" — rendering
        unconditionally with `aria-atomic="true"` and declaring both invariants
        on the component. **It has three usages: a fixture host and two
        Storybook stories. None of them ships.** Beside it, walked **tag by
        tag** with brace-depth tracking rather than counted by file, the two
        products carry **808 hand-rolled live elements**, of which **25** set
        `aria-atomic` and **464 are gated by a conditional in the expression
        they sit directly inside** — the exact shape the primitive prevents.

        **AND THE 464 ARE NOT 464 DEFECTS**, which is the other half of the
        cell. **313 are `role="alert"` or `aria-live="assertive"`**, and an
        alert added to the document is announced by the major readers — the
        standard toast pattern, not a bug. **151 are polite**, and those are the
        silent case. They are filed **needs-exercising, not fails**, because
        what settles one is an observation this module does not make.

        **WHAT THE SUITES ACTUALLY POINT AT A REGION**, over 5,264 test files:
        **112 role queries**, **23 politeness assertions**, **3 atomic
        assertions**, **0 relevant assertions**, and **four `MutationObserver`s,
        not one of them on a live region** — one samples chip paint, one hides
        the Next dev overlay, one times a search cold start, and the fourth is a
        comment in `panel-timeline.ts` explaining that it samples *instead of*
        observing. The instrument is in the estate's hands four times, aimed
        elsewhere.

        **THE SECOND RULE, AND IT IS WHY THIS IS A LIBRARY AND NOT A LINT: A
        COMPUTED PROPERTY IS NOT AN ATTRIBUTE, AND HALF OF THESE FACTS ARE
        COMPUTED.** `role="status"` **is** polite and atomic, and
        `getAttribute('aria-live')` returns `null` for every one of the
        estate's **331** of them. An accessible name is computed from several
        sources in order, so `aria-label` is an input and not the answer; a
        language is inherited; a visibility is resolved style. So each fact
        declares the **capability** it needs and each instrument what it
        provides, and `settles` is that one comparison rather than a table.

        **THE THIRD — THE CELL'S OWN CAVEAT MADE INTO DATA: NO FACT HERE IS THE
        UTTERANCE.** `WHAT_ONLY_AN_UTTERANCE_SETTLES` lists four questions the
        library refuses, each with its owner: whether a reader speaks an
        inserted polite region and whether it honours
        `aria-relevant="removals"` (**S11.5.f**), and what happens when two
        regions change in one tick and what is said when a message is replaced
        mid-utterance (**S11.5.d**).

        **DECISIONS.** The gate test is **narrow and says so**: it asks whether
        the tag's existence is gated by the expression it sits *directly
        inside*, not whether the element can ever be absent — an early
        `return null` three functions up also decides that — so it under-counts
        on purpose. And `grader-asked-for-a-value` is asked of what the suites
        **aim**, never of the instrument list, where a measurer always exists
        beside axe: **a check that can never fire is not a check** (S10.14's
        trap, caught here before shipping).

        **THE READING:** 4 of 13 facts have an instrument the suites aim at a
        region; 1 of 6 instruments is aimed at one at all; 12 faults. **The scan
        caught its own register**: the 113th role query is a comment about
        Next's route announcer, and **a mention in prose is not a use** →
        112. -->

  - [x] S11.5.b Assert validation summary/control error, focus movement,
        correction, and resolved error announcements. Evidence: validation
        fixtures.
        <!-- S11.5.b: shipped as
        `libs/oshun/workbench-kit/src/validation-announcements.ts` (+ `.spec.ts`,
        29 tests) with `tools/validation-announcements-scan.mjs` (11 negative
        controls, exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/validation-announcements`,
        `verify:operations:v1-validation-announcements`, and the
        `validation-announcements-scan` Nx target. Entry point 209 of 209,
        0 export-map violations.

        **THE VEIN: A LIVE REGION ANNOUNCES ARRIVALS. `aria-relevant` DEFAULTS
        TO "additions text", SO ONE OF THE CELL'S FIVE EVENTS IS SILENT BY
        CONSTRUCTION AND ANOTHER CANNOT BE OBSERVED AT ALL.** Sorted by what has
        to happen rather than by what they are called, the five are not five of
        a kind: **summary** and **control-error** are ARRIVALS; **focus-
        movement** is a MOVE, the only one the platform announces for free (a
        reader speaks whatever took focus — no region involved); **resolution**
        is a DEPARTURE — removing the error node is a removal, the default
        `relevant` does not include removals, so **the error disappearing
        announces nothing**, and the only carrier is a positive message pushed
        into a region that was already there, which turns it back into an
        arrival; and **correction** is a SUPPRESSION — the requirement is that
        nothing is re-announced on every keystroke, and **an absence cannot be
        read out of a final state**.

        **THE SECOND RULE: A SUMMARY IS A PATTERN, NOT A WORD.** Searched as the
        word, `ErrorSummary` appears in five files and **every hit is a job
        record's `lastErrorSummary` column** or a paragraph in a studio
        workspace. Searched as the pattern the cell means — a form-level error
        list whose entries link to their fields — the two products have
        **zero**, against **507 `onSubmit` handlers** and 21 `<form>` tags. The
        cell's first item has nothing to assert.

        **THE THIRD, AND IT IS S11.5.a's SHAPE ONE FLOOR DOWN: THE ESTATE
        CONTAINS THE ANSWER AND ALMOST NOTHING CALLS IT.**
        `apps/oshun/web/src/components/studio/a11y/accessibilityHooks.tsx`
        exports **`useErrorAssociation`** — `aria-invalid` +
        `aria-describedby` + `role="alert"` on the message so it **is announced
        when it appears**, with the association dropped entirely when there is
        no error so nothing points at a missing element — **zero callers**.
        **`useLiveAnnouncer`** keeps stable polite and assertive regions, both
        atomic, clearing then re-setting on a tick so an identical repeated
        message still reads as a change (its own comment: "a remounted aria-live
        region often fails to announce") — **one caller**. `useFocusTrap` and
        `useRovingTabIndex`, same file, **none**.

        **WHAT THE SUITES ASSERT:** 11 mentions of `aria-invalid`, 3
        `aria-describedby` assertions, **0 `aria-relevant` anywhere**, and — the
        number that decides the cell — **not one test that both touches an
        invalid control and asserts what has focus**. 71 `toBeFocused`
        assertions, none after a failed submit.

        **THE READING: 1 of 5 fixtures runnable, and it is `focus-movement` —
        the only one that could run today is the only one nobody has written.**
        The module specifies each fixture and does **not** write them, because
        two of the five have no subject in this estate and a fixture asserting
        them would be asserting its own scaffolding; `fixtureReadiness` computes
        every blocker from **S11.5.a's** registers, so the gate moves when the
        instrument or the product changes rather than when somebody edits a
        table. 11 faults. -->

  - [x] S11.5.c Assert save/offline/retry/conflict/recovery and job/gate/review/
        comment/publish/destructive progress/result announcements. Evidence:
        async state matrix.
        <!-- S11.5.c: shipped as
        `libs/oshun/workbench-kit/src/async-announcements.ts` (+ `.spec.ts`, 22
        tests) with `tools/async-announcements-scan.mjs` (10 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/async-announcements`,
        `verify:operations:v1-async-announcements`, and the
        `async-announcements-scan` Nx target. Entry point 210 of 210,
        0 export-map violations. The matrix is the cell's eleven subjects ×
        two phases = **22 cells**, each saying what the announcement has to
        carry (`conflict/result`: *which version won and what the user has to
        do about it*; `gate/result`: *the verdict and the reason, not just the
        verdict*; `destructive/result`: *what was deleted and whether it can be
        undone*).

        **THE VEIN: A PROGRESS ANNOUNCEMENT AND ITS RESULT ARE WRITTEN AS TWO
        BRANCHES OF ONE CONDITIONAL, SO THEY ARE TWO NODES AND NOT ONE REGION
        SPEAKING TWICE.** `state === 'busy' ? <span role="status">{busyLabel}
        </span> : state === 'error' ? <span role="status">…</span> : null` is the
        estate's shape and it is **two insertions** — S11.5.a's thirteenth fact
        applies to each separately. **Whether the progress message was ever
        spoken before the result replaced it is an ordering claim, and no read
        of the final DOM contains one**, so **11 of the 22 cells are marked not
        fillable from source** and handed to the change observer.

        **THE SECOND RULE, AND IT IS THE DECIDABLE ONE: TONE IS DECIDED BY THE
        OUTCOME, NOT BY THE SUBJECT.** Progress is polite (interrupting a reader
        mid-sentence to say "Saving…" is worse than waiting); success is polite;
        a failure the user's own action produced is **assertive**, because a
        polite failure queues behind whatever is being read and lands after they
        have moved on.

        **AND THE ESTATE IS GOOD AT IT.** Of **464 conditionally-rendered live
        regions**, **180 are gated on a failure condition** and **175 are
        `role="alert"` or assertive** — the strongest accessibility discipline
        measured anywhere in this family. So the deliverable is **the five that
        are not**, named with the gate that renders each:
        `AtelierPlateActions.tsx:218` (`state === 'error'`),
        `SupportContinuityDiagnosticsPanel.tsx:417` (`error &&`),
        `MetisAssessmentEditor.tsx:416` (`submitState === 'error'`),
        `IntegrityAppealWorkspace.tsx:271` (`load.kind === 'load-error'`), and
        `NotificationsCenterPanel.tsx:1691` (`feedState === 'unavailable'`).

        **ONE OF THE FIVE IS RECORDED AS DEFENSIBLE AND FOUR ARE NOT — a
        distinction worth making rather than a number worth inflating.** The
        notifications one announces a **disclosed outage**, an ambient page
        state, which is exactly what polite is for; the other four are results
        of something the user just did.

        **THE THIRD: `aria-busy` IS NOT AN ANNOUNCEMENT.** It tells a reader to
        hold off while a subtree updates and says nothing itself. The products
        carry **216 `aria-busy` tags and exactly one is on a live region** —
        `StudyRouteBoundary.tsx:197`, set only while loading with its politeness
        keyed to its own role, and **it is right**. A register counting
        `aria-busy` as progress coverage would report 216 announcements that do
        not exist.

        **DECISIONS.** The literal-text window (260 chars) is **stated in the
        register**, because the number depends on it — a message beginning 300
        characters in reads as computed at 260 — and the scan uses the same
        window, so the two agree by construction rather than by luck. And the
        scan **fails both ways** on the five: a named site the walk cannot find
        is a repair the register should stop naming; an unnamed one it finds is
        a new defect. 6 faults. -->

  - [x] S11.5.d Assert debouncing/deduplication, priority/interruption, no stale
        queued message, and no announcement flood under rapid events. Evidence:
        event storm tests.
        <!-- S11.5.d: shipped as
        `libs/oshun/workbench-kit/src/announcement-storms.ts` (+ `.spec.ts`, 29
        tests) with `tools/announcement-storms-scan.mjs` (12 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/announcement-storms`,
        `verify:operations:v1-announcement-storms`, and the
        `announcement-storms-scan` Nx target. Entry point 211 of 211,
        0 export-map violations.

        **THE VEIN: A STORM POLICY THAT GROUPS WHAT YOU WILL READ LATER SAYS
        NOTHING ABOUT WHAT IS SPOKEN NOW.** **S7.19.b already solved this** —
        groups and deduplicates event storms, sizes a group over **distinct
        event ids**, states its distinct-target count, names seven kinds that
        may never be collapsed. It operates on the **notification feed**, a list
        somebody opens and reads. An announcement is not a list; it is a moment.
        **None of that policy is on the announcement path.**

        **THE SECOND RULE, WITH THE ESTATE'S OWN CODE AS EVIDENCE: DEDUPLICATION
        AND RE-ANNOUNCEMENT ARE THE SAME MECHANISM RUN BACKWARDS, AND FIVE
        ANNOUNCERS ANSWER ONE QUESTION THREE WAYS.** **Three say yes on
        purpose** — `useLiveAnnouncer` clears then re-sets after a tick, the
        shell's `announce` clears then writes on the next frame ("so repeated
        identical messages still trigger"), `KeyboardTriageReviewSurface` bumps
        an id. **One says no by accident** — `StudyWorkbench`, the most-called
        announcer at **ten sites** with every overlay handler routed through it,
        is `setAnnouncement(message)` with no id and no timer, so **React bails
        out on an equal string**: deduplication as a consequence of `setState`
        rather than a decision, and the estate's only implementation of it. And
        the `LiveRegion` primitive is a container, not a queue. Repeating is
        right when the same thing happened again and wrong when it is still
        true, and **nothing distinguishes those two cases**.

        **THE THIRD: NO ANNOUNCER HAS A RATE LIMIT**, and the two most-called
        are on repeat paths — `KeyboardTriageReviewSurface` announces the
        artifact ref, priority and stage on **every arrow move** through a
        review queue.

        **THE FOURTH: THE MOST GLOBALLY AVAILABLE ANNOUNCER IS CALLED BY NOBODY
        AND WOULD FAIL SILENT IF IT WERE.** `AccessibilityShell` exports
        `announce(message)` and mounts its region from `layout.tsx` on every
        page; the body is `announceRef?.(message)` on a nullable module
        singleton, **returning `undefined` whether or not anything was said**,
        with **zero importers**.

        **A WORD IS NOT THE THING, FOR THE FOURTH TIME IN THIS FAMILY.**
        `announceRecordsChanged` is a DOM event bus for stale data and reaches
        no reader. And the estate's **event storm tests number zero**: `storm`,
        `rapid`, `burst`, `flood` across 5,264 suite files yield a **comment
        about announcement tone**, a **mass-share 429 guard**, and a character
        named **Kaelen Stormweaver** — all three recorded, because a zero
        nobody can reproduce is worth nothing.

        **DECISIONS.** **A call site is not a shipping call site**: the
        primitive has three and none ships, so both numbers are carried rather
        than one quietly filtered into the other — **the scan derived three
        where the register said zero**, which is how the distinction got made.
        And each announcer's calls are counted **the way its own callers reach
        it**: a bare `announce(` belongs to the module function only in a file
        that imports it, without which the count attributed ten of
        `StudyWorkbench`'s calls to a function nothing imports. **Four of the
        five requirements are producer-side and one is not** — which of two
        regions is spoken first is the reader's queue, already filed by S11.5.a
        as S11.5.f's. 7 faults. -->

  - [x] S11.5.e Assert dynamic counts, selection, virtualized position,
        sort/filter changes, dialog/drawer, route transition, and focus
        restoration context. Evidence: navigation fixtures.
        <!-- S11.5.e: shipped as
        `libs/oshun/workbench-kit/src/navigation-announcements.ts` (+ `.spec.ts`,
        23 tests) with `tools/navigation-announcements-scan.mjs` (10 negative
        controls, exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/navigation-announcements`,
        `verify:operations:v1-navigation-announcements`, and the
        `navigation-announcements-scan` Nx target. Entry point 212 of 212,
        0 export-map violations.

        **THE VEIN: THE SAME FACT HAS A DIFFERENT MECHANISM IN EVERY PATTERN,
        AND A REGISTER THAT LOOKS FOR ONE OF THEM REPORTS THE OTHERS AS
        ABSENT.** "Where am I in this list" is `aria-setsize`/`aria-posinset` in
        a listbox or tree, `aria-rowcount`/`aria-rowindex` in a grid, and a live
        region stating the visible range in a plain scrolling list — **all three
        correct**. This cell was **one grep from a false finding**: `aria-setsize`
        appears in six files and **none of them virtualizes**, which reads as
        "every virtualized surface is silent" until you look. Of the estate's
        **three real virtualized surfaces, two answer position and they use two
        different mechanisms** — `VirtualList` renders an `srOnly` polite region
        saying "Showing items N to M of TOTAL"; `DataTable` sets `aria-rowcount`
        to the total and `aria-rowindex` to the **absolute** index, with a
        comment saying why. The one that answers by none is the compare matrix.

        **AND `overscan` IS NOT A VIRTUALIZATION MARKER EITHER.** Seven files
        match it: three virtualize, two are non-JSX helpers, and **two are other
        trades entirely** — a cinematography workspace's **broadcast overscan
        safety** and a drone panel's **frustum overscan cushion**. A word is not
        the thing, for the fifth time in this family.

        **THE SECOND: ONE OF THE SEVEN IS THE FRAMEWORK'S, AND THE ESTATE'S ONLY
        INTERACTION WITH IT IS AVOIDING IT.** Next renders `<div role="alert"
        id="__next-route-announcer__">` and speaks the new page's title on every
        client navigation — **already there, on every route, for free**. The
        estate's only two mentions are comments in one spec explaining that a
        locator had to be scoped to `p[role="alert"]` **so as not to collide
        with it**. Recorded as `framework-provided` rather than present or
        missing, because both of those words would be wrong.

        **THE THIRD — THIS FAMILY'S RUNNING TOTAL: `overlay.tsx` EXPORTS A
        CORRECT `Popover` AND `ModalDialog` AND NOTHING THAT SHIPS USES
        EITHER.** The popover reads `document.activeElement` **at open time
        rather than assuming**, and restores only if the saved element
        `isConnected` — because `focus()` on a detached element does nothing and
        says nothing about it. The dialog uses `showModal()`, "so the inertness
        is the platform's and covers what a JS trap cannot reach". **Eight call
        sites, all specs and stories, zero shipping** — against **54 shipping
        files that hand-roll `role="dialog"`** and **five that restore focus at
        all**. Across S11.5 that makes **seven correct mechanisms with almost no
        adoption**: `LiveRegion` 0, `useLiveAnnouncer` 1, `useErrorAssociation`
        0, `useFocusTrap` 0, `useRovingTabIndex` 0, the shell's `announce` 0
        importers, `Popover`/`ModalDialog` 0.

        **TRAP CAUGHT BY THE SCAN:** the register quoted `ModalDialog`'s
        invariant **without its backticks** around `showModal()`, and the
        verbatim check failed. **A quotation missing one character is not a
        quotation** — same family as S10.14's ellipsis. The scan also tightened
        four counts my shell greps had inflated by not excluding stories,
        fixtures and prose (90→76, 202→193, 55→54). 6 faults. -->

  - [ ] S11.5.f Run representative NVDA/JAWS/VoiceOver or supported real screen-
        reader speech-log journeys and reconcile differences from DOM-level
        assertions. Evidence: AT matrix.
        <!-- S11.5.f LEFT UNCHECKED ON PURPOSE, with the matrix shipped empty:
        `libs/oshun/workbench-kit/src/at-reconciliation.ts` (+ `.spec.ts`, 27
        tests) with `tools/at-reconciliation-scan.mjs` (10 negative controls),
        registered as `@oshun/workbench-kit/at-reconciliation`,
        `verify:operations:v1-at-reconciliation`, and the
        `at-reconciliation-scan` Nx target. Entry point 213 of 213.

        **THE CELL'S VERB IS "RUN", AND NO SCREEN READER CAN BE DRIVEN FROM THIS
        HOST.** NVDA, JAWS and Narrator are Windows; VoiceOver is Apple's;
        TalkBack is Android's; Orca needs a desktop session with a running
        speech service. And — measured rather than assumed — **nothing in this
        repository drives one**: `speechLog` and `speech-log` appear nowhere
        outside this cell's own family, and the estate's only register of
        readers is `platformScreenReaderApis` in
        `libs/iris/accessibility/src/index.ts`, a **six-name array inside a
        bridge policy**, which is a declaration and not a driver.

        **SO ALL 36 CELLS ARE `unobserved` AND NOT ONE IS FILLED IN FROM
        MEMORY.** What a reader does with an inserted polite region, or with
        `aria-relevant="removals"`, or with two regions changing in one tick, is
        a fact about a program nobody here has run. **Writing those verdicts
        down because they are widely believed would be exactly the thing the
        rest of S11.5 exists to catch.**

        **WHAT IS SHIPPED, AND IT IS THE CELL'S SECOND CLAUSE.** **The
        questions**: six, none invented here — four imported from S11.5.a's own
        list, one S11.5.b's departure, one S11.5.d's queue — each carrying **who
        raised it and who owns answering it**, which are *not* the same cell
        (S11.5.a raised all four of its own; two are owned by S11.5.d because
        they are about a queue, not a reader). **The pairs**: a reader is never
        tested alone but against a browser, because the tree that reaches it is
        the browser's — so the matrix is 6 pairings × 6 questions. **The
        reconciliation**, which is not "did they match": a DOM assertion passing
        while the reader is silent has **three** meanings — the assertion was
        wrong, the reader does not support the pattern, or the pattern is
        ambiguous and both are defensible — and collapsing them into "fail"
        tells nobody what to change. The reverse — the reader speaks where the
        DOM said nothing — is **always** the assertion being wrong, because an
        utterance is the ground truth this family has been approximating.

        **AND WHAT A SPEECH LOG HAS TO BE:** not a transcript but a
        **time-ordered list of utterances on a clock alignable with the page's
        own events** — without it, "the save message was spoken" cannot be told
        from "the save message was spoken after the user had already navigated
        away", which is the whole reason S11.5.d exists. The register states the
        requirement and deliberately **does not name a vendor facility**, since
        which switch produces such a log on a reader nobody here has run is one
        more claim about an unobserved system.

        **WHAT WOULD CLOSE IT:** a Windows host with NVDA or JAWS, an Apple host
        with VoiceOver, or a desktop Linux session with Orca; a speech-log
        capture meeting the four stated requirements; and a product decision
        about which pairs are supported. None of those is a test somebody forgot
        to write. -->

- [ ] S11.6 Add responsive and theme visual-regression baselines. Evidence:
      approved baseline matrix.

      <!-- S11.6 LEFT UNCHECKED ON PURPOSE. All seven children are `[x]`. The
      parent is unchecked because **its evidence line asks for an APPROVED
      baseline matrix, and S11.6.g measured that nothing approves anything.**

      **THE MATRIX EXISTS.** `libs/oshun/design-language/src/visual-qa.ts` holds
      nineteen claims, eighteen of them `blocking: true`, each naming its spec,
      test title, state, viewports, evidence kind and capture artifact — and its
      own doc says the mappings exist so they can be "required at signoff time".

      **NOTHING APPROVES IT.** No hook and no workflow notices a baseline
      change. The one CI job named Visual Regression is inert **five independent
      ways** — `|| true`, a target declared in zero `project.json` files, six
      specs that are not behind that target, an `if: failure()` upload on a job
      that cannot fail, and globs that name `__snapshots__` where Playwright
      writes `<spec>.spec.ts-snapshots`. **Three of the five would each stop it
      alone.**

      **AND THE TWO AXES THE PARENT NAMES ARE THE TWO LEAST COVERED.**
      *Responsive*: 3 of 7 viewport cases declared, 3 exercised, **and not the
      same three** — zoom/reflow declared and never driven, orientation driven
      only by the mobile Detox suite, and persistent scrollbar, software
      keyboard and panel arrangement neither. *Theme*: the product declares
      three Lilith variants and **every harness lists two — the missing one is
      `cream-hc`, the high-contrast theme**, so a contrast regression in it
      changes no committed pixel.

      **AND WHAT THE MATRIX WOULD BE APPROVING.** 4 of 19 citations resolve as
      written (12 more on a dash); 7 of 32 state-index cells are filled and 69
      of 74 baselines are a surface at rest; 1 of 6 content shapes is captured,
      with a 505-line bidi library and a 404-line pseudo-localiser rendering
      into none of them; 91 pixel tolerances carry seven unnamed values; and a
      signature over the pixels covers **2 of the 8 review dimensions**, because
      six are decided by geometry, an engine rule, a focus assertion or a
      semantic read — and one of those, semantic state, **no threshold can
      reach**.

      What would close this parent is a gate: notice a baseline change, require
      a reason and a linked implementation, and fail without them — three of
      those four decidable from a diff and a commit message. Where the estate IS
      good, the children say so: the masks are named and justified region by
      region, `unstableRegions` is headed "Regions whose CHANGE is not a
      regression", the theme helpers refuse to plant an attribute the product
      never sets, and `tables-and-details` pins six controls and needs one
      tolerance. -->

  - [x] S11.6.a Define supported narrow/medium/wide, zoom/reflow, orientation,
        DPR, persistent scrollbar, software keyboard, and panel arrangement
        viewport cases. Evidence: viewport manifest.
        <!-- S11.6.a: shipped as
        `libs/oshun/workbench-kit/src/viewport-manifest.ts` (+ `.spec.ts`, 20
        tests) with `tools/viewport-manifest-scan.mjs` (11 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/viewport-manifest`,
        `verify:operations:v1-viewport-manifest`, and the
        `viewport-manifest-scan` Nx target. Entry point 214 of 214,
        0 export-map violations.

        **THE VEIN: A CITATION IS A STRING MATCH, AND A DASH IS NOT A DASH.**
        `libs/oshun/design-language/src/visual-qa.ts` is a **good** register —
        nineteen claims, each naming the shell track, surface, UX claim,
        **Playwright spec**, **test title**, state id, viewports, evidence kind,
        capture artifact and whether it blocks; **18 of 19 blocking**; its own
        doc says the mappings exist so they can be "queried, validated, and
        required at signoff time". **All 19 name a spec file that exists. Four
        name a test title that does.** **Twelve more resolve the moment ` - ` is
        read as ` — `** — the register writes a hyphen-minus, the specs write an
        em dash (44 of 44 titles). **The test is there and the citation cannot
        find it**, so a validator taking the register at its word reports twelve
        missing tests that are not missing. **Three are genuinely absent**
        (`UX-SHELL-01`, `UX-ADMIN-SHELL-01`, `UX-ADMIN-QUEUE-01`).

        **THE SECOND: A BASELINE CARRIES THE PLATFORM THAT MADE IT.** Playwright
        stamps `-{project}-{platform}` onto every snapshot name, and the
        committed baselines are **67 `chromium-darwin` and 16
        `chromium-linux`**. A run on a platform with no baseline **does not fail
        the comparison — there is nothing to compare** — so the same suite is a
        regression test on a Mac and a **baseline writer** everywhere else, and
        which one is decided by the runner's OS rather than by the suite.

        **THE THIRD: THREE MANIFESTS, AND THE SIX VISUAL-REGRESSION SPECS USE
        NONE OF THEM.** `OSHUN_QA_VIEWPORTS` (7 sizes), `e2e-inspect`'s
        `VIEWPORTS` (3 lenses **with scale factors and a 200% zoom**), and
        S11.3.c's `OSHUN_AXIS_SUPPLY` (2). The specs declare their sizes inline
        — **49 literal sizes in one file, 30 in another** — and `mobile-large`
        is named by the manifest and claimed by nothing.

        **THREE OF SEVEN CASES ARE DECLARED AND THREE ARE EXERCISED, AND THEY
        ARE NOT THE SAME THREE:** `zoom-reflow` declared in the inspect harness
        and driven nowhere; `orientation` driven by the mobile Detox suite and
        declared nowhere; **persistent scrollbar, software keyboard and panel
        arrangement neither** — each recorded with what it costs (a software
        keyboard shrinks the **visual** viewport without changing the layout
        viewport; a persistent scrollbar moves a layout across a breakpoint a
        headless run never crosses).

        **WHAT IS RIGHT AND WORTH SAYING:** `lenses.ts` refuses to plant the
        theme attribute from an init script — the default theme is the
        attribute's **absence**, so forcing it is a state the product never has
        — and ships `expectThemeApplied` because "without this check a 'both
        themes' pass can silently inspect the same theme twice". **A positive
        control, arrived at independently.**

        **The scan expands the `<viewport>` template and strips the platform
        stamp before looking for an artifact** — a check that skipped either
        would report 13 missing baselines that are committed. 11 faults. -->

  - [x] S11.6.b Define light/dark/high-contrast/forced-colors/branded themes,
        comfortable/compact/touch densities, reduced motion, and color scheme.
        Evidence: context manifest.
        <!-- S11.6.b: shipped as
        `libs/oshun/workbench-kit/src/context-manifest.ts` (+ `.spec.ts`, 21
        tests) with `tools/context-manifest-scan.mjs` (11 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/context-manifest`,
        `verify:operations:v1-context-manifest`, and the `context-manifest-scan`
        Nx target. Entry point 215 of 215, 0 export-map violations.

        **THE VEIN: THE ONE THEME THE CELL NAMES AS ITS OWN CASE IS THE ONE
        EVERY HARNESS LIST OMITS.** The product declares **three** Lilith
        variants — `cream`, `cream-hc`, `dusk` — as a union type with a matching
        guard in `LilithThemeBootstrap.tsx`, and **`cream-hc` is the
        high-contrast one**, with its own block in `globals-v2.css`. The inspect
        harness lists two. The assistant visual spec carries its **own copy** of
        the same `useTheme` helper and lists the same two. **The theme whose
        entire purpose is contrast is the one no visual baseline is taken in**,
        so a contrast regression in it changes no committed pixel.

        **THE SECOND — S11.3.f ARRIVING FOR A SECOND CONTROL FAMILY: THE
        INSTRUMENT EXISTS, IT IS GOOD, AND IT IS AIMED AT THE GALLERY.**
        `forced-colors` is answered by **13 shipping stylesheets** and **eleven
        `*-browser.mjs` harnesses emulate `forcedColors: 'active'`**, their docs
        explaining what must stay distinct under it — and **every one mounts a
        fixture host**. The six visual-regression specs that capture a product
        page emulate it **zero** times. `prefers-reduced-motion`: 45 shipping
        files, three harnesses, **one** visual spec (for the good reason that
        freezing animation makes a capture stable — which also means
        `no-preference` is the rendering never captured).
        `prefers-color-scheme`: four files, **no** visual spec. **A stylesheet
        that answers a media query no capture emulates is a branch no baseline
        has ever rendered.**

        **THE THIRD: A DENSITY REGISTER EXISTS AND IT IS A FIXTURE'S.** The
        cell's three densities are exactly the union in
        `primitiveBaselinesHost.tsx`; the products use `data-density` in 16
        files; and the word `density` carries **three other vocabularies** in
        this estate (`expanded`, `balanced`, `standard`, `spacious`).

        **WHAT IS RIGHT, TWICE:** both theme helpers refuse to plant the
        attribute from an init script — the default theme is the attribute's
        **absence** — and both verify it took, because "without this check a
        'both themes' pass can silently inspect the same theme twice". **A
        positive control, arrived at independently, and the second time by
        copying the first.**

        **CORRECTED BY THE SCAN:** I first wrote that `forcedColors` appears
        nowhere in the repository. It appears in eleven harnesses. The register
        now counts harness uses **separately** from spec uses, because folding
        them together would let eleven harnesses answer for a product none of
        them loads. Two file counts also tightened (21→13, 54→45) once
        spec/test files were excluded. 9 faults. -->
  - [x] S11.6.c Define representative Latin/long pseudo/RTL/mixed-bidi/CJK/
        fallback-font content and deterministic data/clock/assets. Evidence:
        locale fixture manifest.
        <!-- S11.6.c: shipped as
        `libs/oshun/workbench-kit/src/locale-fixtures.ts` (+ `.spec.ts`, 20
        tests) with `tools/locale-fixtures-scan.mjs` (11 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/locale-fixtures`,
        `verify:operations:v1-locale-fixtures`, and the `locale-fixtures-scan`
        Nx target. Entry point 216 of 216, 0 export-map violations.

        **THE VEIN: A 505-LINE BIDI LIBRARY WITH NEUTRAL PROTECTION, AND NO
        BASELINE RENDERS ONE RIGHT-TO-LEFT PIXEL.**
        `studio/i18n/rtlLayout.ts` names **ten surfaces and ten direction
        policies**, maps physical properties to logical ones, and carries
        `assertNoPartialMirroring`, `findUnprotectedNeutrals` and
        `protectNeutralIn` — **the mixed-bidi mechanism itself**, the thing that
        stops a neutral character between an English run and an Arabic one
        landing on the wrong side. Pure, with a React binding and its own spec.
        **Two product files import it. Zero baselines are captured in
        `dir="rtl"`.**

        **THE SECOND — THE SAME SHAPE AGAIN: THE PSEUDO-LOCALISER EXISTS TOO
        AND NOTHING IMPORTS IT.** `i18n/localizationChecks.ts` is **404 more
        lines** — `pseudoLocalize`, `isPseudoLocalized`, expansion bands, a
        per-language expansion table, first-strong isolates — with a conformance
        spec driving it and **zero product importers**. **Two libraries, 909
        lines, one spec each, two product files between them.**

        **A NOTE ON HOW THAT WAS FOUND, BECAUSE IT IS THIS CELL'S OWN LESSON.**
        The register first said pseudo-localisation **did not exist**: a grep for
        `pseudo` that excluded CSS pseudo-elements returned the BFF's redaction
        **pseudonyms**, and reading the head of that list was enough to conclude
        there was nothing there. **The scan's positive control — "the word is
        present, so the zero is not a broken query" — caught it.** *A zero from
        a query nobody positive-controlled is not a measurement.*

        **THE THIRD — DETERMINISM: THERE IS ONE CLOCK FREEZE IN THE ESTATE AND
        IT IS A PRIVATE FUNCTION IN ONE SPEC.** `freezeTime` is declared at line
        308 of `shell-first-impression-visual.spec.ts`, called once, **exported
        nowhere**; the other five visual specs do not freeze time. Data is
        stubbed however each spec felt like (7 route stubs in one, none in
        another); **nothing pins a font or an image**. A baseline is a claim
        that the same inputs produce the same pixels, and each spec makes that
        claim with its own arrangement.

        **THE READING:** 1 of 6 content shapes captured, **6 of 6 supported**;
        2 of 3 determinism controls arranged and **0 shared**. 8 faults. -->
  - [x] S11.6.d Capture shell/workspace/inspector/status plus loading/empty/
        error/degraded/conflict/destructive/complex visualization states.
        Evidence: state baseline index.
        <!-- S11.6.d: shipped as
        `libs/oshun/workbench-kit/src/state-baselines.ts` (+ `.spec.ts`, 20
        tests) with `tools/state-baselines-scan.mjs` (8 negative controls, exits
        2 if any goes undetected), registered as
        `@oshun/workbench-kit/state-baselines`,
        `verify:operations:v1-state-baselines`, and the `state-baselines-scan`
        Nx target. Entry point 217 of 217, 0 export-map violations.

        **THE VEIN: A BASELINE SUITE PHOTOGRAPHS THE STATE THE PRODUCT IS IN
        WHEN NOTHING IS WRONG.** 74 unique baselines; reading their own
        filenames — **the only place a capture says what it captured** — **five
        name a state** (`page-404-desktop`, `page-error-desktop`,
        `page-home-loading-desktop`, `page-loading-skeleton-desktop`,
        `page-explore-offline-mobile`). The other **69 are a surface at rest**.
        Those are worth having, and they are **the state a regression is least
        likely to reach first**.

        **THE SECOND: ONE OF THE FOUR SURFACES THE CELL NAMES HAS NO BASELINE AT
        ALL.** shell 11, workspace 2, inspector 1, **`status` none** — and the
        cell asks for it because a status region is where a degraded or
        conflicted system says so.

        **THE THIRD — WHY THIS IS AN INDEX AND NOT A COUNT: THE MISSING STATES
        ARE THE DEAR ONES.** Loading, error and degraded come from a **route
        stub**, which these specs already write. Empty needs a deliberately
        empty fixture. **Conflict needs two writers and a version that lost.**
        **Destructive needs an irreversible act that must not happen.** A
        complex visualisation is only complex with enough data to be. **Every
        missing state needs a built fixture; every present state is one a route
        stub produces** — a fact about the cost of the capture, not about
        anybody's diligence.

        **THE READING: 7 of 32 index cells filled (3 of 4 surfaces, 5 of 8
        states), 0 unfilled states a route stub could produce.** The scan
        rebuilds the index from the committed filenames, because a title can say
        "loading state" and photograph a settled page. 5 faults. -->
  - [x] S11.6.e Mask only proven nondeterministic data and retain layout/content
        areas so regressions cannot be hidden. Evidence: mask review.
        <!-- S11.6.e: shipped as `libs/oshun/workbench-kit/src/mask-review.ts`
        (+ `.spec.ts`, 19 tests) with `tools/mask-review-scan.mjs` (9 negative
        controls, exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/mask-review`,
        `verify:operations:v1-mask-review`, and the `mask-review-scan` Nx
        target. Entry point 218 of 218, 0 export-map violations.

        **THE VEIN: THE MASK IS THE PART THAT IS REVIEWED AND THE TOLERANCE IS
        THE PART THAT IS NOT.** The estate masks, and it masks **well**: two of
        six specs pass a `mask`, both through a named helper, both saying what
        they cover and why. `unstableRegions` is headed **"Regions whose CHANGE
        is not a regression"** with a comment per selector — the greeting says
        "Good morning" or "Good evening" without anything having regressed; the
        diagnostics blocks are ids and counters *put behind a disclosure
        precisely because they are not member copy*; the account chip carries a
        seeded name encoding a timestamp. That is exactly the cell's ask, and a
        mask **keeps the layout**.

        **AND THEN THERE ARE 91 PIXEL TOLERANCES CARRYING SEVEN DIFFERENT
        NUMBERS, NONE OF THEM NAMED** — 0.02 (35×), 0.03 (18×), 0.01 (17×),
        0.001 (16×), 0.004, 0.015, 0.05. A tolerance is **unbounded in
        location**: it forgives a difference of any nature anywhere in the
        capture as long as it is small, so it **cannot be reviewed region by
        region**. The estate *has* a named tier register — `VISUAL_THRESHOLD`,
        three tiers with a comment each — as a `const` inside one spec **of
        another domain**, reachable by no V1 spec.

        **THE SECOND, WHICH THE MATRIX MAKES VISIBLE: THE TOLERANCE IS WHAT EACH
        SPEC PAYS FOR THE CONTROLS IT DOES NOT HAVE.** No two of the six control
        the same things. `tables-and-details` pins viewport, locale, `timezoneId:
        'UTC'` and reduced motion, disables animations, **awaits
        `document.fonts.ready` so baseline and replay paint with the same
        font**, and masks a volatile-selector register — and needs **one**
        tolerance. `design-system` controls **nothing** and carries **20**.
        `visual-regression-pages` controls one thing and carries **47**. Not
        diligence — arithmetic: every control removed is bought back as slack,
        and **slack is the suppression nobody can review**.

        **TRAP, RECORDED IN THE SCAN'S OWN HEADER:** `git grep --
        'apps/oshun/*/e2e'` **matches nothing** in this repository and returns
        **zero rather than an error**. An earlier draft of this register
        concluded from those zeros that the estate masked nothing, set no
        tolerance and disabled no animation — **the exact opposite of the
        truth**. Every path in the scan is now a literal directory, and the
        controls positive-check each count. 4 faults. -->
  - [x] S11.6.f Review at intended pixel/structural thresholds for overflow,
        clipping, overlap, focus, contrast, semantic state, card/chrome drift,
        and font/icon change. Evidence: signed baseline matrix.
        <!-- S11.6.f: shipped as
        `libs/oshun/workbench-kit/src/review-dimensions.ts` (+ `.spec.ts`, 20
        tests) with `tools/review-dimensions-scan.mjs` (8 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/review-dimensions`,
        `verify:operations:v1-review-dimensions`, and the
        `review-dimensions-scan` Nx target. Entry point 219 of 219,
        0 export-map violations.

        **THE VEIN: A PIXEL DIFF NOTICES A DIFFERENCE AND CANNOT NAME ITS
        KIND.** The eight are not eight of a kind:

        **Two are a screenshot's own** — card/chrome drift and font/icon change
        have **no structural signature**: same box, same roles, same geometry,
        different picture. The 74 baselines are exactly the right instrument.

        **Three are geometry** — overflow is `scrollWidth > clientWidth`,
        clipping is a box outside its container, overlap is two boxes
        intersecting. Each is a **decision that arrives with the number that
        made it**, which a diff image never has. **The estate agrees**: its
        visual-QA register carries an `evidenceKind` field and **both claims it
        marks `playwright-layout-assertion` are about horizontal spill**, with a
        comment saying to use them "only for viewport-fit claims whose primary
        evidence is measured geometry and overflow checks". The answer exists
        and is applied to **one geometric dimension of three**, while **397
        suite files already touch a geometry API** — so choosing a picture over
        a measurement is a choice, not a capability gap.

        **Two have a better instrument elsewhere, pointed away** — contrast is
        axe's `color-contrast` (S11.3.f: aimed at the primitive gallery); focus
        is S11.4.a's (**one of seven claims established**).

        **And one is invisible to a screenshot entirely** — `aria-pressed`
        flipping changes **no pixel** unless the styling depends on it, and a
        semantic state that no longer matches the visual one is exactly the
        defect worth catching. **No threshold, however low, brings it into a
        picture.**

        **WHAT THAT MEANS FOR THE SIGNATURE:** a signed baseline matrix claims
        somebody looked at eight things; six are decided by an instrument other
        than the picture, four of those exist and point elsewhere, and **a
        signature over the pixels alone covers two**. 7 faults. -->
  - [x] S11.6.g Require named review/update reason and linked implementation for
        baseline changes. Evidence: visual approval gate.
        <!-- S11.6.g: shipped as
        `libs/oshun/workbench-kit/src/visual-approval-gate.ts` (+ `.spec.ts`, 19
        tests) with `tools/visual-approval-gate-scan.mjs` (9 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/visual-approval-gate`,
        `verify:operations:v1-visual-approval-gate`, and the
        `visual-approval-gate-scan` Nx target. Entry point 220 of 220,
        0 export-map violations.

        **THE VEIN: A GATE THAT CANNOT FAIL IS NOT A GATE.** **Nothing in this
        repository gates a baseline change** — not the pre-commit hook (which
        locks V2 binary assets and ratchets six conventions and never mentions a
        `.png`), not any workflow. And the one CI job named **Visual
        Regression** is inert **five times over**, each confirmed against the
        file that carries it:

        1. **`|| true`** — `pnpm nx run-many --target=test:visual --all || true`
           discards the exit code, so a failing comparison passes.
        2. **The target does not exist** — `test:visual` is declared in **zero**
           `project.json` files; the one `package.json` with it belongs to
           another domain's admin app.
        3. **None of the six V1 visual specs is behind it** — they run under the
           `e2e` targets, so even a real `test:visual` would not reach them.
        4. **The artifact upload is `if: failure()`** — a job that cannot fail
           never uploads, so **a human cannot review what a human is not
           shown**.
        5. **The globs do not match the baselines** — it collects
           `__snapshots__` and `visual-diff`; Playwright writes
           `<spec>.spec.ts-snapshots`. The **34 `__snapshots__` directories that
           do exist hold vitest text snapshots**, and `visual-diff` exists
           nowhere.

        **Three of the five would each stop the job alone, and that count is the
        deliverable:** a reader who sees the `|| true` and deletes it will
        conclude the gate now works, and it will still be running a target
        nothing declares over specs that are not behind it.

        **AND THE SAME JOB IS WHY S11.6.a's PLATFORM SPLIT MATTERS:** it runs on
        `ubuntu-latest`, and **16 of 83** committed baselines carry that stamp —
        so even a working comparison would find no baseline for most captures
        and **write one rather than fail**.

        **WHAT A GATE WOULD HAVE TO DO** is recorded rather than implemented:
        notice a baseline change, require a reason, require a linked
        implementation, and **fail** when either is missing — **three of the
        four decidable from a diff and a commit message**, and the fourth not a
        measurement at all but a decision to exit non-zero, which is exactly the
        one the existing job discards. 4 faults. -->
- [ ] S11.7 Add deterministic clock, UUID, provider, queue, and storage test
      adapters. Evidence: no production adapter imports test fixtures.

      <!-- S11.7 LEFT UNCHECKED ON PURPOSE. All seven children are `[x]`, and
      unlike its neighbours this family **built things**: five working adapters,
      not five registers — `clock-adapter`, `id-adapter`, `provider-adapter`,
      `queue-adapter`, `storage-adapter`, each with its own suite driving
      behaviour, plus `adapter-contracts` (the measurement) and
      `test-only-boundary` (the enforcement).

      **The parent is unchecked because its evidence line is a claim nothing
      can currently make.** "No production adapter imports test fixtures" is
      S11.7.g's subject, and S11.7.g measured: **six libraries publish a
      `./testing` subpath and one of them is banned**; the estate's rule is
      exemplary and **scoped to one package**; and the only bundle check
      **weighs** the artifact rather than reading it, so **a budget in kilobytes
      is asked to answer "did the forgery kit ship"**. Five of the six
      publishers could be imported from production today and nothing would
      say so.

      **AND THE ADAPTERS ARE BUILT, NOT WIRED.** They are contract subpaths of
      the kit; the products still take `now` in **twelve incompatible shapes**
      (784 declarations), still call `randomUUID` **179 times** in the BFF with
      no seam, and still have no queue port. The one subject that already had a
      real port — object storage, with `AssetObjectStore`, a factory and
      `assetResolverFrom(download)` — is the shape the other four lack, and it
      is the estate's own work rather than this family's.

      What would close this parent is a wiring and a rule: route the products'
      `now` and id calls through the ports, extend the S1.13.e ban to the other
      five `./testing` publishers, and add a scan that **reads** a built
      artifact rather than weighing it. Three of those are mechanical; the
      fourth — deciding which shape of `now` every call site should take — is
      the one that needs a person. -->

  - [x] S11.7.a Define production interfaces and explicit test-only entry points
        for clock/time zone/timer, stable/random ID, provider, queue/worker, and
        blob/object storage. Evidence: adapter contracts.
        <!-- S11.7.a: shipped as
        `libs/oshun/workbench-kit/src/adapter-contracts.ts` (+ `.spec.ts`, 20
        tests) with `tools/adapter-contracts-scan.mjs` (10 negative controls,
        exits 2 if any goes undetected), registered as
        `@oshun/workbench-kit/adapter-contracts`,
        `verify:operations:v1-adapter-contracts`, and the
        `adapter-contracts-scan` Nx target. Entry point 221 of 221,
        0 export-map violations.

        **THE VEIN: THE SEAM IS EVERYWHERE AND THE CONTRACT IS NOWHERE.** The
        estate injects `now` **784 times** across the oshun and shared code, in
        **twelve distinct shapes** — three value types (`string`, `Date`,
        `number`) × two forms (a value or a getter) × two arities (required or
        optional), and **every one of the twelve is in use**: `now: string` 123,
        `now: Date` 105, `now?: () => Date` 102, `now: () => Date` 102, down to
        `now?: number` 4. **Not a missing seam — a seam at almost every call
        site with no contract behind any of them**, so a controllable clock has
        to be written once per shape, and the cell's evidence line ("no
        production adapter imports test fixtures") **cannot even be asked**,
        because there is no adapter to ask it about.

        **AND THERE ARE NAMES, PLURAL, FOR THE SAME THING:** `ai-platform`
        exports an `interface Clock`; `creative-autonomy` exports **`type Clock
        = () => number` AND `type NowFn = () => number` in the same domain**.

        **THE SECOND HALF IS ALREADY SOLVED ELSEWHERE:** **six libraries declare
        a `./testing` subpath** in their own `package.json` — exactly the
        explicit test-only entry point the cell asks for, enforceable by the
        module boundaries `eslint.config.js` already declares. **None of the
        five subjects has one.**

        **THE THIRD — THE ESTATE'S OWN ANSWER AGAIN: ONE OF THE FIVE ALREADY HAS
        THE PORT.** `apps/oshun/bff/src/assets/asset-store.ts` declares
        `AssetResolver`, an `AssetObjectStore extends AssetResolver`, a
        `resolveAssetObjectStore` factory, and — the part that matters —
        **`assetResolverFrom(download)`, a constructor that takes the one
        function a double has to supply**; a `GeneratedArtifactObjectStoreFactory`
        and an integration test sit beside it. Built, **for one subject of
        five**, while the BFF calls `randomUUID` **179 times** with no seam at
        all.

        **A NOTE ON HOW THAT WAS FOUND, BECAUSE IT IS THE SAME LESSON TWICE:**
        this register first said the BFF touched **no** object storage, because
        a shell loop handed an escaped-alternation pattern to `git grep -lE` and
        got zero. **The scan's positive control caught it** — exactly as it
        caught the pseudo-localiser in S11.6.c. *A zero from a query nobody
        positive-controlled is not a measurement.* 10 faults. -->
  - [x] S11.7.b Implement controllable wall/monotonic clock, timers, DST/time
        zone, advance/run-pending, and timeout/backoff semantics. Evidence:
        clock adapter tests.
        <!-- S11.7.b: shipped as
        `libs/oshun/workbench-kit/src/clock-adapter.ts` — **a working
        implementation, not a register** — with `.spec.ts` (22 tests driving
        behaviour) and `tools/clock-adapter-scan.mjs` (10 negative controls),
        registered as `@oshun/workbench-kit/clock-adapter`,
        `verify:operations:v1-clock-adapter`, and the `clock-adapter-scan` Nx
        target. Entry point 222 of 222, 0 export-map violations.

        **THE RULE THAT DECIDES THE DESIGN: A WALL CLOCK AND A MONOTONIC CLOCK
        ARE TWO CLOCKS, AND A TIMEOUT MEASURED ON THE WRONG ONE IS NOT A
        TIMEOUT.** Wall time is what a user sees and **can move backwards** — an
        NTP correction, a manual change, a VM resuming from a snapshot;
        monotonic time cannot. So a **duration** is measured on the monotonic
        clock, a **deadline** is displayed on the wall clock, `setWallClock`
        moves wall time alone and `advance` moves both — **so a test can produce
        the NTP step that breaks a naive timeout**, which is the only way anyone
        finds one. Driven end to end: after a backwards step of an hour and six
        seconds of real time, the monotonic deadline **has** expired and the
        wall comparison still has **3,599,000 ms** to go.

        **THE SECOND: ADDING 24 HOURS IS NOT ADDING A DAY.** A physical day is
        23 or 25 hours twice a year, so `localParts` answers local wall time in
        a named IANA zone through `Intl.DateTimeFormat` — the platform's own
        tables — and the suite advances across the **real Europe/London
        boundary** (2026-03-29) reading the offset go 0 → 60. **A scan control
        fails if this platform's tables do not put a change there**, because a
        DST test on a stale ICU build asserts nothing while passing.

        **THE THIRD: `advance` MUST BE EQUIVALENT TO TIME PASSING.** A timer
        scheduled by a callback running during the advance is due inside the
        same window if its deadline falls there; timers run in due order, ties
        by insertion; and **the clock stops at each timer's own due time** so a
        callback reading the clock sees the instant it was scheduled for, not
        the destination.

        **DECISIONS.** **Jitter is a parameter, not a call to a random source** —
        a backoff schedule a test cannot reproduce is one nobody can assert
        about — and a ratio outside `[0,1)` is **refused rather than clamped**.
        **Nothing patches a global**: a port is taken as an argument, not
        installed, and the scan checks that the module touches neither
        `setTimeout` nor `Date`. -->
  - [x] S11.7.c Implement seeded/scripted UUID/ID sequences,
        collision/exhaustion/ invalid generation, and no production
        predictability. Evidence: ID adapter tests.
        <!-- S11.7.c: shipped as `libs/oshun/workbench-kit/src/id-adapter.ts` —
        **a working implementation** — with `.spec.ts` (20 tests) and
        `tools/id-adapter-scan.mjs` (11 negative controls), registered as
        `@oshun/workbench-kit/id-adapter`, `verify:operations:v1-id-adapter`,
        and the `id-adapter-scan` Nx target. Entry point 223 of 223,
        0 export-map violations.

        **THE RULE: A PREDICTABLE IDENTIFIER THAT REACHES PRODUCTION MUST SAY
        SO.** A seeded id generator is a security defect wearing a test double's
        clothes — a guessable session id, invite token or object key is the whole
        game — and "it only exists in tests" is a claim about a wiring nobody
        checks. So it is answered twice: **every identifier is marked** (they
        begin `7e57`, hex for "test"), with `assertNotATestId` as a fail-loud
        seam for the paths that **mint** an id; and **the odds of a real UUID
        carrying the marker by chance are stated — 1 in 65,536** — because a
        marker treated as a proof is a false negative waiting to happen.

        **AND THE MARKED IDS ARE STILL VALID UUIDs** — version nibble 4, variant
        bits `10xx`, the right dashes. A double producing `id-1` would pass
        every test and fail against a `uuid` column, a parser or a router,
        **hiding exactly the bugs it exists to find**.

        **A SCRIPT THAT RUNS OUT HAS TO SAY SO** — exhaustion throws rather than
        wrapping (a silent collision) or returning `undefined` (a failure three
        layers away). **And a collision is something a test wants**: a script
        may repeat, so `scriptedCollisions` **reports** the repeats for the
        author rather than refusing them.

        **DETERMINISM WITHOUT `Math.random`:** SplitMix64 over a 64-bit state,
        written out in eight lines of `BigInt` so the sequence is **pinned to
        this file** and cannot move under a dependency bump — a seeded generator
        whose output moves is worse than none.

        The scan pins a **golden vector** for seed 42, runs **10,000
        identifiers** checking distinctness, shape and marker, checks all four
        legal variant nibbles appear (so the shape is not one lucky path), and
        confirms the module reaches for no real randomness — **a check that
        caught its own comment-stripper reporting a single-line JSDoc that
        *names* `crypto.randomUUID` in prose**. -->
  - [x] S11.7.d Implement provider scripts for chunks/results/usage/cost/safety/
        errors/delays/cancel/unknown outcome with exact request capture.
        Evidence: provider adapter tests.
        <!-- S11.7.d: shipped as
        `libs/oshun/workbench-kit/src/provider-adapter.ts` — **a working
        implementation** — with `.spec.ts` (17 tests) and
        `tools/provider-adapter-scan.mjs` (12 negative controls), registered as
        `@oshun/workbench-kit/provider-adapter`,
        `verify:operations:v1-provider-adapter`, and the
        `provider-adapter-scan` Nx target. Entry point 224 of 224,
        0 export-map violations.

        **THE RULE: AN UNKNOWN OUTCOME IS A THIRD RESULT, NOT AN ERROR.** A call
        can succeed, fail visibly, or **have the connection drop after the
        request was accepted** — the work may have happened, been billed and
        been recorded upstream, and this side cannot tell. A double that models
        that as an error teaches every consumer to treat it as "it did not
        happen", **and the consumer then retries a generation that already
        completed**.

        **WHAT THE ESTATE ALREADY HAS, AND WHERE IT STOPS.**
        `libs/iris/agents/core` declares a real port — `AgenticProvider` with
        `complete(request, signal?: AbortSignal)` — so **cancellation is already
        in the contract**, and the assistant factory takes an env documented
        "injectable for testing". Two things it cannot express: its taxonomy is
        **`PROVIDER_ERROR | UNKNOWN_TOOL | DUPLICATE_TOOL | INVALID_REQUEST`**
        and **none of them is the ambiguous outcome**, so a dropped connection
        arrives as a plain provider error and **reads as safe to retry — the bug
        arriving through the type system**; and `Promise<AgenticCompletion>` is
        all-or-nothing, with no way to say "four chunks arrived and then the
        connection died", so **the partial output somebody may already have
        shown a user is not in the result at all**.

        **THE SECOND: A COST IS A MEASUREMENT TIMES A PRICE, AND A MISSING PRICE
        IS A REFUSAL.** `costOf` throws rather than returning zero — *a cost of
        zero is a number a dashboard will happily add up, and the sum of a
        hundred unpriced calls is a confident nought*. `totalCost` refuses a
        call with no usage rather than skipping it. And **an unknown outcome
        reports no usage by construction**, so it cannot be priced at all.

        **THE THIRD: EXACT REQUEST CAPTURE MEANS EXACT** — recorded unmodified
        and undefaulted, so a test can assert on the field somebody added by
        accident and the prompt that grew a trailing newline.

        **Delays are advanced on S11.7.b's clock** rather than slept, and the
        capture keeps **how many chunks were delivered before a cancel**. -->
  - [x] S11.7.e Implement queue scripts for delivery/order/duplicate/lease/
        heartbeat/retry/cancel/dead-letter/worker loss and observable state.
        Evidence: queue adapter tests.
        <!-- S11.7.e: shipped as
        `libs/oshun/workbench-kit/src/queue-adapter.ts` — **a working
        implementation** — with `.spec.ts` (16 tests) and
        `tools/queue-adapter-scan.mjs` (7 negative controls), registered as
        `@oshun/workbench-kit/queue-adapter`,
        `verify:operations:v1-queue-adapter`, and the `queue-adapter-scan` Nx
        target. Entry point 225 of 225, 0 export-map violations.

        **THE RULE: A LEASE IS A CLAIM WITH AN EXPIRY, AND A QUEUE CANNOT TELL A
        DEAD WORKER FROM A SLOW ONE.** Everything difficult follows from it: when
        a lease runs out the queue does not know whether the worker crashed or
        is merely slower than somebody guessed, so it **redelivers** — same
        message id, incremented attempt, **and nothing said to the worker that
        had it**. That is at-least-once delivery; a double that cannot produce
        it cannot test the only part of the consumer that matters.

        **THE SECOND, AND IT IS THE ONE THAT BITES: A DEAD-LETTER POLICY COUNTS
        DELIVERIES, NOT FAILURES.** A message whose worker dies five times **was
        never nacked once** — nobody reported anything — so a policy keyed on
        failures never fires and the message circulates for ever while every
        dashboard shows zero errors. The scan drives that sequence end to end:
        **3 deliveries, 0 rejections, 1 dead-letter.**

        **THE THIRD: A HEARTBEAT EXTENDS A CLAIM, IT DOES NOT DISCHARGE ONE** —
        and it is capped from the moment the claim was granted, because without
        the cap **the only difference between a stuck worker and a careful one
        is patience**.

        **AND ORDER IS PER PARTITION, NEVER GLOBAL** — a partition with a message
        in flight offers nothing, so its order holds across a redelivery while
        different partitions interleave; the scan's second sequence produces
        exactly the reordering a global-order consumer would get wrong.

        Time is S11.7.b's clock; an acknowledgement from an expired lease
        **throws** rather than silently succeeding. -->
  - [x] S11.7.f Implement storage scripts for bytes/version/hash/range/latency/
        partial/corruption/eventual consistency/permission/failure. Evidence:
        storage adapter tests.
        <!-- S11.7.f: shipped as
        `libs/oshun/workbench-kit/src/storage-adapter.ts` — **a working
        implementation** — with `.spec.ts` (18 tests) and
        `tools/storage-adapter-scan.mjs` (9 negative controls), registered as
        `@oshun/workbench-kit/storage-adapter`,
        `verify:operations:v1-storage-adapter`, and the `storage-adapter-scan`
        Nx target. Entry point 226 of 226, 0 export-map violations.

        **THE RULE: A WRITE THAT SUCCEEDED IS NOT A WRITE YOU CAN READ.** An
        eventually consistent store answers a `put` with a success and then
        answers a `get` with the **previous** version — or nothing, if the object
        is new. Every consumer that writes and immediately reads back is either
        correct about that or **has a bug that appears under load and nowhere
        else**. `setConsistency` opens the window, `advanceReplication` closes
        it; the scan drives a consumer straight into it.

        **THE SECOND: A STORE THAT CANNOT LIE CANNOT TEST A CHECKSUM.**
        `corrupt()` returns **altered bytes with the original hash** — exactly
        what a silently damaged object looks like, and exactly what a consumer
        comparing them will catch. A double that always returns what it was
        given lets a consumer skip verification for ever and pass every test.

        **THE THIRD: PARTIAL AND RANGE ARE DIFFERENT THINGS AND ONLY ONE IS AN
        ERROR.** A **range** read is a slice the caller asked for; a **partial**
        read is a **prefix of the truth with nothing saying so**. Modelling them
        alike teaches a consumer to treat a truncated body as a successful short
        read — *which is how a half-written file gets committed*.

        **THE HASH IS FNV-1a OVER 64 BITS AND IS NOT CRYPTOGRAPHIC** — written
        out so its values are pinned to this file, detecting the accidental
        corruption a store produces and nothing an adversary produces. The
        module says so and **the scan checks that it still does**, because a
        test-double digest quietly used as an integrity guarantee is a defect
        with no symptom. Latency is advanced on S11.7.b's clock. -->
  - [x] S11.7.g Enforce dependency rules and production bundle scan so no app/
        production adapter imports test builders/fixtures. Evidence: deliberate
        invalid import and bundle tests.
        <!-- S11.7.g: shipped as
        `libs/oshun/workbench-kit/src/test-only-boundary.ts` (+ `.spec.ts`, 17
        tests) with `tools/test-only-boundary-scan.mjs` (9 negative controls),
        registered as `@oshun/workbench-kit/test-only-boundary`,
        `verify:operations:v1-test-only-boundary`, and the
        `test-only-boundary-scan` Nx target. Entry point 227 of 227,
        0 export-map violations.

        **THE VEIN: A DEPENDENCY RULE READS WHAT SOMEBODY WROTE, AND A BUNDLE
        SCAN READS WHAT ARRIVED.** A `no-restricted-imports` rule matches the
        **specifier at an import site** — exactly right for a developer typing a
        test-only path into production, and **blind to the chain where nobody
        types it**: a production module imports a barrel, the barrel re-exports
        a helper, the helper imports the builders, and every site is spelled
        legally.

        **WHAT THE ESTATE ALREADY HAS IS EXEMPLARY.** S1.13.e bans
        `@oshun/workbench-kit/testing*` repo-wide, adds a **second rule for the
        relative `../testing/` spelling** the specifier pattern cannot see,
        explains why it uses the `@typescript-eslint` rule rather than the core
        one (flat config resolves by last match), and — the part worth copying —
        **names what it does not cover**: spec files are globally unlinted, so
        the ban rests on "every linted file is production source" rather than on
        a carve-out. **And it is proved**:
        `tools/test-only-import-fixtures.mjs` writes a real TypeScript file,
        runs the repository's own ESLint, and checks **both halves** — *"a rule
        that also refuses the allowed spelling is not a rule"*. That **is** the
        deliberate invalid import this cell asks for.

        **WHERE IT STOPS IS THE MEASUREMENT: SIX libraries publish a `./testing`
        subpath and ONE is banned.** `shared/live-media`, `calliope/stage`,
        `isis/comfyui-sdk`, `yemaya/agents` and `yemaya/study-workspace` each
        publish a test-only entry point with nothing stopping a production
        import. The rule is not weak — it is **scoped to one package while the
        convention is used by six**.

        **AND THE BUNDLE HALF DOES NOT EXIST.** `oshun-web-bundle-monitor.yml`
        **weighs** the bundle — size analysis, a budget, a failure if over — and
        **a budget is not a content scan**: a test builder that adds four
        kilobytes passes comfortably, and *"did the forgery kit ship"* is not a
        question a number in kilobytes answers.

        **DECISION:** this scan deliberately **does not re-run the existing
        prover** — a second tool making the same deliberate invalid import would
        be **a copy claiming to be a check**. 7 faults. -->
- [x] S11.8 Add real-database isolation and migration harnesses. Evidence:
      multi-tenant round-trip tests.
<!--
S11.8 parent — a–g all closed. Seven kit modules, seven scans, 160 unit tests,
five registrations each → 234 entry points, 0 violations. Every scan verified
green in one sweep on 2026-08-15, and every one of them follows the same rule:
NO SERVER IS EXIT 1, because an unmeasurable fact and an absent one produce the
same silence.

THE PARENT'S EVIDENCE — a multi-tenant round-trip against a real database — EXISTS
AND RAN. `tools/isolation-paths-scan.mjs` applies the 35 migrations to a fresh
PostgreSQL 16.14 database, puts ONE natural key under TWO tenants, and drives
write → read → search → history → event through the real
`ContractPersistenceService`. Four legs read `tenant-scoped`. A deliberately
tenant-blind reader over the same rows reads `leaks`, so the fixture is known to
be capable of failing.

AND THE WRITE LEG DOES NOT COMPLETE, WHICH IS THE HARNESS WORKING. `put()` looks
the record up by `@@unique([tenantId, contractSchema, sourceRecordId])` and then
creates the row with `id` taken from the CONTRACT PAYLOAD, and `id String @id` is
a primary key that is not tenant-scoped — so the second tenant is refused with
P2002, and the scan had to build the collision underneath the repository to
continue. `DurationBucketSchema.id` is a five-value enum, so the first tenant to
persist a duration bucket blocks every other tenant for ever. 91 of 147 models
carry the shape. That defect is the reason this section asks for these harnesses
and it was invisible to every single-tenant fixture in the estate.

WHAT THE SEVEN CELLS ESTABLISHED, each measured rather than argued:

  .a  the production bootstrap leaves 24 tables, 24 keys, 110 indexes and ZERO
      foreign keys, check constraints, policies and triggers
  .b  the 35 migrations do not build the datamodel the client is generated from
      (drift exit 2, 73 statements, 17 of 147 tables), one model no migration
      creates, and all three `migrate diff` invocations pass flags Prisma 7
      removed
  .c  no fixture in the estate has ever put one natural key under two tenants,
      and 99 tenant-scoped compound keys make it legal by design
  .d  5 of the 9 paths scope by tenant; the write path leaks; and 5 of the 8
      database-backed integration suites FAIL the moment a database is present
  .e  eight concurrent `save()` calls ended at version 1 — seven lost updates,
      and the version says one save happened
  .f  the migration files hold 0 destructive statements and the script the
      deploy applies holds 16, in 0 transactions, with no down migration
  .g  294 live EXPLAIN checks, taken with sequential scans switched off; the
      narrowing is real and — measured — currently costs nothing

FOUR TIMES A SCAN REFUTED ITS OWN REGISTER and the register was corrected: the
11 double-produced tables AGREE exactly; `readEvent` is NOT an escalation; the
destructive count is 16 and not 18; and the planner settings change no plan
today. A register nothing can contradict is not a measurement.

TRAP, twice in one file: **`git grep` READS THE INDEX**, so this cell's own
module, spec, scan AND the export-map register — which is a register OF SOURCE
TEXT and matches every term any entry discusses — all began matching `pg_locks`
the moment they were committed, taking the count 1 → 4 → 2. A REGISTER THAT
COUNTS ITS OWN FAMILY INVENTS A DEFECT. All four are excluded by path, and the
absent-term control is now assembled at runtime so its literal never exists in
the file that searches for it.
-->
  - [x] S11.8.a Provision supported database version/extensions in isolated
        schema/database/container with production constraints/policies/triggers
        and deterministic cleanup. Evidence: harness setup tests.
<!--
S11.8.a — libs/oshun/workbench-kit/src/database-harness.ts (+ .spec.ts, 14 tests)
and tools/database-harness-scan.mjs (7 negative controls; exit 2 if any goes
undetected; NO SERVER IS EXIT 1, NEVER A PASS).

THE RULE: A HARNESS THAT PROVISIONS LESS THAN PRODUCTION IS TESTING SOMETHING
ELSE, AND ONE THAT CLAIMS MORE IS WORSE.

The cell asks for "production constraints/policies/triggers". A harness can only
supply those if production HAS them, so the deliverable is not a list of things
to create — it is an introspection of what the production bootstrap actually
leaves behind. The scan creates `oshun_harness_<run>`, points the BFF's own
`tryConnectPostgres()` at it via `OSHUN_STUBS_DATABASE_URL` so the PRODUCTION
`bootstrapSchema()` runs — the DDL itself, not a copy of it — reads `pg_catalog`,
and drops the database.

THE READING, taken live from PostgreSQL 16.14 on 2026-08-15:

    tables 24 · primary keys 24 · indexes 110 · unique constraints 1
    FOREIGN KEYS 0 · CHECK CONSTRAINTS 0 · POLICIES 0 · TRIGGERS 0
    tables with row security 0 · extensions: plpgsql

So a harness that faithfully provisions production's constraints, policies and
triggers provisions ALMOST NONE OF THEM — the cell's requirement is satisfied by
supplying nothing, and that is a fact about the estate rather than about the
harness. Concretely: `goal3_stub_habit_entries.habit_id` is `TEXT NOT NULL` with
no `REFERENCES`, so a child row can name a parent that never existed and the
server will store it; and with zero row policies, tenant isolation — the
evidence S11.8's parent asks for — is enforced by whatever each query remembers
to write, nowhere else. Four findings recorded: `no-foreign-keys`,
`no-row-policies`, `no-triggers`, `no-check-constraints`.

THE SECOND: ISOLATION IS A PROPERTY OF THE CONTAINER, NOT OF THE CLEANUP. A
harness that truncates tables at the end shares a namespace with everything else
on the server: a parallel run sees half-deleted rows, and a failed run leaves
them for the next one. A separate DATABASE makes the isolation structural and
the cleanup one statement that cannot partially succeed. `assertDroppable()`
throws on any name outside `oshun_harness_*` — the cleanup path of a test
harness is the one place a `DROP DATABASE` gets written with a variable in it,
and this server also holds `oshun_dev` and nine databases belonging to other
work.

THE THIRD: the kit takes a PORT, not a driver. `Sql` is one function — send this
text, get rows — so the 14 unit tests drive a recorded double (no server needed
to check the naming, the guard, the catalogue queries and the faults) and the
scan drives the real thing. AND AN ABSENT SERVER AND A CLEAN SCHEMA PRODUCE THE
SAME SILENCE, which is why no server is a failure and not a pass.

Registered five ways (exports, tsconfig path, root script
`verify:operations:v1-database-harness`, Nx target `database-harness-scan`,
export-map register) → 228 entry points, 0 violations; `public-api.spec.ts` 237
passing.
-->
  - [x] S11.8.b Apply migrations from empty, prior supported versions, and
        production-like snapshots; verify generated clients/schema metadata.
        Evidence: migration paths.
<!--
S11.8.b — libs/oshun/workbench-kit/src/migration-paths.ts (+ .spec.ts, 27 tests)
and tools/migration-paths-scan.mjs (8 negative controls; re-measures the drift
against a live server; NO SERVER IS EXIT 1).

THE RULE: A MIGRATION PATH IS A MECHANISM PLUS A STARTING STATE, AND TWO PATHS
THAT END AT THE SAME SCHEMA FROM DIFFERENT STARTS ARE NOT ONE PATH.

Four paths exist and they are different machines:

  ci-migrate-deploy    oshun-ci.yml:335       ordered files   FROM EMPTY   records
  deploy-migrate-diff  deploy.sh:166          structural diff FROM LIVE    no record
  database-yml-drift   database.yml:177       structural diff FROM EMPTY   ✗ not V1
  bff-bootstrap        domain-stubs-pg.ts:394 if-not-exists   FROM LIVE    no record

So the 35 migration files are exercised in CI and NEVER EXECUTED IN PRODUCTION:
production's schema is whatever `migrate diff` synthesised, and no
`_prisma_migrations` row exists there to say which version a database is at.
The deploy's own comment says why — the datamodel declares no datasource url —
which makes it a considered decision and does not change what it costs. Of the
three starting states the cell names, ONE (empty) is exercised by anything.

MEASURED, not asserted. Applying all 35 migrations to a fresh PostgreSQL 16.14
database (35 recorded, 147 tables for 147 models) and diffing the result against
`schema.prisma`:

    exit 2 · 73 statements · 17 of 147 tables · 25 SET DATA TYPE
    2 foreign keys dropped and NEVER RECREATED
      (decision_supersedes_fkey, ori_presence_inference_usage_fkey)
    1 CREATE TABLE — v1_cross_cutting_gold_set_entry

The `SET DATA TYPE` block is real drift, not a Prisma-version artifact:
`schema.prisma` carries **zero** `@db.Timestamptz` annotations, four migrations
write `TIMESTAMPTZ`, and the live column is `timestamp with time zone`.

`v1_cross_cutting_gold_set_entry` is a model at `schema.prisma:4584` that **no
migration mentions** and that is **absent from a fully migrated database**. The
generated client has a delegate for it. Its only callers are three test files,
two of which skip unless `OSHUN_V1_DATABASE_URL` is set — and their own comment
says "normal CI has no DB", so the absence is undetectable there by
construction.

THE CHECK THAT WOULD HAVE CAUGHT IT EXISTS AND DOES NOT COVER THIS SCHEMA.
`database.yml` globs the `libs/<domain>/database` directories in three places
(destructive-op scan :104, `prisma validate` :151, `migrate deploy` + `migrate
diff --exit-code` :164–180). Twelve schemas match. V1's is at
`libs/oshun/persistence` — and persistence is not database.

AND ALL THREE `migrate diff` INVOCATIONS PASS FLAGS THE PINNED PRISMA REMOVED.
The catalogue pins `@prisma/client ^7.8.0`; `--from-url` and
`--to-schema-datamodel` are absent from both 7.0.0 and 7.8.0 (verified by asking
the published CLI), and `--from-migrations` now demands a
`datasource.shadowDatabaseUrl` no `prisma.config.ts` in this repository sets.
The deploy's copy dies under `set -euo pipefail` — fail-closed, correct
direction, and the migration step cannot complete. `database.yml`'s copy is
caught by `|| { echo "Schema drift detected in $schema"; exit 1; }`, and an
argument error exits 1 exactly as an error does: A CHECK THAT NEVER RAN REPORTS
THE DEFECT IT WAS NEVER ABLE TO TEST FOR.

WHAT I EXPECTED AND DID NOT FIND. Eleven of the 24 tables the BFF bootstraps are
also Prisma models, and `CREATE TABLE IF NOT EXISTS` is a no-op against a table
Prisma already made — so I compared every column and type of all eleven between a
BFF-only database and a fully migrated one. THEY AGREE EXACTLY. Recorded as two
producers with no check, not as a defect. A related one that is real: the
bootstrap adds 13 tables the datamodel does not know, so a production-shaped
diff emits `DROP TABLE` for all 13 into `psql -v ON_ERROR_STOP=1` — which has
not happened only because the diff command errors first. Two defects holding
each other up is not a safeguard.

Registered five ways → 229 entry points, 0 violations; `public-api.spec.ts` 238
passing. Every harness database dropped; the other 21 on the server untouched.
-->
  - [x] S11.8.c Provide transactional/nontransactional seed builders for two
        tenants with colliding IDs/data and role/ownership/protected cases.
        Evidence: fixture setup.
<!--
S11.8.c — libs/oshun/workbench-kit/src/tenant-seeds.ts (+ .spec.ts, 36 tests)
and tools/tenant-seeds-scan.mjs (9 negative controls; drives the instrument
against a live server; NO SERVER IS EXIT 1).

THE RULE: A COLLIDING ID IS THE ONLY FIXTURE THAT CAN CATCH A QUERY THAT FORGOT
ITS TENANT.

Give tenant A a row with id `a1` and tenant B a row with id `b1`, and a query
reading `WHERE id = 'a1'` with no `AND tenant_id = $1` STILL RETURNS EXACTLY A'S
ROW. The assertion passes. The isolation was never tested. Only when both
tenants hold a row under the same natural key does the missing predicate show.

And the schema permits it BY DESIGN: 99 of the datamodel's compound keys are
`@@unique([tenantId, …])` or `@@id([tenantId, …])`, and the BFF's own
`goal3_stub_scim_user` is `PRIMARY KEY (tenant_id, external_id)`. A collision is
the legal state the key was shaped to allow — not a violation to engineer around.

THE SECOND: TRANSACTIONAL AND NONTRANSACTIONAL ARE NOT TWO STYLES OF THE SAME
BUILDER. THEY DECIDE WHO CAN SEE THE DATA. A transactional seed is private to
its connection until it commits, so a subject that takes its own connection from
a pool — an HTTP handler, a worker, a listener — sees an EMPTY DATABASE and
passes because nothing was found, which is the same shape of pass as isolation
working. `canSeedFor()` refuses that combination outright.

THE THIRD: the cases fail in different places, so they are separate builders.
role breaks a permission check that resolves a role without the tenant (and
every audit line still names the right person); ownership breaks an
`owner_user_id` match wherever user ids also collide; protected destroys a row
under legal hold AND REPORTS SUCCESS, because it did delete something.

THE INSTRUMENT REFUSES TO ANSWER FROM A NON-COLLIDING SEED. `isolationVerdict`
returns `inconclusive`, with the reason, when the plan holds no collision or
when the reader found nothing — because a reader returning one row proves
nothing unless the other tenant had a row it could have wrongly returned.

THE SCAN PRODUCES BOTH VERDICTS AGAINST A REAL DATABASE. It seeds a real
colliding pair into `goal3_stub_scim_user`, then runs a tenant-scoped reader and
a tenant-BLIND one and requires `tenant-scoped` and `leaks` respectively. Live
result: 2 rows seeded, scoped → `tenant-scoped`, blind → `leaks`, 0 rows after
cleanup. If the blind reader did not leak, the fixture did not collide and every
other reading would be worthless — so that is a hard failure, not a note.

THE ESTATE, AND THE SCAN CORRECTED ME TWICE. Eight database-backed suites in
`libs/oshun/persistence`: seven name one tenant, ONE names two, ONE uses a
transaction, 26 `deleteMany` cleanups. I had written 0 and 0.

The two-tenant suite comes within one line of this fixture and stops.
`contract-record-repository.integration.test.ts` declares `BUCKET_TENANT` and
`SITTING_TENANT`, and they are NAMESPACES FOR TWO UNRELATED SUBJECTS:
`taraDurationBucket` sees only the first, `taraSittingCompletionEvent` only the
second. One table sees both — `oshunV1TombstoneAuditEvent`, in a
`deleteMany({ tenantId: { in: [a, b] } })` and two per-tenant `count` calls —
and the rows the two tenants put there are audit records OF DIFFERENT THINGS,
sharing no natural key. So the register gained a second measure:
`delegatesWrittenUnderTwoTenants`, which is **0**. NAMING TWO TENANTS IS NOT
PUTTING ROWS OF BOTH IN ONE TABLE.

Registered five ways → 230 entry points, 0 violations; `public-api.spec.ts` 239
passing. Harness database dropped.
-->
  - [x] S11.8.d Run read/write/search/history/blob ref/event/job/export/audit
        tenant isolation through real repositories/services. Evidence:
        multi-tenant tests.
<!--
S11.8.d — libs/oshun/workbench-kit/src/isolation-paths.ts (+ .spec.ts, 20 tests)
and tools/isolation-paths-scan.mjs (11 negative controls; drives the REAL
ContractPersistenceService against a REAL database; NO SERVER IS EXIT 1).

THE RULE: A PATH WITH NO TENANT BOUNDARY CANNOT PASS A TENANT-ISOLATION TEST,
AND REPORTING IT GREEN IS THE DEFECT.

Of the nine paths the cell names: 7 are served, 5 scope by tenant, and the other
four are not gaps —

  blob-ref, job   no repository exists in the V1 estate at all
  audit           AdminAuditEvent has NO tenantId; it keys on userId and a
                  nullable workspaceId, and listAcrossOperators turns an empty
                  filter into null — no restriction, by design
  export          a subject-access bundle assembled per USER; a tenant probe
                  against it would be measuring the wrong boundary

`path-scoped-by-something-else` and `probeable-but-unprobed` are separate fault
kinds on purpose: folding them together lets an unrun probe shelter behind a
legitimate design decision.

**A REQUIRED PARAMETER IS NOT AN APPLIED PREDICATE.** That `ContractListQuery`
declares `readonly tenantId: string` says the caller must pass one and nothing
about the WHERE clause. So the verdicts come from driving the real service
against a real PostgreSQL 16.14 database with ONE NATURAL KEY HELD BY TWO
TENANTS.

**LIVE DEFECT — THE WRITE PATH LEAKS, AND NO SINGLE-TENANT FIXTURE COULD EVER
SEE IT.** `ContractRecordRepository.put()` looks the record up by
`@@unique([tenantId, contractSchema, sourceRecordId])` — correctly tenant-scoped
— and then creates the row with `id` taken from the CONTRACT PAYLOAD, and
`id String @id` is the primary key and is NOT tenant-scoped. The second tenant to
write a natural key the first one used is refused with **P2002**. And
`DurationBucketSchema.id` is a **five-value enum** (`micro, short, standard,
long, retreat`), so this is not an unlucky coincidence:

    THE FIRST TENANT TO PERSIST A DURATION BUCKET BLOCKS EVERY OTHER TENANT
    FROM EVER HAVING ONE.

**91 of the 147 models carry the same shape** — a bare `id String @id` beside a
tenant-scoped compound unique — and 99 contracts are registered through this
repository.

The other four paths HOLD: read, search, history and event all read
`tenant-scoped`, proved against a real collision the scan had to build
UNDERNEATH the repository, because the repository itself cannot make one. A
deliberately tenant-blind reader over the same rows reads `leaks`, so the
fixture is known to be capable of failing.

**A SECOND LIVE FINDING, MEASURED THE SAME WAY: FIVE OF THE EIGHT DATABASE-BACKED
INTEGRATION SUITES FAIL THE MOMENT A DATABASE IS PRESENT.** They skip unless
`OSHUN_V1_DATABASE_URL` is set and their own comment says "normal CI has no DB",
so nobody has run them. Against a fully migrated database:

    contract-record-repository          1 failed 1 passed  ZodError — the
                                        DurationBucket contract gained
                                        defaultSessionMinutes,
                                        minimumHoursBetweenRecommendations and
                                        cadenceRationale; the fixture still has
                                        five fields
    dsar-deletion-cascade               1 failed           same put path
    dsar-erasure-runtime                2 failed           same put path
    gold-set-entry-persistence          crashes in setup   table does not exist
    gold-set-entry-store                crashes in setup   table does not exist
    durable-admin-audit-events-store    1 passed
    durable-memory-store                1 passed
    durable-snapshot-store              5 passed

The two crashes confirm S11.8.b's `v1_cross_cutting_gold_set_entry` operationally:
that model is not a latent gap, it breaks two suites on contact.

WHAT I LOOKED FOR AND DID NOT FIND. `readEvent(eventId)` is the one method in
the audit store with no scope predicate — every sibling filters by `userId` —
and it is reached over HTTP from the investigation export, resolving bookmark
ids the caller chose. It looks exactly like a scope bypass. It is not one: the
same caller can set an empty filter and read every operator's events through the
ordinary list path, so the unscoped read grants no authority the scoped one
withholds.

The scan compares the write verdict to the register in BOTH directions, so a fix
trips it too and the register cannot silently go stale.

Registered five ways → 231 entry points, 0 violations; `public-api.spec.ts` 240
passing. Every harness database dropped.
-->
  - [x] S11.8.e Run unique/foreign/check/concurrency/idempotency/outbox/inbox/
        lease/retention/legal-hold constraints under contention. Evidence:
        constraint tests.
<!--
S11.8.e — libs/oshun/workbench-kit/src/contention-constraints.ts (+ .spec.ts,
23 tests) and tools/contention-constraints-scan.mjs (8 negative controls; RACES
the real store; NO SERVER IS EXIT 1).

THE RULE: A CONSTRAINT UNDER CONTENTION IS ENFORCED BY WHOEVER HOLDS THE LOCK,
AND FIVE OF THESE TEN HAVE NO LOCK BECAUSE THEY HAVE NO ROW.

    unique       103 @@unique  → real PostgreSQL unique indexes
    foreign       19 @relation → real foreign keys
    check          NONE — Prisma cannot express one, live schema has zero
    concurrency   version Int ×4, revision Int ×10, etag ×1  → read-then-write
    idempotency   Redis; production sets OSHUN_BFF_IDEMPOTENCY_REDIS_URL
    lease         lease Json? on work_item, guarded by a ROW LOCK
    outbox        no model
    inbox         no model
    legal-hold    no model — and 78 TypeScript files that mention one
    retention     19 models carry retention fields

You cannot contend for what has no row, so the honest first output is which of
the ten can be contended at all (**6**), not a pass rate.

THE FINDING: THE ESTATE CONTAINS ITS OWN ANSWER AND APPLIES IT ON ONE PATH ONLY.

`apps/oshun/bff/src/workbench/intent-store.ts:550` does it exactly right — opens
a transaction, takes `SELECT … FROM work_item WHERE id = $1 FOR UPDATE`, and
checks its compare-and-set INSIDE that transaction, with a comment saying why:
"so a racer that already released and re-leased the item cannot have its fresh
lease stolen". **Eight `FOR UPDATE` sites across four files.**

`libs/oshun/persistence/src/durable-snapshot-store.ts:104` does not, and it holds
V1's admin state (`admin_store_snapshot`, 52 store keys per S10.12.c):

    const existing = await …findUnique({ where: { id: key } });
    const version  = (existing?.version ?? 0) + 1;
    await …upsert({ where: { id: key }, create: {…}, update: { document, version, updatedAt } });

**A VERSION COLUMN THAT IS NOT IN THE WHERE CLAUSE IS A COUNTER, NOT A LOCK** —
and it is worse than no version at all, because the number looks like optimistic
concurrency, so a reader comparing versions concludes nothing was lost. The doc
comment above `save()` states the invariant this breaks: the new version "is the
partition's current version + 1, so it strictly increases across saves".

**AND THE PORT'S TYPE FORBIDS THE FIX.** `AdminStoreSnapshotDelegate.upsert`
declares `where: { id: string }` and there is no `updateMany`. A conditional
update cannot be expressed through this interface at all — a shape the seam was
cut to, not a slip at one call site.

**MEASURED, NOT ARGUED. Eight concurrent `save()` calls at one partition:**

    8 of 8 succeeded · the row ended at VERSION 1 · 7 LOST UPDATES
    the surviving document is writer 6; writers 0–5 and 7 are gone
    and the version says one save happened

A ONE-WRITER RACE IS NOT A RACE. `lostWrites` of a single writer is 0 and so is
`lostWrites` of a perfectly locked eight-writer race — the same number for
"proved it holds" and "did not try" — so `raceIsConclusive` refuses a reading
with fewer than two writers or two successes, and the scan fails rather than
reports if the race comes out inconclusive.

CREDITED, NOT FLAGGED: the idempotency fallback logs "idempotency store:
in-memory … replay safety is per-process only" and production configures Redis —
an honest fail-loud seam.

Registered five ways → 232 entry points, 0 violations; `public-api.spec.ts` 241
passing. Harness database dropped.
-->
  - [x] S11.8.f Rehearse expand/backfill/verify/contract with old/new binaries,
        live writes, interruption/resume, rollback/compensation, and production-
        size data. Evidence: migration report.
<!--
S11.8.f — libs/oshun/workbench-kit/src/expand-contract-rehearsal.ts (+ .spec.ts,
24 tests) and tools/expand-contract-rehearsal-scan.mjs (7 negative controls;
rebuilds a production-shaped database and re-measures the applied script; NO
SERVER IS EXIT 1).

THE RULE: A MIGRATION THAT DROPS NOTHING AND AN APPLY THAT DROPS SIXTEEN THINGS
ARE THE SAME DEPLOY.

    the 35 migration FILES     0 destructive · 0 data statements · 0 down
    what the deploy APPLIES   86 statements · 16 DESTRUCTIVE · 0 transactions

Every migration file is an expand — no `DROP TABLE`, `DROP COLUMN`,
`SET NOT NULL` or `RENAME` between them. That is the property a reviewer relies
on, and it is a property of THE FILES. Production applies a computed diff
instead, and against a production-shaped database (the migrated schema AND the
BFF's bootstrap in one database, which production guarantees because
`OSHUN_STUBS_DATABASE_URL` is deliberately unset — the terraform says so) that
script drops 13 tables, 1 index and 2 constraints.

So the contract phase is NEVER AUTHORED and is PERFORMED ON EVERY DEPLOY, on
tables nobody listed. And the destructive-operation guard cannot see it twice
over: it globs the `libs/<domain>/database` directories, which do not include
this schema — and even correctly aimed it reads the MIGRATION FILES, where there
is genuinely nothing to find. **A GUARD THAT READS WHAT WAS WRITTEN CANNOT SEE A
DESTRUCTION THAT IS COMPUTED.**

THERE IS NO RESUME POINT AND NO REVERSAL, AND BOTH ARE STRUCTURAL. The generated
script has no `BEGIN` and no `COMMIT`, so `psql` applies 86 statements in
autocommit and an interruption at statement 40 leaves 39 applied and 47 not;
`migrate diff` never writes a `_prisma_migrations` row, so nothing records where
it stopped. Zero down migrations exist — Prisma has no down concept, and the
deploy's `head -n -20` retention of applied scripts is a record of what happened,
not a way to undo it. **Two of the 35 files DO write their own BEGIN/COMMIT**, so
the estate knows the technique; the path that runs in production is the one
without it.

BACKFILL AND VERIFY ARE NOT SLOW, THEY ARE ABSENT. Not one of the 35 migrations
contains an `UPDATE`, `INSERT` or `DELETE`, and an expand/contract cycle exists
to move DATA. 1 of 4 phases is performed deliberately.

0 of 5 conditions are rehearsed, and **2 of them cannot be**:
`interruption-and-resume` and `rollback-or-compensation` are not missing tests —
the mechanism has to change first. `condition-unrehearsed` and
`condition-impossible` are separate fault kinds for exactly that reason.

CORRECTED BY MY OWN SCAN: I first counted 18 destructive statements from a grep
whose pattern also matched the `ALTER TABLE` line that merely OPENS a multi-line
statement, so two statements were counted twice. The scan classifies whole
statements and reports **16**. A count off a line prefix is a count of lines.

Registered five ways → 233 entry points, 0 violations; `public-api.spec.ts` 242
passing. Harness database dropped.
-->
  - [x] S11.8.g Capture slow queries/locks/plans and fail on unsafe sequential
        scans or incompatible schema readiness. Evidence: database verification
        output.
<!--
S11.8.g — libs/oshun/workbench-kit/src/plan-capture.ts (+ .spec.ts, 23 tests)
and tools/plan-capture-scan.mjs (10 negative controls; runs the estate's own
EXPLAIN checks BOTH ways against a live database; NO SERVER IS EXIT 1).

THE RULE: A PLAN TAKEN WITH SEQUENTIAL SCANS DISABLED CANNOT REPORT A SEQUENTIAL
SCAN — AND WHETHER THAT IS COSTING ANYTHING TODAY IS A SEPARATE QUESTION THAT
HAS TO BE MEASURED.

`validate-explain-checks.ts` is real and thorough: **294 EXPLAIN checks across
98 tables**, derived from the 99 registered contracts in three query patterns
(`identity_lookup`, `live_contract_scan`, `payload_hash_drift`), run LIVE in CI
against a `postgres:16-alpine` service. More plan coverage than most estates
have.

And before planning anything it issues, with no comment:

    SET plan_cache_mode = 'force_custom_plan'
    SET enable_seqscan = off
    SET enable_sort = off

The first is right. The second is a real narrowing: the script asserts that the
REQUIRED INDEX NAME appears in the plan, and `enable_seqscan = off` guarantees an
index path if any index can serve the query — so a query that would sequentially
scan in production is forced onto an index and the check passes.

    an index exists that can serve this query          ← what it proves
    production will not sequentially scan this table   ← what it cannot

**WHAT I ASSUMED AND MY OWN SCAN REFUTED.** I wrote that the setting is
*necessary* because an empty table always seq-scans regardless of its indexes.
Running all three query patterns against a freshly migrated database with the
planner left alone produces **ZERO sequential scans**, and the plans are
**IDENTICAL** with and without the settings — these predicates are selective
equalities on the exact indexed columns and PostgreSQL takes the index anyway.

So the narrowing is REAL AND CURRENTLY COSTS NOTHING. Both numbers are recorded
rather than one verdict, and the scan fails if they ever diverge **in either
direction** — a regression and a fix both trip it.

THE ESTATE CONTAINS ITS OWN ANSWER AND APPLIES IT ON ONE PATH ONLY — twice more:

  slow queries  `infra/terraform-v1/modules/rds/main.tf:131` sets
                `shared_preload_libraries = pg_stat_statements,vector` and
                `log_min_duration_statement = 500`; the deployed Hetzner postgres
                command sets `shared_buffers`, `effective_cache_size`,
                `max_connections` and neither of those
  locks         a Grafana panel plots `sum by (mode) (pg_locks_count)` from
                Prometheus, and the deployed compose runs no prometheus, no
                grafana and no postgres exporter — neither `deploy.sh` nor
                `deploy-hetzner.yml` mentions `docker/observability` at all.
                **A READER WITH NO PRODUCER.**

(My first pass said `pg_locks` appears in 0 files. It appears in 1 — the scan's
`git grep` found the dashboard my extension-filtered grep had excluded.)

SCHEMA READINESS HAS NOTHING TO COMPARE AGAINST: a readiness gate compares the
schema a binary expects with the one the database has, and `migrate diff` never
writes a `_prisma_migrations` row (S11.8.b), so nothing states what production is
at.

1 of 4 captures is taken where it runs.

Registered five ways → 234 entry points, 0 violations; `public-api.spec.ts` 243
passing. Harness database dropped.
-->
- [x] S11.9 Add contract fuzzing for envelopes, manifests, and remote payloads.
      Evidence: minimized failure corpus is retained.
<!--
S11.9 parent — a–g all closed. Seven kit modules, seven scans, 183 unit tests,
two committed corpora, one new CI workflow. 241 entry points, 0 violations; the
kit's 15,257 tests pass and all 83 file-only scans pass.

THE PARENT'S EVIDENCE IS RETAINED AND REPLAYS.
`evidence/v1-workbenches/workbench-kit-minimized-corpus.jsonl` holds 13
reproducers, every one carrying seed, schemaId, version, expectedResult, the
rule that computed the signature, and the minimized text; every one replays
against the parser it was minimized from; and `shrinking-scan` rebuilds, rewrites
and re-replays the whole file on every run. Beside it,
`workbench-kit-negative-envelope-corpus.jsonl` holds the 18 wire-text cases.

ONE RULE RUNS THROUGH ALL SEVEN CELLS: **DECLARE WHAT YOU EXPECT TO HAPPEN, NOT
MERELY THAT SOMETHING DID.**

  .a  a generated envelope invalid for TWO reasons tests neither, so every
      mutation names the one ParseErrorCode it provokes
  .b  a negative corpus stored as OBJECTS cannot hold half the cases it names —
      `{"kind":"a","kind":"b"}` is one key after JSON.parse
  .c  a relationship is broken by changing ONE side, and a builder that
      regenerates both cannot produce one
  .d  a polyglot is a fact about a SET and the decider takes a SCALAR
  .e  an invariant you cannot OBSERVE cannot be asserted — four verdicts, not
      two, because `unobservable` and `vacuous` are not `held`
  .f  a shrink is only valid if the smaller case fails for the SAME reason
  .g  a check that nothing SCHEDULES is a check nobody runs

WHAT THE SECTION FOUND IN THE ESTATE, all measured:

  · all four encoding mutations are ACCEPTED — a lone surrogate, a NUL, a bidi
    override and an NFD/NFC pair, each a string in a string-typed field
  · a duplicate `tenant` is ACCEPTED with the LAST declaration winning, so a
    signature over one and an authorization over the other disagree invisibly
  · a duplicate `kind` silently RE-ROUTES the envelope to a different parser
    before any validation runs
  · a large integer is accepted with a DIFFERENT VALUE than was sent
  · three of the ten manifest relationships are NEVER COMPARED — a manifest may
    credit anybody as its producer and the assembly passes
  · the sniffer's four facts cannot reach the content decision's one string, so
    two of three collapses ACCEPT a polyglot and none can quarantine it
  · **74 of 82 verification scripts were run by nothing**, and the first honest
    run of them found nine failing scans — three real estate drift, five
    instances of one systemic trap, and one real crash in shipped kit code

AND THE METHOD CORRECTED ME REPEATEDLY, which is the point of building it this
way. My `survivesAsAnObject` compared an expression to ITSELF and could never
fail (2 lost cases where the truth is 7). I claimed the sniffer would miss the
canonical polyglot; it does not. I claimed `{ ...parsed }` drops `__proto__`;
only `Object.assign` does, and it changes the prototype too. I tiered the
schedule on 30 database scans when the number is 8. Every one was found by
running the thing rather than reading it.

**THE TRAP OF THE SECTION, worth carrying forward: A REGISTER THAT COUNTS ITS
OWN FAMILY INVENTS A DEFECT, AND THE FAMILY HAS FIVE MEMBERS** — module, spec,
scan, the Nx doc in `project.json`, and the export-map reason — **four of them
carrying prose about the terms the scan searches for, and one living OUTSIDE
`libs/oshun/workbench-kit`.** It fired four separate times in S11.9.g alone.
-->
  - [x] S11.9.a Generate schema-valid, boundary-valid, and invalid
        command/query/ event/job/probe envelopes across
        kind/version/field/size/order/encoding. Evidence: envelope property
        suite.
<!--
S11.9.a — libs/oshun/workbench-kit/src/envelope-generators.ts (+ .spec.ts, 28
tests incl. a fast-check property over the real parsers) and
tools/envelope-generators-scan.mjs (9 negative controls). `fast-check` added to
the kit as a catalog devDependency.

THE RULE: A GENERATED ENVELOPE THAT IS INVALID FOR TWO REASONS TESTS NEITHER.

Take a valid command, set `schemaVersion` to 99 to exercise the version gate,
and — because the mutation also dropped `idempotencyKey` — the parser answers
`missing-field`. The assertion "it was rejected" passes. The version gate never
ran. So every mutation declares the ONE `ParseErrorCode` it exists to provoke,
and `outcomeVerdict` returns **`rejected-for-the-wrong-reason`** when the parser
answers with a different one — a failure, not a pass.

THE SECOND: BOUNDARY-VALID IS THE LARGEST INPUT THAT MUST SUCCEED, NOT THE
SMALLEST THAT MUST FAIL. A generator making small valid envelopes and enormous
invalid ones never evaluates the comparison operator — `>` and `>=` behave
identically on every input it produces — so the boundary class emits envelopes
of **exactly** `MAX_ENVELOPE_BYTES` that must parse.

WHAT THE ESTATE HAS, MEASURED. `workbench-kit-envelope-goldens.json` holds **57
hand-authored cases**, 22 accept and 35 reject, each carrying its expected code.
Sorted by the six dimensions this cell names:

    field 18 · version 4 · kind 3 · size 3 · order 3 · ENCODING 0

and it provokes **7 of the 14** declared `ParseErrorCode` values — the seven it
misses are exactly the identity parser's own rejections. A hand-authored corpus
covers the cases somebody thought of. That is not a criticism of it; it is why a
generator is a different artifact rather than a bigger version of the same one.

**THE FINDING: all four encoding mutations are ACCEPTED today.** A lone UTF-16
surrogate, a NUL byte, a right-to-left override and an NFD/NFC pair all pass
`parseCommandEnvelope`, because each is a string in a string-typed field and a
type check cannot see the difference. The scan reports them as
`accepted-but-should-not-be` — 20 of 245 applications — rather than failing,
because that is this cell's finding and not its bug.

THE THIRD, WHICH THE GENERATOR TAUGHT ME: THERE ARE TWO ENTRY POINTS AND ONLY
ONE WEIGHS THE ENVELOPE. The five `parse<Kind>Envelope` functions take an
already-parsed object and enforce shape; `parseEnvelopeJson` takes the wire TEXT
and checks the bytes BEFORE `JSON.parse`, which is the right order and its own
comment says why. My first pass asserted the size limit at the wrong door.

AND THE CELL'S OWN RULE CAUGHT ME TWICE MORE. The size mutation first padded
`payload` — which `job-update` and `capability-probe` do not declare — so a
mutation about SIZE silently became one about SHAPE on two of the five kinds and
came back `unknown-field`. Its replacement padded `extensions` under `x-pad`,
which `parseExtensions` refuses because keys must be `<vendor>.<name>`. Both
were found by RUNNING the mutations, not by reading them. And `field/undefined-
not-null` targeted `missing-field` and the parser answers `not-a-string`,
because it distinguishes an absent key from a present one holding the wrong type
— a sharper answer than the register assumed.

Registered five ways → 235 entry points, 0 violations; `public-api.spec.ts` 244
passing.
-->
  - [x] S11.9.b Fuzz duplicate/unknown/missing fields, discriminators, enums,
        IDs, numbers, dates, Unicode, nesting, arrays, aliases, and
        older/newer/unsupported versions. Evidence: negative envelope corpus.
<!--
S11.9.b — libs/oshun/workbench-kit/src/negative-corpus.ts (+ .spec.ts, 26 tests)
and tools/negative-corpus-scan.mjs (8 negative controls), emitting
`evidence/v1-workbenches/workbench-kit-negative-envelope-corpus.jsonl` — 18
cases over all 13 categories.

THE RULE: A NEGATIVE CORPUS STORED AS OBJECTS CANNOT HOLD HALF THE CASES IT
NAMES.

    {"kind":"command","kind":"event"}  →  JSON.parse  →  { kind: 'event' }

One key. `JSON.parse` resolves a repeated key by last-wins, silently, before any
parser runs — so a corpus entry written as a JSON *object*, which is how all 56
envelopes in `workbench-kit-envelope-goldens.json` are stored, has had the
defect removed from it before it reached disk. The same is true of precision
above 2^53, of `-0` (parses as `-0`, serializes as `0`), of `NaN` and a leading
BOM (not JSON at all), and of escaped key forms.

**5 of the 13 categories are text-only, and 7 of the 18 cases do not survive
object storage.** So the corpus is JSON Lines with the envelope held as a
STRING, and the scan PROVES the format matters rather than asserting it: it
writes the corpus, reads it back byte-identical, then stores the same cases as
objects, reads THAT back, and requires exactly 7 to have changed. If none had,
the text format would be unnecessary and the register wrong.

FINDINGS, all measured against the real `parseEnvelopeJson`:

  · **the duplicate TENANT is ACCEPTED** and the last declaration wins, so a
    signature computed over one and an authorization computed over the other
    disagree and nothing downstream can see there were two
  · the duplicate KIND silently RE-ROUTES the envelope to a different parser
    before any validation runs — the rejection that follows is correct and its
    reason is not the one a reader would guess
  · a large integer inside a legal extension is ACCEPTED with a **different
    value than was sent**
  · an escaped lone surrogate is ACCEPTED, confirming S11.9.a's finding at the
    wire level

`__proto__` ARRIVES AS AN ORDINARY OWN KEY AND THE TWO COPY IDIOMS DISAGREE:
`{ ...parsed }` keeps the key and leaves the prototype alone; `Object.assign({},
parsed)` DROPS the key and CHANGES THE PROTOTYPE, because spread defines own
properties and assign triggers the setter. I first wrote that both did the
second; only one does.

OLDER, NEWER AND UNSUPPORTED ARE THREE ANSWERS, NOT TWO. A newer envelope that
still parses must be TOLERATED — that is what a closed shape with an extension
namespace is for — and a corpus that only knows supported from unsupported
cannot express forward compatibility at all.

**AND THE MEASURE ITSELF WAS WRONG FIRST.** `survivesAsAnObject` compared
`JSON.stringify(JSON.parse(text))` with `JSON.stringify(JSON.parse(text))` — the
same expression twice, a check that could never return false — and reported 2
lost cases where the truth is 7. Replaced with a comparison against the ORIGINAL
minified text, with the minified precondition asserted rather than assumed.

Registered five ways → 236 entry points, 0 violations; `public-api.spec.ts` 245
passing.
-->
  - [x] S11.9.c Generate profile-valid and invalid bundle manifests with path/
        ref/hash/size/media/schema/dependency/provenance/version/lifecycle
        relationships. Evidence: manifest property suite.
<!--
S11.9.c — libs/oshun/workbench-kit/src/manifest-relations.ts (+ .spec.ts, 23
tests against the real `bundleAssemblyFaults`) and
tools/manifest-relations-scan.mjs (7 negative controls).

THE RULE: A RELATIONSHIP IS BROKEN BY CHANGING ONE SIDE, AND A BUILDER THAT
REGENERATES BOTH CANNOT PRODUCE ONE.

`bundleAssemblyFaults` takes FIVE inputs — profile, request, layout, observed,
manifest — and every one of its thirteen faults is a DISAGREEMENT between two of
them. A generator that derives the manifest from the layout can never emit one
that disagrees with it: the derivation is what maintains the invariant. Feed a
mutated artifact through the builder and the digest is recomputed, the path
re-templated, the bytes re-counted, and the five inputs come out consistent
again — a different VALID bundle. So `consistentTuple` builds the valid tuple
(the thing the estate has only inside `bundles.spec.ts`) and each break reaches
in and changes exactly one side of one edge, which the spec and scan both
verify by comparing object identity across all five inputs.

BREAKING ONE EDGE MUST PRODUCE EXACTLY ONE FAULT. `also-broke-something-else` is
a distinct verdict from `wrong-fault` because they call for different repairs:
the first means the mutation is too broad, the second means the register is
wrong. And `expectedFaults` is a SET — renaming a manifest entry's path is
inherently TWO facts (an entry the layout lacks, and a layout entry the manifest
omits), and a register demanding one would be wrong about the checker rather
than about the bundle.

WHAT THE ESTATE HAS. `MANIFEST_FIXTURE_CORPUS` holds **16 fixtures** across five
classes, 2 valid and 14 invalid — and every one is a LONE MANIFEST:
`{ id, fixtureClass, expect, because, manifest }`, with no profile, layout or
observed entries beside it. A single document can be malformed, out of bounds or
version-skewed; **it cannot DISAGREE WITH ANYTHING**, because there is nothing in
the fixture for it to disagree with. That corpus tests the schema validator.

**THE FINDINGS: THREE OF THE TEN RELATIONSHIPS ARE NEVER COMPARED.**

  media       entries are matched BY PATH and only the DIGEST is compared, so
              the manifest's own `kind`, `slotId` and `byteLength` are never
              checked against the layout's — a manifest can call a document a
              signature and the assembly passes
  ref         the external-reference branch reads the LAYOUT's `externalRef`;
              the manifest's own field is never read by the check at all
  provenance  `producedBy` and `producedAt` are never read, so **a manifest may
              credit anybody and the assembly passes** — and the manifest is the
              only record an auditor will ever see

All three are declared as `expectedFaults: []`, so the register STATES the gap
rather than tripping over it, and the scan fails **in both directions** if any of
them starts firing.

A FOURTH CORRECTION WAS MINE: I expected changing `bytesRead` to raise the
byte-length fault. It raises `digest-not-verified-from-the-bytes` instead, which
is the checker being right — a file shorter than expected fails to verify before
anybody counts it — and the byte-length fault is about the DECLARED length. And
the media break first wrote `'signature'` unconditionally, which is a no-op on
the layout's first entry, so it now chooses relative to what is there — the same
lesson as S11.9.a's kind mutation.

Registered five ways → 237 entry points, 0 violations; `public-api.spec.ts` 246
passing.
-->
  - [x] S11.9.d Fuzz raw remote JSON/XML/form/multipart/archive/media/stream/
        provider/tool/webhook payloads for malformed/truncated/oversized/
        ambiguous/polyglot/version-skew input. Evidence: remote payload suite.
<!--
S11.9.d — libs/oshun/workbench-kit/src/remote-payloads.ts (+ .spec.ts, 24 tests)
and tools/remote-payloads-scan.mjs (7 negative controls). 11 payloads over all
10 kinds and all 6 defect classes.

THE RULE: A POLYGLOT IS A FACT ABOUT A SET, AND THE DECIDER TAKES A SCALAR.

BOTH HALVES OF THE ESTATE ARE RIGHT, AND I CHECKED RATHER THAN ASSUMED.
`sniffMediaType(bytes)` returns FOUR facts — every signature matched, the
leading one, `polyglot`, `executable` — and its comment explains why it returns
a LIST: "a GIF header with a zip's central directory at the end is a valid image
to an image decoder and a valid archive to an unzipper". I expected the
15-signature table to miss that case. It does not:

    GIF89a + padding + PK\x05\x06
      → matches ['application/zip','image/gif'], leading 'image/gif', polyglot true

thanks to a **scanning trailing-zip signature at offset −1** written for exactly
this. And `decideContent` checks the SNIFFED type against the policy — noting
that "the DECLARED type is the far side's claim and is not what is checked" —
and QUARANTINES rather than refuses on a mismatch.

**AND THEY CANNOT BE JOINED WITHOUT LOSS.** `ResponseObservation` carries
`sniffedMediaType: string`. One string. No field for the match list, none for
`polyglot`, none for `executable`. Measured across all three available
collapses, against a policy accepting `image/gif`:

    leading         → collapsed image/gif        → ACCEPTED
    first-match     → collapsed application/zip  → refused
    trust-declared  → collapsed image/gif        → ACCEPTED
                       …and all three discard the polyglot flag

**None can quarantine, because the decision never receives it.** Two of the
three accept. `trust-declared` disables the check entirely: sniffed equals
declared, so nothing ever mismatches and the accepted-media-type test becomes a
test of the far side's own claim.

**AND THE TWO HALVES ARE JOINED NOWHERE.** `sniffedMediaType` has **0**
producers and `decideContent` **0** callers outside a spec, so the collapse has
never been written down and the first host to write it will choose one of the
three by instinct. `decideWithSniff` is that join, written once, in the open,
taking the collapse as an EXPLICIT argument because there is no correct default
— a function that picked one silently would be making the decision this module
exists to surface.

THE SECOND: THREE OF THE SIX DEFECT CLASSES ARE NOT ABOUT THE PAYLOAD.
Malformed, truncated and oversized are properties of the bytes — you can hold
the payload alone and say whether it has them. Ambiguous, polyglot and
version-skew are properties of a RELATIONSHIP between the bytes and whoever
decides what they are: the same payload is unambiguous to a reader that sniffs
and ambiguous to one that trusts a header. A corpus filing all six under "bad
input" loses that.

The scan re-derives the signature table from source, re-counts the producers,
and re-runs the collapse experiment — failing in BOTH directions, so a change
that let the decision carry the polyglot trips it exactly as a regression would.

Registered five ways → 238 entry points, 0 violations; `public-api.spec.ts` 247
passing.
-->
  - [x] S11.9.e Assert bounded time/memory/depth/output/logging, no crash/hang/
        traversal/SSRF/leak, stable typed rejection, and no partial side effect.
        Evidence: invariants.
<!--
S11.9.e — libs/oshun/workbench-kit/src/fuzz-invariants.ts (+ .spec.ts, 27 tests)
and tools/fuzz-invariants-scan.mjs (7 negative controls), run over the S11.9.b
and S11.9.d corpora against the real `parseEnvelopeJson`.

THE RULE: AN INVARIANT YOU CANNOT OBSERVE CANNOT BE ASSERTED, AND ASSERTING IT
ANYWAY IS THE DEFECT.

"No partial side effect" is a claim about what happened somewhere else. "No
leak" is about what appeared in an output. "Bounded logging" is about what was
written. A fuzz run that feeds a malformed envelope to a parser, gets a clean
typed rejection, and concludes that nothing was logged, written or leaked has
concluded three things from a return value that carries none of them.

So each invariant records THE OBSERVATION IT REQUIRES — a clock, a recorder, a
mutation log, an egress log, a process probe, a second run, or the value itself
— and `checkInvariants` returns FOUR verdicts:

    held          observed, and it holds
    violated      observed, and it does not
    unobservable  the seam that would show it is not wired
    vacuous       the subject cannot violate it

**The last two are the point.** `unobservable` reported as `held` is the
unrunnable-lane defect. `vacuous` reported as `held` is how a suite claims
coverage it does not have: a parser is pure, so "no partial side effect" is TRUE
of it and says nothing about the writer three layers down.

**MEASURED over 29 inputs:**

    6 HELD · 0 violated · 3 UNOBSERVABLE · 3 VACUOUS
    unobservable: bounded-memory, bounded-depth, bounded-logging
    vacuous:      no-ssrf, no-traversal, no-partial-side-effect

**SIX OF TWELVE WERE ACTUALLY CHECKED**, and the harness reports
`mostly-unchecked` rather than twelve passes.

THE SECOND: A STABLE TYPED REJECTION IS A CLAIM ABOUT TWO RUNS, NOT ONE. Every
other invariant can be judged from a single execution, so `runTwice` is built
into the recorder rather than left to a caller who might skip it when the suite
is slow — a minimized reproducer that does not reproduce is worse than none.

THE THIRD: A CRASH AND A HANG ARE THE SAME OBSERVATION FROM DIFFERENT SIDES. A
throw is loud and immediate; a hang is silent and indefinite, and a harness with
no deadline turns the second into a process killed by something outside the test
whose report says nothing at all. The deadline is part of the observation.

AND PURITY BOUNDS EFFECTS, NOT WORK: `bounded-time` and `bounded-memory` are
deliberately NOT vacuous for a pure subject, because a pure function can still
take quadratic time and allocate a gigabyte on an adversarial input.

Registered five ways → 239 entry points, 0 violations; `public-api.spec.ts` 248
passing.
-->
  - [x] S11.9.f Seed runs deterministically, shrink failures, retain smallest
        reproducer with seed/schema/version/expected result, and promote to
        regression fixture. Evidence: minimized corpus workflow.
<!--
S11.9.f — libs/oshun/workbench-kit/src/shrinking.ts (+ .spec.ts, 22 tests) and
tools/shrinking-scan.mjs (7 negative controls), emitting
`evidence/v1-workbenches/workbench-kit-minimized-corpus.jsonl` — 13 reproducers,
6170 bytes saved, every one replaying.

THE RULE: A SHRINK IS ONLY VALID IF THE SMALLER CASE FAILS FOR THE SAME REASON.

Measured on the estate's own `versions/below-minimum` case, the same input shrunk
twice:

    still-fails     548B → **2B** `{}`                  fails **unknown-kind**
    same-signature  548B → 18B  `{"kind":"command"}`  fails invalid-field

The first is a reproducer for a DIFFERENT DEFECT, reported with total confidence
— whoever picks it up fixes the wrong thing. So `shrink` takes a predicate
returning a FAILURE SIGNATURE, not a boolean, and keeps a candidate only when the
signature matches; three candidates were refused for drift on this one case.
That is S11.9.a's discipline applied where "still fails" is most tempting.

A REPRODUCER IS A CLAIM SOMEBODY ELSE CAN RUN, AND FOUR OF ITS FIVE FIELDS ARE
ABOUT THE RUNNER: the schema says what it was validated against, the version
which build, the `expectedResult` what "reproduces" means, and the seed how to
reach the generator state. A corpus of texts alone is a corpus of things that
USED to fail.

**THE THIRD, AND IT LIMITS THE WHOLE TECHNIQUE: A SHRINK CAN BE NO MORE PRECISE
THAN THE FAILURE THE SUBJECT REPORTS.** That 18-byte reproducer is
`{"kind":"command"}` — which carries NO `schemaVersion` rather than a zero one.
`parseCommon` answers a missing and a zero `schemaVersion` with a byte-identical
`invalid-field | schemaVersion must be an integer >= 1`, verified here. A finer
signature does not help: code-only and code-plus-message reach the same 18 bytes.
**The fix is a subject that distinguishes, not a predicate that squints harder**
— and the same parser DOES distinguish elsewhere (`not-a-string` vs
`missing-field` for `envelopeId`, S11.9.a). Two levels of precision in one file.

And deletion is CHEAPER than substitution for any shrinker, so wherever a
subject conflates "absent" with "wrong value", the minimized case will always be
about absence. Worth knowing before reading a reproducer as a description of the
bug.

A PROMOTED FIXTURE THAT STOPS FAILING MUST SAY SO, NOT PASS. `replay` returns
`no-longer-reproduces` — a fix is good news that must be written down, because
the expectation is now wrong and the fixture covers nothing until somebody says
what it should cover instead. A regression corpus whose entries can quietly
become vacuous grows without ever telling you what it still covers.

The scan runs the whole loop every time — shrink, build, write, read back,
replay — and re-runs the two-predicate comparison, failing if the loose
predicate ever stops drifting.

Registered five ways → 240 entry points, 0 violations; `public-api.spec.ts` 249
passing.
-->
  - [x] S11.9.g Run fast mutation corpus in PR and broader scheduled fuzzing
        with resource limits and owner routing. Evidence: CI schedule.
<!--
S11.9.g — libs/oshun/workbench-kit/src/fuzz-schedule.ts (+ .spec.ts, 22 tests),
tools/fuzz-schedule-scan.mjs (6 negative controls), and the deliverable itself:
**.github/workflows/v1-workbench-kit-verification.yml**.

THE RULE: A CHECK THAT NOTHING SCHEDULES IS A CHECK NOBODY RUNS, AND A
REPOSITORY FULL OF THEM LOOKS EXACTLY LIKE A REPOSITORY THAT VERIFIES ITSELF.

`package.json` declared **82 `verify:operations:*` scripts** and exactly **8**
were named by any workflow, all in `v1-disaster-recovery.yml`. The other **74
worked and nothing ran them**. That is not a criticism of the scans; it is the
shape of defect hardest to see from inside — a directory of ninety scanners is
indistinguishable at a glance from a repository that checks ninety things.

A TIER IS DECIDED BY WHAT A CHECK NEEDS, NOT BY HOW IMPORTANT IT IS. **8 of the
90 scans open a PostgreSQL connection** and cannot run in a job that has none,
however important; the other 82 read files and finish in ~1.7s each, however
minor. (`tools/` holds 135 `.mjs` files — 90 scans and 45 race probes the scans
drive. 31 of the 135 open a database and only 8 of them are scans, a distinction
my first tiering got wrong and **this cell's own scan caught**.)

OWNER ROUTING WITH ONE OWNER IS NOT ROUTING: `.github/CODEOWNERS` carries **94
rules and one owner**, catch-all included. Recorded as `single-owner` — a real
state whose consequence is that the routing cannot go wrong and cannot help.

THE WORKFLOW partitions the scans **by path at run time**, so a scan added
tomorrow is scheduled without anybody filing it; runs the files-only tier on
every PR touching the kit and the database tier nightly against a postgres
service; refuses an empty partition (a pattern matching nothing would leave the
job green having run nothing); reports `exit 2` as *undetected negative control*
distinctly from a failure; and carries **no `continue-on-error` and no
`|| true`** — six other workflows in this repository already do.

**AND THE FIRST HONEST RUN FOUND NINE FAILING SCANS.**

  · **three were genuine estate drift** from commits that landed on main — a
    register said 759 `.evaluate(` calls in the e2e suites and there are 760;
    verified at the register's own authoring commit, where it was 759
  · **five were one systemic trap.** A REGISTER THAT COUNTS ITS OWN FAMILY
    INVENTS A DEFECT, and **THE FAMILY HAS FIVE MEMBERS** — module, spec, scan,
    the Nx doc in `project.json`, and the export-map reason — **four of which
    carry PROSE about the very terms the scan searches for**, and the fifth
    lives OUTSIDE `libs/oshun/workbench-kit`, so every exclusion written as a
    `startsWith` on that directory misses it. One scan searched for a literal
    its own source contains, inside the regex doing the searching.
  · **one was a real crash in shipped kit code.** `game-day.ts` gained
    `announcedToActors`, `unrehearsableBecause`, `presentsAs`, `confirmedBy`,
    `movesTheRecoveryPoint` and `detects` after its `.mjs` probe was written.
    The absent fields read as `undefined`, and `undefined` slipped past every
    `=== null` guard and threw on `.length`. **A .MJS PROBE AGAINST A TYPED
    MODULE HAS NO COMPILER BETWEEN THEM**, so a field added to the type is not
    added to the fixture and nothing says so until the probe runs — which
    nothing did. Four of seventeen conditions were left indistinguishable; the
    kit arm now scores 17/17.

All nine repaired. **All 83 file-only scans pass and the kit's 15,257 tests
pass.** Registered five ways → 241 entry points, 0 violations.
-->
- [x] S11.10 Add malicious upload and remote-fetch fixtures. Evidence: archive,
      media, path, metadata, and SSRF cases.
<!--
S11.10 CLOSED — a through g. Seven cells, seven corpora, and one shape that
turned up so often it stopped being a coincidence.

THE SHAPE: A HAZARD IS A PROPERTY OF A PAIR, AND EVERY CORPUS THAT HOLDS ONLY
ONE HALF OF THE PAIR IS ANSWERING A QUESTION NOBODY ASKED.

  .a  a collision needs two names, and it is a collision only on a filesystem
      that folds — so a name check reading one path cannot hold one.
  .b  'executes' is not a property of a file, it is a property of what OPENS it.
  .c  the same pair again, and the estate refuted three of my four claims before
      the fourth survived.
  .d  a metadata value is dangerous in one SINK and inert in another; '=1+1' is
      four characters in HTML and a live formula in a CSV export.
  .e  a spelling is a bypass only against a guard that SEES it, and 8 of the
      estate's 21 SSRF targets reach a post-parse guard respelt.
  .f  a transport hazard is a transfer that never ends, and every observation
      the estate can make is a record of one that DID.
  .g  and then: five of the six closing expectations cannot be violated by a
      fixture at all, because the subject was never the fixture.

Every one of those needed a register that returns BOTH lists — where a case
bites and where it is inert — because a register that recorded only the first
makes "harmless everywhere" and "never tested" identical. Three cells needed a
THIRD value on top of that: S11.9.e's unobservable, S11.10.f's from-neither,
S11.10.g's intentionally-persistent. In each case a boolean would have reported
a defect against something correct, or a pass against something nobody checked.

WHAT THE ESTATE ALREADY HAD, because measuring first was the discipline that
made this section work. bundle-paths modelled all three filesystems and refuted
three of .a's claims. The media sniffer caught the canonical GIF+zip polyglot I
predicted it would miss. fetching.ts covered ten of .e's eleven categories and
all of .f's machinery. Five of the seven cells found MORE credit than gap, and
saying so is the honest half of each entry.

LIVE DEFECTS FOUND AND FIXED: game-day-scan leaking 4 temp directories a run
(88 accumulated, 2.4 MB) and asTheResolverSeesIt quoting an attacker-chosen
hostname into a diagnostic. LIVE DEFECTS FOUND AND RECORDED: three of
FetchBounds' five fields have no reader, so a five-gigabyte compressed response
that decompresses to four kilobytes is ACCEPTED; `timeout` was a declared
refusal nothing in the repository could produce; and the SSRF corpus's own prose
names a trailing-dot bypass the corpus does not contain.

AND THE TRAP THAT KEPT FIRING: a register that counts its own family invents a
fact. Five separate instances across this section, the last two in .g alone — a
network census that found a scan SEARCHING for a fetch call, and a writer census
that found the pattern naming the writing functions inside its own source. The
answer is never a longer exclusion list: it is to ask what a file IMPORTS rather
than what it mentions, or to assemble the search string at run time so the
searcher cannot match itself.

7 modules, 7 specs, 7 scans, 5 registrations each. Kit suite 244 files / 15,435
tests green; 248 entry points, 0 violations.
-->
  - [x] S11.10.a Add archive fixtures for traversal/absolute/symlink/hardlink/
        device/duplicate/case/Unicode/reserved paths, excessive entries/depth,
        nested archives, and decompression bombs. Evidence: archive corpus.
<!--
S11.10.a — libs/oshun/workbench-kit/src/archive-fixtures.ts (+ .spec.ts, 27
tests against the estate's real checkers) and tools/archive-fixtures-scan.mjs
(8 negative controls). 15 recipes over all 13 hazards.

**WHAT I CLAIMED AND THE ESTATE REFUTED — THREE TIMES.** I set out to report
that `bundle-paths.ts` reads only names, so an entry's KIND, a collision's
DESTINATION and a resolved traversal are all invisible to it:

  · `specialFileFaults` checks `ADMISSIBLE_FILE_KINDS` — symlink, hardlink,
    device and fifo are refused BY KIND already
  · `collisionFaults(paths, target)` **TAKES the destination filesystem**, and
    all three behaviours are modelled: `POSIX_EXT4`, `WINDOWS_NTFS` and
    `APPLE_APFS` with `unicodeForm: 'nfd'`. NFC/NFD of one word →
    `unicode-fold-collision` on APFS and **nothing** on ext4;
    `README.md`/`readme.md` → `case-fold-collision` on NTFS
  · a name resolving INSIDE the root (`a/b/../c/thing.txt`) is reported as
    `dot-dot-segment` and **not** as `escapes-its-own-root`

That last one caught my scan, not the estate: I treated any finding as a
refusal, which would have reported the estate as rejecting a legitimate entry.
**AN OBSERVATION AND A REFUSAL ARE DIFFERENT FINDINGS, AND A READER THAT COUNTS
THEM TOGETHER INVENTS A DEFECT.**

**THE RULE THAT SURVIVED: A KIND CHECK AND A TARGET CHECK ARE DIFFERENT CHECKS,
AND ONLY ONE CAN BE ASKED HERE.** `ObservedFileEntry` is
`{ path, kind, linkCount }` — there is **no field for where a link points**, and
nothing in the module names one. So the only expressible policy is "no links at
all", which is sound. The moment a deployment admits symlinks, because a bundle
legitimately carries one, nothing can ask where one goes: a link named
`docs/readme` is confined, normalized, in NFC and innocent by every check in the
file, and its target is `../../../../etc/passwd`. The scan reads the field list
out of the source and fails **in both directions** — the day a target field
appears, the register is wrong and says so.

THE CHECKERS EXIST AND NOTHING FED THEM THE HOSTILE INPUT — the same shape as
S11.9.a, five parsers and no generator. The corpus is the deliverable.

AND AN ARCHIVE FIXTURE THAT IS SAFE TO COMMIT IS NOT THE ARCHIVE YOU NEED. A
bomb is 42 KB expanding to petabytes; committing one puts a live hazard in every
clone, flags every content scanner, and is one double-click from a full disk in
any tool that previews archives helpfully. So the corpus holds **recipes** —
deterministic builders with expected refusals — and the bytes exist only inside
the run. **4 of the 15 must never be files**: the bomb, a million entries, a
2000-deep path and a device node. The million-entry recipe builds and counts
without being walked.

Registered five ways → 242 entry points, 0 violations; `public-api.spec.ts` 251
passing.
-->
  - [x] S11.10.b Add image/audio/video/document/3D fixtures for wrong declared/
        actual type, polyglot, malformed headers/chunks, huge dimensions/
        duration/frames, parser bugs, embedded active content, and corrupted
        metadata. Evidence: media corpus.
<!--
S11.10.b — libs/oshun/workbench-kit/src/media-fixtures.ts (+ .spec.ts, 23 tests
against the estate's own sniffer) and tools/media-fixtures-scan.mjs (8 negative
controls). 11 cases over all 8 hazards.

THE RULE: "EXECUTES" IS NOT A PROPERTY OF A FILE, IT IS A PROPERTY OF WHAT OPENS
IT.

`EXECUTABLE_MEDIA_TYPES` lists four formats — ELF, Mach-O, a Windows PE, a shell
script — with a comment saying, correctly, "not a policy: a fact about the
format". It is a fact about the format **when the opener is an operating
system**. A media pipeline's opener is a browser, a PDF viewer, an image
library. Measured against the estate's own `sniffMediaType`:

    an SVG with a <script> element   →  matches [], executable FALSE
    an HTML file with a <script>     →  matches [], executable FALSE
    a PDF carrying /JavaScript       →  matches ['application/pdf'], executable FALSE

A user asked for an image uploads a program, and every check in the pipeline
agrees it is not executable.

**AND THE CURRENT SAFETY IS ACCIDENTAL.** Two of the three sniff as *no format
at all*, so a policy with a non-empty `permittedMediaTypes` refuses them as
`declared-type-unrecognisable`. That works — because the sniffer does not know
what an SVG is. **The day somebody adds an SVG signature, which is the obvious
improvement to make when SVG uploads are wanted, the accidental refusal becomes
an acceptance** and `executable: false` is what decides it. The scan fails if a
signature for SVG or HTML appears, so the improvement cannot land silently.

THE SECOND: A DIMENSION IS A NUMBER IN A HEADER, AND THE ALLOCATION HAPPENS
BEFORE ANYTHING VERIFIES IT.

    a 33-byte PNG header declaring 65535 × 65535
      = 4,294,836,225 pixels = 17.18 GB of RGBA
      and maxByteSize bounds 33

Nothing in the kit reads a header past its magic number, so nothing could bound
it. `declaredPixels` computes the claim so a caller can refuse on the claim
rather than on the size — and returns **null rather than zero** for anything
that is not a PNG header, because a zero from an unparsed header and a zero from
a real one are the same number and not the same fact. The ordinary 1920×1080
control lives in the same 33 bytes, so the check is shown to discriminate rather
than to refuse everything.

THE THIRD: A CHUNK LENGTH IS ALSO A NUMBER IN A HEADER. A chunk claiming 4 GB
inside a 20-byte file is a refusal or a read past the end depending entirely on
whether the parser believes the number or the buffer.

Registered five ways → 243 entry points, 0 violations; `public-api.spec.ts` 252
passing.
-->
  - [x] S11.10.c Add filenames/paths/IDs for Unicode normalization/confusables/
        bidi/invisible/control, encoded traversal, drive/UNC/device, trailing
        dots/spaces, separators, and extreme length. Evidence: path corpus.
<!--
S11.10.c — two vectors added to `PATH_VECTOR_CORPUS` (27 → 29) and
libs/oshun/workbench-kit/src/path-pairs.ts (+ .spec.ts, 19 tests) with
tools/path-pairs-scan.mjs (7 negative controls).

**MOST OF THIS CELL WAS ALREADY DONE.** `PATH_VECTOR_CORPUS` carried **27
vectors covering 9 of the 10 categories** — normalization, bidi, invisible,
control, encoded traversal, drive/UNC/device, separators, reserved names, and
confusables through a real per-component script-mixing check with Latin,
Cyrillic and Greek ranges. Each vector records the DECODE STAGE at which it
becomes visible. Saying so is the honest first half of this entry.

**THE ONE GAP WAS LENGTH — AND A CORPUS OF HOSTILE CHARACTERS HAS NO ENTRY FOR
"TOO MANY ORDINARY ONES."** Every existing vector holds something wrong IN THE
STRING: a control character, a bidi override, a Cyrillic lookalike, a
percent-encoded separator. The two added here hold nothing wrong at all — three
hundred ordinary letters, forty ordinary directory components — and the fault is
the COUNT. `component-longer-than-the-limit` and `deeper-than-the-limit` both
already fired; the corpus's longest path was **32 characters**.

**THE SECOND: A HAZARD THAT NEEDS TWO INPUTS CANNOT LIVE IN A CORPUS OF ONE.**
Third time this section has met that shape — S11.9.b (a corpus of objects cannot
hold a duplicate key), S11.9.c (a lone manifest cannot disagree with anything),
and now this. `bundlePathFaults('story/body.json.')` returns `[]`, correctly:
there is nothing wrong with it. It becomes a hazard only beside
`story/body.json`, and only where a filesystem trims. The estate files it
exactly there, as `trailing-dot-or-space-collision`.

Measured against the estate's three modelled destinations:

    body.json / body.json.     NTFS only
    README.md / readme.md      NTFS and APFS
    café NFC / café NFD        APFS only

Three pairs, three answers, and **no pair is a hazard everywhere** — one that
were would be a property of the names and would belong in the single-path
corpus. So "does this bundle contain a collision?" has no answer until the
destination is named: a bundle built on Linux, scanned on Linux and extracted on
a developer's Mac passes every check and overwrites a file.

`readPair` returns BOTH the dangerous and the harmless destinations, because a
register recording only where a pair is dangerous would make "harmless
everywhere" and "never tested" look the same — the same defect as reporting an
unobservable invariant as held (S11.9.e).

AND MY OWN FREEZE CAUGHT ME: `.sort()` mutates in place, and `readPair` returns
frozen arrays, so the spec's assertions threw until they sorted a copy.

Registered five ways → 244 entry points, 0 violations; `public-api.spec.ts` and
`bundle-paths.spec.ts` 297 passing together.
-->
  - [x] S11.10.d Add EXIF/XMP/ID3/container/document/archive/3D/filename/title/
        caption/comment metadata containing injection, secrets, protected data,
        URLs, scripts, and oversized nested values. Evidence: metadata corpus.
<!--
S11.10.d — libs/oshun/workbench-kit/src/metadata-fixtures.ts (+ .spec.ts, 20
tests) and tools/metadata-fixtures-scan.mjs (7 negative controls). 11 payloads
over 6/6 classes, 8/8 fields and 8/8 sinks.

THE RULE: A FIELD CALLED "COMMENT" IS INPUT, AND ITS NAME IS THE WHOLE REASON
NOBODY TREATS IT AS ONE. An EXIF `UserComment`, an XMP `dc:title`, an ID3 artist
tag, a PDF `/Title`, a filename — every one is bytes the uploader chose,
arriving labelled as a *description* rather than a request, and then rendered in
an admin panel, written into a log line, interpolated into an export, used to
name a download.

**THE SECOND, AND IT IS THE SHAPE OF THIS WHOLE SECTION: THE HAZARD IS IN THE
PAIR (BYTES, CONSUMER), NOT IN THE BYTES.** Fourth time S11.9–S11.10 has met it:

    S11.9.d   a payload has no format; a READER has a format
    S11.10.b  "executes" is a property of what OPENS it
    S11.10.c  a collision is a property of the DESTINATION
    S11.10.d  a metadata value is a hazard in one SINK and inert in another

`=1+1` is four harmless characters in an HTML page and a **live formula** the
moment the same value reaches a spreadsheet export — an HTML escaper does
nothing to it and is not wrong to. A newline is nothing in a caption and a
**forged log record** in a log line. `${…}` is inert in a renderer that escapes
and a read of the environment in one that interpolates, and which of those a
codebase uses is not a property of the value.

So every payload records the sinks it is dangerous in AND the sinks it is inert
in, and the two lists must **PARTITION** the sink set exactly. A sink named in
neither is one nobody has considered, and a register permitting that state would
make "inert everywhere" and "never thought about" look the same — the same
defect as reporting an unobservable invariant as `held` (S11.9.e) and as
recording only where a path pair is dangerous (S11.10.c). No payload is
dangerous everywhere (that would be a property of the bytes) and none is
dangerous nowhere (that demonstrates nothing).

A NUL is filed as a **WRITE THAT CANNOT HAPPEN** rather than an injection:
PostgreSQL refuses it outright, so the upload succeeds and the row does not.

THE THIRD: THE SIZE OF A METADATA FIELD IS NOT BOUNDED BY THE SIZE OF THE FILE.
XMP is XML inside an image and XML nests, so a 2 kB JPEG can carry two thousand
levels of nesting and exhaust a recursive walker from a file that passed every
size limit. The oversize is in a part nothing weighs separately.

AND THE NUL IS BUILT WITH AN ESCAPE. A literal NUL byte in a source file makes
it **binary to `grep`**, so a scan over it reports zero hits for the same reason
a clean file does — a trap this repository has been caught by before. The first
draft of the module carried a raw one; it was replaced, and **the scan now checks
this cell's own three files for raw NULs on every run**, with a positive control
that the files are non-empty so "no NULs" is not the silence of an unread file.

Registered five ways → 245 entry points, 0 violations; `public-api.spec.ts` 254
passing.
-->
  - [x] S11.10.e Add URLs/redirect/DNS fixtures for loopback/private/link-local/
        reserved/metadata/internal, userinfo, IDNA, IP encodings, rebinding,
        redirect escape, alternate ports/schemes, and credential leakage.
        Evidence: SSRF corpus.
<!--
S11.10.e — MOST OF THIS CELL WAS ALREADY BUILT, AND THE ONE THING MISSING WAS
NOT A CATEGORY.

MEASURED FIRST, as S11.10.a and .c taught. libs/oshun/workbench-kit/src/
fetching.ts already carries: SSRF_CORPUS, 21 targets over 9 classes covering
loopback, private, link-local, reserved, metadata and encoded IP forms;
REDIRECT_POLICIES with validate-every-hop and a maximumRedirects bound (redirect
escape); connectionWasPinned comparing validatedAddress against
connectedAddress, which is the whole of DNS rebinding; withoutUserinfo
(userinfo); redactUrl, looksSigned and SIGNED_URL_MARKERS (credential leakage);
allowedSchemes and allowedPorts on RemoteFetchPolicy with scheme-outside-http
and unbounded-port faults (alternate ports/schemes); and
DEPLOYMENT_SUPPLIED_CLASSES marking internal-service as the deployment's to
supply. Ten of the cell's categories were done. Only IDNA had zero cases — a
grep for punycode, idna and xn-- across fetching.ts returns 0.

But IDNA turned out to be the symptom, not the finding.

THE RULE: A SPELLING IS A BYPASS ONLY AGAINST A GUARD THAT SEES IT.

Third consecutive cell to land on this shape, so it is a shape and not a
coincidence. S11.10.c: whether a bundle contains a collision has no answer until
the destination filesystem is named. S11.10.d: whether '=1+1' is dangerous has
no answer until the sink is named. Here: whether 2130706433 is a bypass has no
answer until somebody says WHERE THE GUARD STANDS.

runCorpus(refuses, corpus) hands the guard entry.target — the string somebody
typed into the corpus. A deployment whose guard reads the raw user string and
one whose guard reads new URL(input).hostname are two different systems, they
fail to different inputs, and both call runCorpus and record the same green
result.

WHAT WAS MEASURED — all 21 targets through Node's own WHATWG URL parser. EIGHT
reach a post-parse guard as a string the corpus does not contain, three ways:

  127.1  2130706433  0177.0.0.1  -> REWRITTEN to 127.0.0.1. Against a post-parse
      guard these three test nothing. They were real bypasses, against the
      inet_aton-era checkers they were written for, which read the string.
  ::ffff:127.0.0.1  ::ffff:169.254.169.254
                              -> REWRITTEN into hex: [::ffff:7f00:1] and
      [::ffff:a9fe:a9fe]. A post-parse guard comparing against the corpus string
      never matches. THE ONE DIRECTION WHERE STANDING AFTER THE PARSER IS THE
      WEAKER POSITION.
  ::1  fe80::1  fc00::1       -> BRACKETED. Raw new URL THROWS on a bare IPv6
      literal — it is not a URL host. asTheResolverSeesIt brackets rather than
      reporting it unparseable, because refusing to answer would hide the
      address rather than reveal it. Same address, different string: a
      membership test against ::1 does not match [::1].

Not one of those eight is wrong. Each is wrong ONLY at an unstated vantage
point, which is the whole point.

THE DEMONSTRATION, and it is decisive. Build the most literal guard a deployment
could write from that corpus — membership in SSRF_CORPUS — and run the corpus
against that ONE guard twice:

    as-written   0 got through
    as-resolved  6 got through: [::1] [::ffff:7f00:1] [::ffff:a9fe:a9fe]
                 [fe80::1] [fc00::1] example.com

Five are forbidden addresses in another spelling. The sixth is the credentialed
entry, whose target is a userinfo string rather than a destination; a guard
SHOULD let example.com through and that entry's hazard is leakage, not where the
socket goes — recorded in the spec rather than counted as a miss. Nothing about
the corpus or the guard changed between those two runs. runCorpus reports the
clean answer, correctly, for the only question it can ask.

THE PARSER CREATES SPELLINGS TOO. Circled digits are IDNA compatibility variants
of the digits, so a name containing no digit and no dot resolves to
169.254.169.254 — the target that makes an SSRF a credential theft rather than a
port scan. A tab inside a host is STRIPPED by the parser, joining two labels a
reader sees as separate. Userinfo puts a friendly name where every reader, every
log line and every indexOf check looks, and the metadata endpoint where the
socket goes; a hash turns a trailing at-sign into decoration, so somebody who
has learnt the userinfo trick reads that one backwards and calls it safe. A
Cyrillic lookalike does NOT reach the name it resembles, which is why it is
included: a denylist comparison fails and so does the attack, while an ALLOWLIST
comparison also fails and refuses a legitimate name that renders identically.

AND ONE THAT NORMALIZING DOES NOT FIX. 127 written with U+3002 IDEOGRAPHIC FULL
STOP separators is unchanged by NFKC — U+3002 is not a compatibility variant of
the full stop — so a guard that normalizes and then checks still sees one
dotless label while fetch sees 127.0.0.1. IDNA's own label-separator rule is
what turns it into an address and the only thing in reach implementing that rule
is the URL parser. Hence asTheResolverSeesIt: NORMALIZE BY PARSING, NOT BY
NORMALIZING.

WHAT I GOT WRONG, TWICE, AND THE REGISTER CAUGHT BOTH. The reach-not-as-declared
check parses every spelling's `written` and compares it to the declared
`reaches`. On its first run it reported that idna/circled-digits-loopback
reaches 128.0.0.1 and idna/circled-digits-metadata reaches 179.254.179.254 — I
had typed a circled 8 for a 7 and a circled 17 for a 16. That is the entire
reason the field is computed rather than believed. I also wrote in the module
header that the parser THROWS on all five bare IPv6 targets, which is true of
raw new URL and FALSE of asTheResolverSeesIt, which brackets them; the header
now states all three outcomes separately and the census reports them as three
numbers (5 rewritten, 3 bracketed, 0 refused) rather than one.

ALSO FOUND: the corpus prose names 'a trailing dot that changes DNS resolution
but not string comparison' among its bypasses and the corpus contains no target
ending in a dot. Supplied here as parser-rewrite/trailing-dot, and the scan
re-reads that prose each run so the finding disappears the day somebody adds a
case for it.

SHIPPED. libs/oshun/workbench-kit/src/url-spellings.ts — VANTAGE_POINTS with
what each one sees, asTheResolverSeesIt, changesUnderParsing, 12 SPELLINGS over
5 classes (idna, numeric-form, deceptive-authority, parser-rewrite, confusable),
readCorpusAtBothVantagePoints, runCorpusAt(point, refuses, corpus), 6 fault
kinds, census and headline. Every spelling names the vantage points it fools AND
the ones where it is inert, and the two must PARTITION the vantage-point set —
same discipline as S11.10.d's sink partition and S11.10.c's returning both
destination lists, so a vantage point nobody considered cannot look like one
where the spelling is known to be harmless. A target the parser refuses is
reported `unreachable`, not as a pass.

VERIFIED. 31 tests in src/url-spellings.spec.ts; kit suite 242 files / 15,383
tests green. tools/url-spellings-scan.mjs re-derives all four corpus counts by
re-parsing every target, recomputes the two-vantage-point demonstration from
scratch, verifies all 12 declared reaches against the parser, and checks this
cell's own three files for raw NUL bytes; 8 negative controls, exits 2 if any
goes undetected. Five registrations done — kit package.json exports,
tsconfig.base.json path, root script verify:operations:v1-url-spellings, Nx
target url-spellings-scan, export-map register; generator run: 246 entry points,
0 violations.
-->
  - [x] S11.10.f Add slow headers/body, chunked lie, no length, endless stream,
        timeout/retry, TLS/host mismatch, oversized compressed response, and
        malicious content-type fixtures. Evidence: transport corpus.
<!--
S11.10.f — THE RULE: A TRANSPORT HAZARD IS A TRANSFER THAT NEVER ENDS, AND EVERY
OBSERVATION THE ESTATE CAN MAKE IS A RECORD OF ONE THAT DID.

Six of the eight hazards this cell names are not responses at all. A slow-loris
header dribble, a body arriving one byte a minute, an endless stream, a chunked
encoding that never sends its terminating zero — each is a socket still open,
still making progress by every measure the far side controls, and never
finishing. Only two — an oversized compressed response and a lying content-type
— can be held in your hand and judged.

MEASURED FIRST. fetching.ts is a serious piece of work and almost all of the
machinery is there. What is missing is the thing that reads it:

  FetchTimeouts   6 fields. ALL SIX READ, ALL SIX INSIDE validateFetchPolicy,
                  WHICH COMPARES THEM TO EACH OTHER (phase <= total). Not one is
                  ever compared to an elapsed measurement, because no function
                  in the estate takes one. A policy may declare headersMs: 5000
                  and nothing can be handed "96 header bytes, 90 seconds".
  FetchBounds     5 fields. THREE HAVE NO READER AT ALL — compressedBytes,
                  archiveEntries, archiveDepth. Verified by calling the estate's
                  own decideContent: five gigabytes on the wire that decompress
                  to four kilobytes comes back ACCEPTED, because the decompressed
                  bound is satisfied, the ratio is below one, and the bound
                  written for exactly this case is never read.
  FETCH_REFUSALS  11 declared, 4 returned by fetching.ts (all in decideContent),
                  6 left to the host through GuardedFetchSeam — legitimate for a
                  seam — and `timeout` PRODUCED BY NOTHING IN THE REPOSITORY. No
                  host could produce it either: nothing gives one an elapsed time.
  FetchOutcome    fetched | refused. No third state. A transfer that is still
                  going has neither, and the type cannot say so.
  ResponseObservation
                  4 fields, every one a TOTAL. A chunked lie is a disagreement
                  between a declared length and what arrived, and there is no
                  field for a declared length.

SHIPPED. libs/oshun/workbench-kit/src/transport-fixtures.ts — TransferProgress,
the observation the estate cannot make (phase, elapsed total, elapsed in phase,
time since the last byte, header and body bytes, what the far side claimed, and
whether a chunked response is still unterminated); decideInFlight, THE FIRST
PRODUCER OF `timeout` IN THIS REPOSITORY, phase-aware because ninety seconds in
headers and ninety in body are refused by different clauses of one policy;
TRANSFER_PHASES mapped 1:1 onto the FetchTimeouts fields, with totalMs the one
deliberately unmapped because it bounds all of them; 9 cases over 9 hazards; 7
fault kinds; census and headline.

A null declaredLength is kept distinct from a zero one, because "would not say"
and "said it would send nothing" are different claims and only the second makes
one further byte an overrun. The chunked lie abandons with a NULL refusal,
because FETCH_REFUSALS has no kind for a sender that exceeded its own
declaration and borrowing one that means something else would file it under a
wrong cause forever.

A THIRD DECIDABILITY, WHICH THIS CORPUS FOUND BY BEING WRONG. The TLS host
mismatch was first filed as in-flight, and the register reported it
`not-abandoned`: decideInFlight returns keep-waiting for it. THAT ANSWER IS
CORRECT. A handshake that completes quickly to the wrong name is not slow, not
large and not malformed; every bound the policy states is satisfied, and the
fact that decides it — the name on the certificate — is in no type here. So it
is filed `from-neither`, because "nothing in reach can decide this" is a
different statement from "it passed". A corpus that had quietly retuned a
threshold until the case abandoned would have shipped a working-looking check
over a fact it never read. Same discipline as S11.9.e's fourth verdict and
S11.10.c returning both destination lists.

AND A CHECK THE CORPUS NEEDED: caught-by-a-different-bound. THREE cases were
written with numbers that abandoned for a reason other than the bound their own
prose named — the retry case at nine seconds in connect against a connectMs of
three, and the two slow cases pushed past the total — and all three ABANDONED,
so the suite was green and each demonstrated something weaker than it claimed. A
NUMBER THAT AGREES WITH A VERDICT IS NOT A NUMBER THAT AGREES WITH THE REASON.
The check reads the decider's own detail line rather than re-deriving the
decision, because a second implementation would agree with itself and not with
the decider. All three cases were rebuilt to sit inside every other bound, so
each now shows why its own bound exists.

TWO TRAPS, BOTH IN THE SCAN, BOTH ALREADY KNOWN TO THIS SECTION.
`git grep` READS THE INDEX, so the scan's first run reported that its own module
does not exist — twice — because the files were not staged; fixed with
`--untracked` rather than by staging, so the answer no longer depends on whether
somebody ran `git add`. And THE SCAN'S OWN QUERY CONTAINED THE LITERAL IT
SEARCHED FOR: grepping for the timeout-refusal string found the grep. The
pattern is now assembled at run time from two halves, which is better than an
exclusion list because an exclusion list can also hide a real hit; a positive
control confirms the query still matches a refusal known to be produced.

VERIFIED. 27 tests in src/transport-fixtures.spec.ts; kit suite 243 files /
15,410 tests green. tools/transport-fixtures-scan.mjs re-parses fetching.ts on
every run to recount its timeout fields, its bound fields, which bounds have no
reader and which refusals are returned — nothing is quoted from the register —
and it re-derives validateFetchPolicy's line range from the source rather than
hardcoding it. 9 negative controls, exits 2 if any goes undetected. Five
registrations done — kit package.json exports, tsconfig.base.json path, root
script verify:operations:v1-transport-fixtures, Nx target
transport-fixtures-scan, export-map register; generator run: 247 entry points, 0
violations.
-->
  - [x] S11.10.g Assert quarantine/no parse before scans, resource bounds,
        stable refusal, redacted diagnostics, cleanup, and zero outbound/
        persistent side effects. Evidence: fixture expectation matrix.
<!--
S11.10.g — THE RULE: FIVE OF THESE SIX EXPECTATIONS CANNOT BE VIOLATED BY A
FIXTURE, AND ALL SIX CAN BE VIOLATED BY WHAT RUNS IT.

Written as a matrix of fixtures × expectations this grades six greens and says
nothing. MEASURED: all 239 modules under libs/oshun/workbench-kit/src — every
one, not a sample — import ZERO capabilities. No node:fs, no
node:child_process, no node:net, no database client. A frozen array cannot fail
to clean up, cannot open a socket, cannot exceed a bound it never allocates.
Grading it on those is not a weak test, IT IS NOT A TEST: the green is a
property of the KIND of thing being graded. So the subject is wrong, and naming
the right one is the cell.

WHAT ACTUALLY RUNS THE FIXTURES: 97 scan tools — 89 read the filesystem, 68
spawn a subprocess, 9 open a database connection, 0 make an outbound call, 4
WRITE.

TWO LIVE DEFECTS, BOTH FOUND HERE AND BOTH FIXED IN THIS COMMIT.

FIRST — game-day-scan.mjs called mkdtempSync on every invocation, four
invocations a run, and there was NO rmSync anywhere in the file. Measured by
counting the OS temp directory before and after a run: four more every time, 88
accumulated at 2.4 MB. It now removes the tree in a `finally`, because the
interesting runs of that scan are the ones that throw. The 88 leftovers were
swept. And this cell's scan RE-RUNS IT and re-counts rather than grepping for
rmSync — a grep would pass on a file that calls it in a branch nothing reaches.

SECOND — seven diagnostics across the six corpora are built from their own
input. Six use a lookup key or a hand-written corpus id: bytes a developer typed
into this repository, and quoting them is how a caller finds their typo. ONE
received a hostname supplied by whoever is being defended against — because that
is the entire purpose of the function it lives in — and QUOTED it into its
refusal message. asTheResolverSeesIt now reports the length and refuses to carry
the bytes.

WHETHER ECHOING BYTES INTO A DIAGNOSTIC IS A DEFECT DEPENDS ON WHO CHOSE THE
BYTES, AND A CHECK THAT ASKS "DOES THE MESSAGE CONTAIN THE INPUT" CANNOT TELL.
Applied to all seven, a redaction rule strips the identifier out of every useful
error in the kit. Applied to none, it misses the one that matters.

AND PROVENANCE IS NOT CARRIAGE, established by being wrong. The first register
had ONE field for both, and the spec caught it: after the fix landed it went on
reporting a leak, because the site still RECEIVES an attacker-chosen string —
permanently, that is what the function is for — and no longer QUOTES it.
bytesChosenBy is a fact about the data flow; quotesThem is a fact about the
message. One field has to either forget where the bytes came from or keep
reporting a leak that has been closed.

PERSISTENCE HAS THREE VALUES, NOT A BOOLEAN — removed, left-behind,
intentionally-persistent — because a cleansUp flag marks the two tracked
evidence files this section was ASKED to produce as failures, and would call
deleting them a fix. Same shape as S11.10.f's third decidability and S11.10.c
returning both destination lists.

TRAPS — THE SAME ONE TWICE, FROM OPPOSITE SIDES. A text search for outbound
network access found a scan that SEARCHES for a fetch call and holds it as a
string, so the searcher looked like a caller. And the writer census flagged THIS
CELL'S OWN SCAN, because the pattern naming the writing functions appears as a
regex literal inside it. Fifth time this section has been caught by a register
that counts its own family. Both censuses are now decided by what a file
IMPORTS, which is a capability, rather than by what it mentions, which is not.

SHIPPED. libs/oshun/workbench-kit/src/fixture-expectations.ts — EXPECTATIONS (6)
with the SUBJECTS each binds to and why the others cannot violate it,
vacuousForFixtures, DIAGNOSTIC_ECHOES (7) with bytesChosenBy and quotesThem,
hostileInputSites and leakingEchoes, RUNNER_WRITES (4) over three persistence
values, 5 fault kinds, census and headline.

VERIFIED. 22 tests in src/fixture-expectations.spec.ts; kit suite 244 files /
15,435 tests green. tools/fixture-expectations-scan.mjs recomputes the
capability census by reading every src module and every scan tool, re-derives
the echo list BY CALLING each diagnostic with a marked string, and re-measures
cleanup by running game-day-scan and counting the temp directory before and
after; a positive control confirms the capability query matches where a
capability IS present. 8 negative controls, exits 2 if any goes undetected. Five
registrations done — kit package.json exports, tsconfig.base.json path, root
script verify:operations:v1-fixture-expectations, Nx target
fixture-expectations-scan, export-map register; generator run: 248 entry points,
0 violations.
-->
- [x] S11.11 Add provider fault-injection fixtures for timeout, throttle,
      malformed output, partial output, safety refusal, and cost overrun.
      Evidence: exact UI and API results.
<!--
S11.11 CLOSED — a through g. Seven cells against a provider surface that was
already substantial, and one shape underneath all of them.

THE SHAPE: THE ESTATE DECIDES WELL AND RECORDS THE DECISION IN A FIELD NOTHING
FILLS IN, OR ON A KEY ONLY ONE SIDE CAN SUPPLY.

  .a  `unknown` is what the CALLER writes down when it stops waiting, and
      ProviderScriptStep lets the PROVIDER declare it — putting the caller's
      ignorance in the provider's mouth. Nothing compares elapsed time to a
      deadline, so a scripted hour still returns ok.
  .b  the provider sent a Retry-After and RetryContext has eight fields, none of
      them it — so a caller burns all five attempts in 31.0s against a 60s
      window and providerRetryDecision answers with 999ms.
  .c  six validators exist and correctionAllowed takes a family array NOTHING
      PRODUCES; the join is missing and the order is the whole thing.
  .d  Usage is two required numbers, so a half-reported meter is rounded to zero
      and prices identically to a call that produced nothing; and safety
      defaults to `allowed` when the provider said nothing.
  .e  refusalFaults is keyed on faultClass, which only a PROVIDER can populate,
      so working around a LOCAL refusal is invisible — and humanInitiated, the
      one legitimate way past a refusal, is read once and SET BY NOTHING.
  .f  reserve compares a bare number to budgets that each carry a unit, and
      settle is a report that releases nothing, so an unknown outcome holds its
      room for ever.
  .g  and FAULT_TABLE answers what to do next in six fields, exactly one of
      which is something a caller is told.

WHAT THE ESTATE ALREADY HAD, because crediting it first was the discipline that
made every finding above precise. Full-jitter backoff with the randomness
supplied. A retry-share ceiling with the best note in the library on why
per-request bounds turn a partial outage into a full one. `costOf` throwing on a
missing price and `totalCost` throwing on a null usage. A 1,471-line structured
output validator with eighteen lexical faults and a semantic pass that refuses
javascript: links. And the sentence this section kept coming back to: "an
automatic re-ask with different wording after a safety refusal is not error
handling: it is the system looking for a phrasing that gets the answer that was
declined, run at machine speed with nobody in the sequence."

THE METHOD THAT KEPT WORKING: measure by CALLING, not by reading. Every cell's
central claim is re-established on every scan run by driving the estate's own
functions, so if somebody fixes one of these — widens Usage, types the estimate,
writes the appeal path — the scan fails and the register is rewritten rather
than going quietly stale.

A CORRECTION THE SECTION OWES: S11.11.c first reported SIX blocked corrections
and the number was an artefact of its own context naming a trust zone that does
not exist. The real number is ONE. The scan now checks the baseline first.

AND A DEFECT I SHIPPED AND CAUGHT FOUR TIMES: a fault kind whose condition is
the negation of the branch it guards. .a had one, .b had two, .d had three, .f
had one — seven in all, every one unsatisfiable by construction and every one
sitting in a list looking like coverage. They are now swept invariants with
positive controls. A FAULT KIND THAT NOTHING CAN PRODUCE IS THE SAME DEFECT AS A
GATE THAT HAS NEVER FAILED.

7 modules, 7 specs, 7 scans, 5 registrations each. Kit suite 251 files / 15,625
tests green; 255 entry points, 0 violations.
-->
  - [x] S11.11.a Inject connect/first-byte/idle/total/tool/stage timeout before
        acceptance, during stream, after provider success, and with unknown
        outcome. Evidence: timeout fixtures.
<!--
S11.11.a — THE RULE: `unknown` IS NOT SOMETHING A PROVIDER RETURNS. IT IS WHAT
THE CALLER WRITES DOWN WHEN IT STOPS WAITING.

The estate says this itself and says it well. OUTCOME_MEANS.unknown in
provider-adapter.ts: "the connection ended after the request was accepted and
before the answer arrived; the work MAY have happened, been billed and been
recorded upstream, and this side cannot tell." That is a statement about what
THIS SIDE cannot tell — and ProviderScriptStep lets a fixture author write
`outcome: 'unknown'`, which puts the caller's ignorance in the provider's mouth.

MEASURED BY DRIVING createScriptedProvider, not by reading it:

  1. `delayMs` is advanced ONCE, BEFORE the chunk loop. Three chunks after a
     30s delay all arrive at monotonic 30000. Every scripted delay is
     structurally a FIRST-BYTE delay, and a gap BETWEEN chunks — the whole of an
     idle timeout — cannot be expressed.
  2. Nothing anywhere compares elapsed time to a deadline. A step with
     `delayMs: 3_600_000` and `outcome: 'ok'` returns ok, with its text and its
     usage, after a scripted hour. So "the provider succeeded and the caller had
     already given up" — one of this cell's four moments — has no way to occur.
  3. A scripted `unknown` returns `usage: null`. CORRECT for the caller's
     record, and it is the whole problem: the PROVIDER's ledger, which does know
     what it billed, has no representation. The fixture cannot say "5,000 output
     tokens were billed that this side will never see" — the fact that makes an
     unknown outcome expensive rather than merely annoying.

SHIPPED. libs/oshun/workbench-kit/src/timeout-fixtures.ts — ProviderLedger (what
the provider did, with per-chunk arrival times and what it will bill regardless
of what reached the caller) and CallerRecord (EVERY FIELD DERIVED by observe()
from those arrivals against the caller's own deadlines). Nothing declares the
caller's outcome, because the moment a fixture can, the cases where the two
ledgers disagree stop being distinguishable from the cases where they agree.

The six points are six reductions of ONE timeline. connect and first-byte both
reduce to "time before the first chunk", so a scalar delay expresses exactly
those two AND CONFUSES THEM — and only one of them permits a free retry. 8
fixtures, 6/6 points, 4/4 moments, 18,980 billed output tokens no caller
recorded.

FOUR AGREEMENT CLASSES, GRADED OVER THE CORPUS RATHER THAN PER FIXTURE — and
this axis exists BECAUSE THE FIRST VERSION WAS WRONG. It graded every fixture on
whether the ledgers DISAGREE and reported four defects against four CORRECT
fixtures: a caller that stops before acceptance knows the work did not start,
and a provider whose own connection dropped does not know either, so both sides
writing the same thing is right in both cases. Disagreement is not a quality bar
a fixture must clear; it is a third state. The fallback then swallowed a clean
success and filed it as shared ignorance, which is why agreed-it-succeeded is a
fourth class and is the POSITIVE CONTROL: without one in the corpus, an observer
that answered `unknown` to everything would look correct.

AND A FAULT KIND THAT COULD NEVER FIRE. "The caller wrote `failed` after
acceptance" had as its condition the exact negation of the one branch in
observe() that writes `failed`, so it was unsatisfiable by construction and
would have sat in the fault list looking like coverage forever. It is now an
invariant about the OBSERVER, swept over 375 ledgers in both the spec and the
scan — where the sweep IMMEDIATELY REFUTED IT AS FIRST STATED, because a
provider that ends a call with an explicit failure inside every deadline HAS
told the caller the work did not happen, which is the one case the caller can
see. The invariant is about TIMEOUTS, and it now says so.

VERIFIED. 29 tests in src/timeout-fixtures.spec.ts; kit suite 245 files / 15,465
tests green. tools/timeout-fixtures-scan.mjs RE-DRIVES createScriptedProvider on
every run for all three limits — so if somebody improves the provider double,
which would be good news, this scan fails and the register is rewritten rather
than going quietly stale — recomputes both corpus totals rather than reading the
census, and re-sweeps the observer with a positive control (90 of 375 ledgers
finish inside every bound and keep the provider's outcome, so an observer that
answered unknown to everything would fail). 8 negative controls, exits 2 if any
goes undetected. Five registrations done; generator run: 249 entry points, 0
violations.
-->
  - [x] S11.11.b Inject throttle/capacity with retry-after, quota reset,
        transient/terminal variants, repeated exhaustion, and circuit
        transition. Evidence: throttle fixtures.
<!--
S11.11.b — THE RULE: A BACKOFF COMPUTED FROM THE ATTEMPT NUMBER IS A GUESS, AND
THE PROVIDER SENT THE ANSWER.

CREDIT FIRST. provider-lifecycle.ts has a genuinely good retry surface:
full-jitter backoff with the randomness supplied as a parameter so the schedule
stays deterministic, a per-window `retryShareCeiling` with an excellent note on
why per-request bounds are what turn a partial outage into a full one, ten
refusals ordered by certainty, and `throttle` and `quota` as distinct classes.

MEASURED BY CALLING IT. `RetryContext` has EIGHT fields — faultClass, attempt,
requestAgeMs, cancelled, idempotencyKey, windowRetries, windowCalls,
reconciliation — and NOT ONE IS WHAT THE PROVIDER SAID. `backoffDelayMs(attempt,
budget, jitter)` takes three arguments and none is a Retry-After. So a 429 that
says "come back in 60 seconds" and a 429 with no header at all get the SAME
delay, because the header has nowhere to go.

  five-attempt budget, 1s base, 30s cap, maximum jitter:
      999 + 1998 + 3996 + 7992 + 15984  =  31.0s across ALL FIVE attempts

Against a `Retry-After: 60`, every one is refused again before the window opens.
The budget is spent, the load is added at exactly the moment the provider is
shedding it, and the caller NEVER ONCE WAITED LONG ENOUGH.
`providerRetryDecision` answers that sixty-second instruction with 999
MILLISECONDS.

THE SECOND, AND IT IS WHY ONE `retryAfterMs` FIELD WOULD NOT BE ENOUGH:
A RETRY-AFTER IS A DURATION AND A QUOTA RESET IS AN INSTANT, AND JITTER MEANS
THE OPPOSITE THING TO EACH.

A duration is measured from each caller's own response, so a thousand callers
throttled across a second are already spread across a second when they return —
jitter helps and is not load-bearing. "Your quota resets at 00:00 UTC" is the
SAME MILLISECOND for every caller on the plan: they arrive together, and the
first thing the provider does after restoring capacity is receive everything it
refused while it was down. There jitter is the whole mechanism, and it must land
AFTER the instant, because ARRIVING EARLY AT A RESET IS NOT EARLY — it is a
refused request added to the moment the provider is least able to absorb one.
The SAME HEADER in its HTTP-date form behaves like the reset rather than like
its own duration form, which is why the signal KIND and not the header name
decides whether spread is needed.

AND A CIRCUIT IS NOT A BUDGET. `circuit` appears nowhere in
provider-lifecycle.ts, and `retryShareCeiling` is not one: a ceiling bounds how
much of a window may be retries; a circuit stops asking altogether and then asks
ONE question. That half-open probe EXPECTS to fail, so charging it to a caller's
attempt budget spends a real user's retry on a health check — and a failed probe
restarts the open window from NOW rather than from the original opening, because
otherwise each window is shorter than the last and a failing provider is probed
ever faster.

SHIPPED. libs/oshun/workbench-kit/src/throttle-fixtures.ts — SIGNAL_KINDS (5)
with a `perCaller` shape each, ThrottleSignal carrying a duration OR an instant,
waitFor() honouring what was said and spreading only shared instants,
waitAsTheEstateDoes() for the comparison, attemptsWastedByGuessing(),
CIRCUIT_STATES with circuitStep(), 8 fixtures over 6/6 aspects, 5 fault kinds,
census and headline. 21 attempts wasted by guessing across the corpus.

TWO FAULT KINDS THAT COULD NEVER FIRE — "a per-caller duration got spread" and
"spread landed before the instant" — were the negations of what waitFor does,
the same defect S11.11.a found and the same fix: they are now invariants about
the FUNCTION, swept over 312 waits at every jitter including out-of-range ones,
with a positive control confirming spread IS applied somewhere (a waitFor that
never spread anything would satisfy the second one trivially).

VERIFIED. 28 tests in src/throttle-fixtures.spec.ts; kit suite 246 files /
15,494 tests green. tools/throttle-fixtures-scan.mjs re-reads
provider-lifecycle.ts for RetryContext's and RetryBudget's fields (with a
positive control that the field queries find the fields that ARE there),
re-calls backoffDelayMs and providerRetryDecision, recomputes the wasted-attempt
total rather than reading the census, re-sweeps both invariants and walks the
circuit through all three states. 8 negative controls, exits 2 if any goes
undetected. Five registrations done; generator run: 250 entry points, 0
violations.
-->
  - [x] S11.11.c Inject malformed syntax, wrong schema/version/type/unit/ref,
        duplicate/extra fields, unsafe URLs/tool calls, and semantic invariant
        failure. Evidence: malformed fixtures.
<!--
S11.11.c — THE RULE: THE SIX FAULT FAMILIES ARE A SEQUENCE, NOT A SET, AND
`correctionAllowed` TAKES A SET.

CREDIT FIRST, AND IT IS MOST OF THE CELL. structured-output.ts is 1,471 lines
and covers nearly everything named here: `scanJsonSource` (18 lexical faults,
including duplicate-key, which I expected it to miss); `envelopeFaults` for
schema, version, discriminator, required and extra fields; `resultSemanticFaults`
for units, citations, entity scope, cross-field invariants, links to undeclared
hosts and links whose scheme is javascript:/data:/vbscript:/file:;
`preParseFaults` for truncation and content-filter refusal; and a repair layer
with MEANING_CHANGING_REPAIRS and a provenance check that will not let a
repaired answer pass as an original.

`CORRECTABLE_FAMILIES` is `['lexical','envelope']`, with a rationale worth
quoting: "a model told 'that citation did not resolve' returns one that does, and
the second answer is worse than the first because it now looks correct."

WHAT IS MISSING IS THE JOIN. MEASURED: outside index.ts — the barrel, not a
caller — `scanJsonSource`, `preParseFaults` and `resultSemanticFaults` have ZERO
callers in this repository. `correctionAllowed(families)` takes an array of
families and NOTHING ANYWHERE PRODUCES ONE.

AND THE ORDER IS THE WHOLE THING. A document that did not parse has no envelope
to check; a wrong envelope has no coherent result for the semantic pass. A
classifier that unions all six reports facts DERIVED FROM A DOCUMENT THAT WAS
NEVER VALID — and hands them to the function that decides whether to retry.

    correctionAllowed(['lexical'])             -> allowed: TRUE
    correctionAllowed(['lexical','semantic'])  -> allowed: FALSE, blockedBy: [semantic]

TWO COSTS, AND ONLY ONE IS A CHANGED VERDICT.

  VERDICT (1 case). A trailing comma AND a bad citation: ordered stops at
  `lexical` and permits a retry; the union salvages the comma, reads the
  citation out of the salvage, adds `semantic`, refuses. Whether that citation
  survives a retry is exactly what nobody can know until the document parses.

  REASON (3 cases). A truncated body is ONE fault ordered and THREE unioned,
  two of them read from a fragment. The verdict agrees and the record does not,
  and the record is what somebody reads at three in the morning.

A CORRECTION I OWE THE READER. The first version of this cell reported SIX
blocked corrections. That was an artefact of its own ResultSemanticContext
setting `readmittedAs: 'model-generated'` — NOT a TrustZoneKind; the real one is
`prior-generated-output`. Every case was firing
`output-readmitted-above-a-generated-zone`, and the "blocking" was my broken
baseline agreeing with itself. With the zone corrected the count is ONE, and it
took a deliberately compound case to get that one. The effect is real and much
smaller than I first reported. The scan now checks the baseline FIRST.

A LIVE FINDING. `EnvelopeContract` lists requiredFields and allowedFields and NO
TYPES, so `envelopeFaults({kind:'finding',schemaVersion:2,title:42}, CONTRACT)`
returns an EMPTY LIST — verified by calling it, with a positive control that the
same checker does refuse a wrong discriminator. A required field present with
the wrong type passes every check in the estate and a renderer turns it into the
string "42".

TOOL CALLS ARE THE ONE GENUINE GAP. The word `tool` appears once in
structured-output.ts. A tool call is not a field in a result — it is a REQUEST,
and its arguments are the model's text arriving at something that will act on
them. `toolCallFaults` refuses a tool that was not offered, arguments that do
not parse, a path escaping the workspace, the same non-location scheme set the
estate refuses in a result link, and the identical call twice in one turn.

SHIPPED. libs/oshun/workbench-kit/src/malformed-fixtures.ts — CLASSIFICATION_ORDER
with ORDER_REASONS saying what each rung needs the previous one to have
established, classify() stopping at the first family that fails and reporting
BOTH what it found and what it never reached, classifyEverything() for the
comparison, toolCallFaults(), 15 cases held as TEXT (S11.9.b's lesson) over
11/11 kinds, 5 fault kinds, census and headline.

TRAPS, BOTH KNOWN TO THIS SECTION. The scan's family-array query found ITSELF —
the SIXTH instance of a register counting its own family — fixed by assembling
the pattern at run time rather than by an exclusion, because an exclusion can
also hide a real hit. And A SPACE TYPED INSIDE A TEMPLATE LITERAL LANDED IN THE
MODULE AS A RAW NUL BYTE, which makes the file binary to grep so every scan over
it reports zero hits for the same reason a clean file does. This cell's own NUL
check caught it; the separator is now written as an escape.

VERIFIED. 30 tests in src/malformed-fixtures.spec.ts; kit suite 247 files /
15,525 tests green. tools/malformed-fixtures-scan.mjs re-greps for callers of
each checker on every run (with a positive control that the query finds callers
where they DO exist), re-derives both union costs per case rather than reading
the census, and checks the baseline before anything else. 8 negative controls,
exits 2 if any goes undetected. Five registrations done; generator run: 251
entry points, 0 violations.
-->
  - [x] S11.11.d Inject partial stream/output/usage/safety metadata, truncated
        finish, connection loss, cancellation, and inconsistent provider status.
        Evidence: partial fixtures.
<!--
S11.11.d — THE RULE: A PARTIAL FACT MUST BE ROUNDED TO A WHOLE ONE IN EITHER
DIRECTION, AND BOTH DIRECTIONS ARE WRONG.

CREDIT FIRST. The estate is careful about WHOLLY absent facts: `costOf` throws
on a missing price ("a cost is a measurement times a price and a missing price
is a refusal, not a zero") and `totalCost` throws on `usage: null` ("a partial
sum wearing a total's clothes"). Both right.

MEASURED BY CALLING IT. `Usage` is `{inputTokens: number; outputTokens: number}`
— two REQUIRED numbers, no nulls. A provider that reports its input count and
dies before its output count, which is what a cut stream looks like, has exactly
two encodings and both destroy information:

    outputTokens: 0   -> costOf(...) = 0.003, and a call that GENUINELY produced
                         no output ALSO costs 0.003. Understated, and reported
                         as measured.
    usage: null       -> totalCost throws, correctly, and the input count the
                         provider DID send is discarded with it. A partial
                         measurement becomes no measurement.

The honest answer is neither: AT LEAST 0.003, UPPER BOUND UNKNOWN. `costRange`
returns that — a floor, a ceiling that may be absent, and `measured` kept
separate from the bounds so a dashboard can add up what it knows and say how
much of the total is a floor rather than a figure. It composes: a total over
ranges is a range.

THE SECOND: THE DEFAULT ASSERTS A VERDICT NOBODY GAVE. `createScriptedProvider`
writes `safety: step.safety ?? 'allowed'`, so a provider that returned no safety
verdict is recorded as having ALLOWED the content, and `SAFETY_VERDICTS` has no
value for "it did not say". `readSafety` adds `not-reported`; `permitsUse`
refuses it.

THE THIRD: AN INCONSISTENT STATUS IS NOT A BAD VALUE. No single field can be
inconsistent with itself — it is FOUR CHANNELS disagreeing (transport, body,
finish-reason, stream-terminator), and the only way to see it is to read all
four and KEEP the disagreement. Collapsing to one boolean is what makes it
invisible, and a 200 carrying an error body is the case every HTTP client
reports as a success.

A FOURTH CHANNEL VERDICT THIS CORPUS FOUND BY BEING WRONG. `finishReason:
'length'` was first classed as a channel saying the call FAILED, so every
truncated-but-successful response came back INCONSISTENT — while the fixture's
own prose already said it "dissents about COMPLETENESS rather than about
success". A call cut at the token limit ran, billed and terminated cleanly.
`says-it-was-cut-short` is that distinction; consistency is now a question about
SUCCESS and incompleteness is a second question, reported separately.

SHIPPED. libs/oshun/workbench-kit/src/partial-fixtures.ts — PartialUsage,
costRange, costAsTheEstateWouldRound for the comparison, SAFETY_READINGS with a
fourth value, readSafety/permitsUse, STATUS_CHANNELS with channelVerdict and
readStatus, 9 fixtures over 8/8 aspects, 3 fault kinds, census and headline. 5
disagree about success, 2 report an incomplete answer, 5 carry no safety
verdict, and 5 cannot be priced to a figure — the estate would report 0.0105 for
them AS MEASURED. The corpus holds a REAL zero as well as an absence so the two
are exercised apart.

THREE FAULT KINDS THAT COULD NEVER FIRE — each the negation of what costRange
does — became swept invariants over 25 usages with a positive control, the same
defect and fix as S11.11.a and .b. The third is load-bearing: the rounded cost
MUST equal the honest floor, because the finding is that the number a caller
gets today IS the floor presented as the figure.

VERIFIED. 26 tests in src/partial-fixtures.spec.ts; kit suite 248 files / 15,551
tests green. tools/partial-fixtures-scan.mjs re-reads Usage's field types from
the source, re-calls costOf and the scripted provider for both central claims,
recomputes every corpus total rather than reading the census, and re-sweeps the
three invariants. 8 negative controls, exits 2 if any goes undetected. Five
registrations done; generator run: 252 entry points, 0 violations.
-->
  - [x] S11.11.e Inject provider and local safety/policy refusal classes with
        nonretryable behavior, appeal/review, and no prompt-around fallback.
        Evidence: refusal fixtures.
<!--
S11.11.e — THE RULE: THE DETECTOR IS KEYED ON WHO REFUSED, AND WORKING AROUND A
REFUSAL IS THE SAME ACT WHOEVER MADE IT.

CREDIT FIRST, AND IT IS SUBSTANTIAL. provider-lifecycle.ts has the best piece of
writing in this library on the subject: "an automatic re-ask with different
wording after a safety refusal is not error handling: it is the system looking
for a phrasing that gets the answer that was declined, run at machine speed with
nobody in the sequence — and it is reached by ordinary-looking code that
regenerates on failure." The machinery under it is real: `AttemptRecord` carries
a `promptDigest` precisely so the comparison is possible, `refusalFaults`
reports four kinds, and `FAULT_TABLE` marks the three refusal classes
`retryability: 'no'` because a refusal is a decision, not a failure.

TWO MEASUREMENTS, AND THEY FIT TOGETHER BADLY.

FIRST — `PROVIDER_FAULT_CLASSES` is eleven values and every one is something a
PROVIDER does. A refusal made by THIS SYSTEM — an entitlement check, a withdrawn
consent, a rights holder's terms, a local policy engine — has no class, so the
attempt following it carries `faultClass: null` and `refusalFaults` SKIPS IT ON
ITS FIRST LINE. Verified by calling it with the identical attempt pair twice:

    provider safety refusal, then a reworded prompt
        -> ['prompt-changed-after-a-refusal']
    LOCAL refusal, then the same rewording
        -> []

The same act. The second is arguably worse, because it is the refusal this
system is answerable for and nobody outside ever sees the loop.

SECOND — `humanInitiated` is the ONE exemption the rule grants: "a person who
reads a refusal and edits their request is doing something the system should
support; the fault is the loop that does it without them." It is declared on
`AttemptRecord`, READ ONCE in `refusalFaults`, and SET BY NOTHING in this
repository — checked with a positive control that the same query finds writers
of a field that has one.

SO THE ONLY LEGITIMATE WAY PAST A REFUSAL HAS NO PRODUCER, AND THE FORBIDDEN WAY
IS UNDETECTABLE WHENEVER THE REFUSAL WAS LOCAL. What is reachable is exactly the
thing the rule was written to stop.

SHIPPED. libs/oshun/workbench-kit/src/refusal-fixtures.ts — REFUSAL_ORIGINS,
LOCAL_REFUSAL_CLASSES kept deliberately OUT of ProviderFaultClass (adding them
would say a provider can refuse on our entitlement rules), an Attempt extending
the estate's record with a `refusedBy` either side can populate, an origin-blind
`worksAround` that keeps the humanInitiated exemption exactly as written,
`asTheEstateSees` for the comparison, APPEAL_ROUTES with per-route producers, 7
fixtures, 5 fault kinds, census and headline.

The routes are honest about their limits: a provider's safety refusal is not
ours to overturn, so the only route against it is an out-of-band support ticket
measured in days — which is precisely why an automated loop is so tempting and
so wrong. And a withdrawn consent is the one refusal in the corpus with NO route
at all, because there is nobody here who may overturn it.

THE NUMBERS. The origin-blind detector catches 4 and the estate's catches 1, so
THREE WORKAROUNDS ARE INVISIBLE TODAY, and 3 of 3 appeal routes have no
producer. Those three faults are reported on every run and they ARE the finding
rather than a defect in the corpus — the spec asserts exactly three, all of them
the appeal gap. An ordinary retry after a timeout is the positive control,
without which a detector that reported everything would look correct on all six
other cases.

VERIFIED. 20 tests in src/refusal-fixtures.spec.ts; kit suite 249 files / 15,573
tests green. tools/refusal-fixtures-scan.mjs re-calls refusalFaults with both
attempt pairs on every run and re-greps for a writer of the exemption with a
positive control — so if somebody adds a local refusal class or writes the
appeal path, either of which would be good news, this scan fails and the
register is rewritten. 8 negative controls, exits 2 if any goes undetected. Five
registrations done; generator run: 253 entry points, 0 violations.
-->
  - [x] S11.11.f Inject estimate-underrun, reservation expiry, per-attempt
        overrun, missing metering, currency/rate mismatch, budget exhaustion,
        and kill switch. Evidence: cost fixtures.
<!--
S11.11.f — TWO RULES, AND THEY ARE INDEPENDENT.

THE FIRST: A BUDGET CARRIES ITS UNIT AND THE ESTIMATE COMPARED AGAINST IT DOES
NOT.

`SpendBudget` has a `unit: string`. `reserve(budgets, estimate, ...)` takes ONE
BARE NUMBER and compares it to every scope's remaining room. MEASURED, with a
tenant budget in US cents and a provider budget in vendor credits:

    reserve([tenant 10,000 usd-cents, provider 50 vendor-credits], 40)
        -> RESERVED. Forty US cents now charged against a credit budget.
    reserve([the same two], 100)
        -> REFUSED, "the provider budget has 50 vendor-credits left" —
           while the 10,000-cent tenant budget sits untouched.

Both answers are wrong, in opposite directions. And THE REFUSAL MESSAGE PRINTS
THE UNIT: the code knows units exist at the exact moment it declines to compare
them.

THE SECOND: A RESERVATION IS A PROMISE TO SPEND, AND EVERY PATH THAT ENDS
WITHOUT A SETTLEMENT HOLDS IT FOR EVER.

`settle(reservationId, estimated, actual)` takes three arguments — no budget, no
time — and returns a REPORT. It is a good report and its `overran` comment is
right. But NOTHING IN limits.ts ASSIGNS TO A BUDGET'S `reserved` FIELD (0
assignments, measured), and a reservation has no expiry: the `exception` has an
`expiresAt` and the reservation does not.

So S11.11.a's case arrives here. A call whose outcome is `unknown` has no
`actual` and never will, so `settle` can never be called, and the room is held
for ever — the budget shrinks by one estimate every time a provider call ends
ambiguously, which is exactly the failure mode a retry budget exists to survive.

SHIPPED. libs/oshun/workbench-kit/src/cost-fixtures.ts — Money (an amount WITH
its unit), reserveInUnits as a PRECONDITION rather than a replacement (once the
units agree it calls the estate's own reserve), `incomparable` as a fourth
outcome because "refused" says waiting will help and "incomparable" says nobody
can tell, a Reservation with `holdsUntil`, and resolveReservation with four
endings — settled / released / expired / still-held, the last REPORTED rather
than treated as an ending because an unresolved reservation is the state this
module exists to make visible. A settlement whose actual is in a different unit
does not settle either. A kill switch is kept distinct from an empty budget,
because zeroing a ceiling tells somebody their budget ran out when it is full.
10 fixtures, 7/7 aspects, 4/4 endings, 4 fault kinds.

ONE FAULT KIND WAS REMOVED for being unreachable AND malformed — it asked
whether an EMPTY ledger reports room, a question with one answer — and became
three swept ledger invariants. The third is this cell's own opening defect
arriving at the other end of the module: a held-room total across two units
names NO unit rather than adding cents to credits.

VERIFIED. 25 tests in src/cost-fixtures.spec.ts; kit suite 250 files / 15,599
tests green. tools/cost-fixtures-scan.mjs re-reads reserve's and settle's
SIGNATURES from limits.ts, re-runs both mixed-unit reservations, recounts the
assignments to `reserved`, and re-sweeps the ledger invariants — where the third
one caught a bug in the scan itself: the reservation it built to make a
two-unit ledger expired immediately, so heldRoom filtered it out and the ledger
looked single-unit. 8 negative controls, exits 2 if any goes undetected. Five
registrations done; generator run: 254 entry points, 0 violations.
-->
  - [x] S11.11.g Assert exact typed API
        status/code/state/provenance/correlation/ retry metadata and exact UI
        utility state/actions/no fabricated result. Evidence: API/UI snapshot
        matrix.
<!--
S11.11.g — THE RULE: THE TABLE ANSWERS WHAT TO DO NEXT AND THE MATRIX ASKS WHAT
TO SAY.

MEASURED. `FAULT_TABLE` classifies all eleven provider fault classes and its
entries carry exactly SIX fields:

    faultClass  retryability  family  outcomeUnknown  mayHaveBeenCharged  isRefusal

Every one is a decision THIS SYSTEM makes — retry or not, is the outcome
ambiguous, might we have been billed, is this a decision rather than a failure.
NOT ONE IS SOMETHING A CALLER IS TOLD.

Of this cell's six API fields, exactly ONE is a lookup: retry metadata, through
`retryability`, which is a `Retryability` imported from errors.ts so the two
vocabularies already agree. The other five are DECIDED here. `RpcStatus` lives
in errors.ts and the table names an `ErrorFamily` instead — `provider` covers a
throttle, a timeout and a safety refusal, which are resource-exhausted,
deadline-exceeded and failed-precondition. There is no error code. And there
cannot be a correlation id: it belongs to the REQUEST, not the fault, so it can
never come from a table keyed by fault class — which is exactly why it must be
carried explicitly and exactly what gets dropped.

None of that is a defect in the table. A table that decided what to render would
be the wrong table. It is a statement about where the matrix comes from, and
this cell is where those decisions are written down in the open rather than by
whichever component renders first.

THE ONE RULE THAT IS NOT A CHOICE: NO FABRICATED RESULT. `outcomeUnknown` makes
it load-bearing — a call whose outcome is unknown MAY have produced a result and
MAY have been billed, so a screen that renders one is showing something nobody
computed. Every row carries what the UI shows AND whether that presentation can
contain a result. A partial result is permitted exactly when it is MARKED
partial. No refusal row offers a retry, because a retry button on a refusal is
S11.11.e's prompt-around loop with a person clicking it. And a screen that knows
an instant does not show a spinner.

AND THE STATUS CANNOT DECIDE THE SCREEN, which is why this is a matrix and not a
lookup table:

    internal            -> retrying | failed | result        (3 screens)
    resource-exhausted  -> waiting-until | blocked-needs-a-person

A throttle is waited out in seconds; a budget is not waited out at all, somebody
raises it. malformed/lexical and malformed/semantic share a fault class AND a
status and differ in code and screen, because a model told its citation did not
resolve returns one that does. A local refusal takes permission-denied where a
provider's takes failed-precondition, and its only action is a reviewer.

SHIPPED. libs/oshun/workbench-kit/src/api-ui-matrix.ts — API_FIELDS with
FIELD_SOURCES recording per field whether it is a lookup or a decision, UI_STATES
with STATES_THAT_SHOW_A_RESULT, UI_ACTIONS, 12 rows over all 6 families
S11.11.a–.f injected, 7 fault kinds, census and headline. Every status,
retryability, state, action and fault class is taken from the estate's own
vocabularies and checked against them on every scan run.

VERIFIED. 25 tests in src/api-ui-matrix.spec.ts; kit suite 251 files / 15,625
tests green. tools/api-ui-matrix-scan.mjs reads FAULT_TABLE's ENTRY SHAPE on
every run and recomputes which of the six fields it can answer — so if somebody
adds a status or a code to the table, which would be good news, the "one of six"
becomes wrong out loud rather than quietly. 8 negative controls, exits 2 if any
goes undetected. Five registrations done; generator run: 255 entry points, 0
violations.
-->
- [x] S11.12 Add queue fault-injection for lease loss, duplicate delivery,
      out-of-order events, retry exhaustion, and cancellation race. Evidence: no
      duplicated side effects.
<!--
S11.12 CLOSED — a through g, against the best-argued code in this library.

THE SHAPE: EVERY FINDING HERE IS ABOUT WHAT A CORRECT DESIGN CANNOT EXPRESS, NOT
ABOUT IT BEING WRONG. jobs.ts, idempotency.ts, inbox.ts and retries.ts are
excellent, and this section quotes them at length because the quotations ARE the
argument. Seven times the answer was: the mechanism is right, the vocabulary it
decides over is one thing short.

  .a  CHECKPOINT_ORDERINGS deliberately omits `checkpoint-first`, so the silent
      failure the module names CANNOT BE WRITTEN DOWN by a fixture. A type that
      makes a bad state unrepresentable makes it uninjectable.
  .b  decideClaim returns `claimed` to BOTH racers, correctly — and the estate
      names the layer that decides that race for revisions and not for
      idempotency keys, while scopeKey already produces the six-tuple.
  .c  OrderingRequest's seven fields are all one subject's one sequence, so a
      cross-subject dependent event is APPLIED. Order is a fact about a
      sequence; a dependency is a fact about two.
  .d  classifyJobFailure takes two arguments and cannot count, so "transient
      repeats" and "permanent" are the same answer — and defer protects the
      queue from its jobs while nothing protects a dependency from the queue.
  .e  JobRecord has one coarse `state`, so all four positions inside `running`
      get one answer and cost four different things. A cancel after the effect
      loses the RECORD, not the effect.
  .f  four of ten invariants are evaluable from a corpus of faults and six are
      not, and saying which is which is the whole value of the suite.
  .g  and a replay is authorized by a capability and made safe by a
      reconciliation, and only the first is checked — narrowed, because
      planDeadLetterReplay preserves the idempotency key and catches every
      replay whose first attempt left a record.

WHAT THE ESTATE ALREADY HAD, quoted because crediting it first is what made each
finding precise: "expiry protects the QUEUE; only a monotonically increasing
token, checked at the WRITE, protects the DATA" · "a stored record in completed
with a different request digest is not a replay" · "a gap this new is almost
always an event still in flight" · "a deferred job must not burn an attempt" ·
"collapsing the two tells the operator the effects have stopped at the exact
moment they have not" · "this is the cell people fill in with retry because the
alternative needs a person, and it is the cell that produces the duplicate
charge".

MY OWN DEFECT, ELEVEN TIMES, AND NOW A RULE. A fault kind whose condition no
input can reach — because it checks a module's own constants rather than the
input. Seven in S11.11, four more here. All are swept invariants now, and the
rule is stated where the eleventh was found: A FAULT KIND MUST RANGE OVER THE
INPUT; A STATEMENT ABOUT A MODULE'S OWN CONSTANTS IS AN INVARIANT.

AND A FAMILY OF QUERY BUGS, five in this section, every one producing a wrong
answer for a structural reason rather than a factual one: a JSON string's
character count compared to a column count; a wrapped block comment whose
sentence no flat regex matches; a guessed function name whose empty match passed
a negative check; a non-greedy brace match that stopped at an inline parameter
type; and a slice that ran into the next section's banner. A ZERO FROM A QUERY
NOBODY POSITIVE-CONTROLLED IS NOT A MEASUREMENT, AND NEITHER IS A ONE.

7 modules, 7 specs, 7 scans, 5 registrations each. Kit suite 258 files / 15,789
tests green; 262 entry points, 0 violations.
-->
  - [x] S11.12.a Inject lease expiry before/during/after checkpoint or external
        effect, failed heartbeat, fencing violation, worker pause/death, and new
        worker takeover. Evidence: lease fixtures.
<!--
S11.12.a — THE RULE: A TYPE THAT MAKES A BAD STATE UNREPRESENTABLE ALSO MAKES IT
UNINJECTABLE, AND FAULT INJECTION IS THE ONE PLACE THAT MATTERS.

CREDIT FIRST, AND IT IS THE WHOLE SETUP. jobs.ts is 1,929 lines and the
best-argued module in this library. Decision one is this cell's subject: "A
LEASE IS NOT A LOCK, AND WHAT MAKES IT SAFE IS THE FENCING TOKEN. A worker whose
lease expired does not know its lease expired: it was paused — a collector, a
stalled syscall, a partition — the coordinator handed the job to somebody else,
and then it wakes up and writes. Expiry protects the QUEUE; only a monotonically
increasing token, checked at the WRITE, protects the DATA."

Decision three creates the problem: "effect-then-checkpoint REPEATS the effect
on resume, which idempotence answers; checkpoint-then-effect SKIPS it, and
nothing anywhere reports a stage that was never run. `CHECKPOINT_ORDERINGS` has
no `checkpoint-first` member."

MEASURED: exactly two members, and the dangerous third absent by design.

THE CONSEQUENCE. That is right for production and it means the silent failure
CANNOT BE WRITTEN DOWN. S11.12 asks for FIXTURES that inject a lease fault, and
a `CheckpointOrdering` cannot hold `checkpoint-first` — so no fixture can put a
system into the state whose whole danger is that nothing reports it.

A CORPUS THEREFORE NEEDS ITS OWN VOCABULARY, DELIBERATELY WIDER, AND HAS TO SAY
SO. `INJECTABLE_ORDERINGS` is production's plus the forbidden member;
`productionCannotExpress` marks every use; the two are never merged; and the
scan checks on every run that NOTHING outside this cell imports the wider list —
because the day it becomes an importable production type is the day the
constraint stops holding.

THE SECOND MEASUREMENT: `claimJob` has ZERO callers outside a spec. The lease
machinery is complete, argued and driven by nothing — the same shape S11.10.a
found for the archive checkers and S11.11.c for the output validators.

SHIPPED. libs/oshun/workbench-kit/src/lease-fixtures.ts — POSITION_COSTS, a
table over expiry position × ordering whose argument IS the table: the same
position costs a REPEAT under one ordering and a SKIP under the other, and only
the repeat is visible. `wouldBeAccepted` restates fencing so a fixture can
assert on it, refusing a token EQUAL to the highest seen as firmly as a lower
one, because two workers holding the same token is the state fencing exists to
prevent. 9 fixtures over 6/6 events, 4/4 positions, 4/4 loss kinds; 7 fault
kinds. ONE fixture uses the forbidden ordering and it is the SAME ONE whose loss
nothing reports.

THREE DELIBERATE CHOICES. The dead-worker fixture records in its own reason that
it proves NOTHING alone — a dead worker never writes, so a system with no
fencing passes it — which is why the paused-worker case sits beside it. The
clock-skew fixture puts the worker 45s behind so its lease looks live to it and
expired to the coordinator, and the verdict still turns on tokens rather than on
either clock. And the one ordering with NO bad position is in the corpus so the
others are not made to look inevitable.

VERIFIED. 22 tests in src/lease-fixtures.spec.ts; kit suite 252 files / 15,648
tests green. tools/lease-fixtures-scan.mjs re-reads CHECKPOINT_ORDERINGS on
every run (so adding checkpoint-first fails loudly), greps for importers of the
wider vocabulary, and sweeps 25 token pairs with a positive control — a check
that refused everything would satisfy every corpus case. 8 negative controls,
exits 2 if any goes undetected. Five registrations done; generator run: 256
entry points, 0 violations.
-->
  - [x] S11.12.b Deliver same command/event/job concurrently, after ack loss,
        after success, after retry, and with altered payload under same ID.
        Evidence: duplicate fixtures.
<!--
S11.12.b — THE RULE: `decideClaim` RETURNS `claimed` TO BOTH RACERS, AND THE
ESTATE NAMES THE LAYER THAT DECIDES THAT RACE FOR REVISIONS AND NOT FOR
IDEMPOTENCY KEYS.

CREDIT FIRST, AND IT IS MOST OF THE CELL. `IdempotencyScope` is five fields plus
a target with a note on what dropping each one costs. And `decideClaim` COMPARES
THE DIGEST BEFORE THE STATE: "a stored record in `completed` with a different
request digest is not a replay — it is a caller reusing a key for something
else, and returning the stored result reports success for something that never
ran, to somebody who will never look again." So altered-payload-under-the-same-
ID, this cell's sharpest item, is covered — and inbox.ts answers it a second
time by comparing `payloadHash` before admitting an event.

WHAT IS NOT ANSWERED IS CONCURRENCY, AND THE MODULE IS RIGHT NOT TO ANSWER IT.

MEASURED by calling it: `decideClaim(null, digest, at)` returns `claimed`, so two
callers racing before either has written both see nothing and BOTH are told to
proceed. That is correct. A pure function over "whatever record already exists"
cannot decide a race, and pretending it could would be worse than not trying.

The race is decided by a uniqueness constraint at the storage layer — and the
estate says so IN THIS VERY MODULE, about a different constraint:

    unique-sequence-per-entity, layer: 'table'
    "UNIQUE (entity_id, sequence); this is the constraint that decides an
     append-only race, and a SELECT-then-INSERT does not"

`CONSTRAINT_PLACEMENT` holds EIGHT entries and every one is a REVISION
constraint. MEASURED: not one names the idempotency scope. And `scopeKey`
already produces the exact six-tuple such an index would be over — tenant,
client, operation, operation version, target, key — so the thing to place is
written down and its placement is not.

SHIPPED. libs/oshun/workbench-kit/src/duplicate-fixtures.ts — SCOPE_PLACEMENT,
stating that layer in the same shape CONSTRAINT_PLACEMENT uses, with the
estate's own sentence and none of the words changed; ARRIVAL_KINDS,
PAIR_OUTCOMES, and 6 delivery pairs over 5/5 kinds producing 5/5 outcomes, of
which exactly ONE — the concurrent one — leaves both callers claimed while every
other is settled by the decision function alone. 5 fault kinds.

A SEVENTH FAULT KIND THAT NO INPUT COULD REACH was removed and asserted directly
instead — it compared two of this module's own constants, which makes it a
consistency check rather than a finding. AND IT CAUGHT A REAL BUG ON THE WAY IN,
worth recording because it is the shape this section keeps finding elsewhere:
the first version compared `scopeKey(scope).length` to the column count, and
`scopeKey` returns a JSON STRING — so it was comparing twenty-four CHARACTERS to
six COLUMNS. Two quantities that are not the same KIND of thing, compared
because both are numbers.

VERIFIED. 21 tests in src/duplicate-fixtures.spec.ts; kit suite 253 files /
15,670 tests green. tools/duplicate-fixtures-scan.mjs re-calls decideClaim for
both racers AND for a caller who arrives after the record exists — the positive
control, since a function that always said `claimed` would satisfy the first
line — and re-reads CONSTRAINT_PLACEMENT on every run, so adding the scope
placement fails loudly. 8 negative controls, exits 2 if any goes undetected.
Five registrations done; generator run: 257 entry points, 0 violations.
-->
  - [x] S11.12.c Deliver sequence gaps, late predecessor, reordered independent/
        dependent events, stale version, replayed tombstone, and partition
        reorder. Evidence: ordering fixtures.
<!--
S11.12.c — THE RULE: ORDER IS A FACT ABOUT A SEQUENCE, AND A DEPENDENCY IS A
FACT ABOUT TWO.

CREDIT FIRST, AND IT IS FOUR OF THE SEVEN ITEMS. `inbox.ts`'s `decideOrdering`
has five dispositions and the note that carries the whole design: "A GAP IS NOT
A LOSS UNTIL IT IS OLD. Position 5 arriving with 4 unseen is almost always 4
still in flight, so 5 is held; treating the gap as a loss immediately turns
every reordering into an incident. Past the tolerance it stops being a wait and
becomes a fact somebody has to be told."

So a sequence gap is `hold-for-gap`; a late predecessor is the same thing named
from the other end; a reordered INDEPENDENT event is `apply-out-of-order` when
its kind declares `commutes` WITH A REASON (the estate requires one, because "an
unexplained true is what somebody set to stop the queue filling up"); and a
stale version is `ignore-superseded`.

MEASURED, AND THIS IS THE GAP. `OrderingRequest` has SEVEN fields —
appliedThrough, held, sequence, ordering, now, gapFirstSeenAt,
gapToleranceSeconds — and every one is about ONE SUBJECT'S ONE SEQUENCE. Called
with a dependent event that is position 1 on its own subject and whose
predecessor lives on a DIFFERENT subject:

    { disposition: 'apply', detail: 'the next position in the subject' }

AND IT IS RIGHT. On its own sequence it IS the next position, and there is no
field in the request that could mention the other subject, so the question was
never asked.

FOURTH TIME THIS SECTION HAS MET THIS SHAPE, so it is a shape. S11.9.b: a corpus
of JSON objects cannot hold a duplicate key. S11.9.c: a corpus of lone manifests
cannot hold a relationship fault. S11.10.c: a corpus of single paths cannot hold
a collision. And here: a request carrying one sequence cannot hold a
cross-sequence dependency.

SHIPPED. libs/oshun/workbench-kit/src/ordering-fixtures.ts — Dependency,
AppliedThroughBySubject (a MAP, because the point is that there is more than
one), and decideWithDependencies, which asks the ESTATE'S question first and the
cross-sequence one second. That order is deliberate: A DEPENDENCY DOES NOT
OVERRIDE A GAP. An event whose own predecessor is missing holds for that
regardless, and a superseded event stays superseded, so the dependency check
only ever turns an apply into a hold. Unmet dependencies are reported even when
the within-sequence answer already holds, because the two release at DIFFERENT
MOMENTS and a consumer knowing only the first would release too early. 8
fixtures, 7/7 hazards, 6/6 dispositions against the estate's 5, 6 fault kinds.

THREE ARE DECIDED DIFFERENTLY once a second sequence is in scope — the dependent
event, the replayed tombstone and the partition reorder — and ALL THREE get
`apply` from the estate. The tombstone is the sharpest: on its own stream it is
position 1 and perfectly in order, and what makes it wrong is the state of a
subject it names. A partition reorder is cross-sequence by construction, since
each partition is internally ordered and the relationship between them is what
no single sequence carries.

A fixture with a SATISFIED dependency is the positive control, without which a
decider that held everything carrying a dependency would look correct on every
other case. And every dependency must say why, because one that does not holds
the queue for ever and nobody can tell whether removing it is safe.

VERIFIED. 22 tests in src/ordering-fixtures.spec.ts; kit suite 254 files /
15,693 tests green. tools/ordering-fixtures-scan.mjs re-reads OrderingRequest's
FIELD LIST from a fixture rather than restating it (so widening the request
fails loudly), re-calls decideOrdering for the dependent case, and recomputes
`crossesSequences` per fixture by comparing the two decisions rather than
reading the flag. 8 negative controls, exits 2 if any goes undetected. Five
registrations done; generator run: 258 entry points, 0 violations.
-->
  - [x] S11.12.d Exhaust retry by transient repeats, permanent reclassification,
        max attempts/age/cost, poison payload, dead-letter/replay, and circuit
        open. Evidence: retry fixtures.
<!--
S11.12.d — TWO RULES, AND THEY ARE THE SAME MISSING INPUT SEEN TWICE: HISTORY.

THE FIRST: A CLASSIFICATION THAT DOES NOT COUNT CANNOT RECLASSIFY.

`classifyJobFailure(failure, delivery)` is a pure function of the failure KIND
and the effect delivery, and it is a good one — its unknown-outcome rule is the
best thing in the module: "with an idempotent effect an unknown outcome is just
a retry, and with an at-most-once effect it is a question for a person, because
retrying is how one payment becomes two and doing nothing is how it becomes
none."

MEASURED by calling it: `dependency-unavailable` classifies `after-backoff` on
attempt 1 and on attempt 50, and the function takes TWO arguments so there is no
third that could carry a count. THIS CELL'S FIRST TWO ITEMS — "transient
repeats" and "permanent reclassification" — ARE THE SAME ANSWER FROM IT, and a
failure that has now happened fifty times is still filed as the kind of thing
that goes away on its own.

That is not a defect. A classifier over one failure cannot count, and one that
guessed from a single failure would be guessing. What is missing is a caller
that keeps the count.

THE SECOND: `defer` PROTECTS THE QUEUE FROM ITS OWN JOBS, AND NOTHING PROTECTS A
DEPENDENCY FROM THE QUEUE.

`decideJobRetry`'s third answer is excellent: "A queue at capacity has not
refused the attempt — it has not offered one — so a deferred job must not burn an
attempt or reserve budget for one. Folding capacity into `retry` is how a busy
hour quietly consumes every job's attempt allowance and leaves a queue full of
jobs that are out of attempts and were never actually run."

MEASURED: `JobRetryContext` carries `queueRunning` and `queueConcurrency` — the
QUEUE'S OWN capacity — and nothing about the health of what the job calls. Five
hundred jobs failing on one broken dependency each back off on their own
schedule and each return, and that sentence applies to the dependency with NONE
OF THE WORDS CHANGED.

SHIPPED. libs/oshun/workbench-kit/src/retry-exhaustion-fixtures.ts —
FailureHistory; `reclassify`, which calls the classifier FIRST and keeps its
answer as `classified` because the point is the DIFFERENCE, hardens only
`after-backoff` and only in one direction, and files a repeated failure with the
SAME payload as `after-change` rather than `after-remediation` (nobody fixes the
dependency; somebody changes the input); and `decideAgainstDependency`, reusing
capability-state.ts's three circuit positions rather than inventing a parallel
set, where a defer NEVER burns an attempt and the half-open PROBE does — because
unlike a capacity defer it is a real request that really ran, and a probe that
cost nothing would let a job probe for ever. 11 fixtures, 8/8 hazards, 3/3
reclassifications, 5 fault kinds; 3 changed by history, 2 deferred for the
dependency, 4 past a ceiling. Attempts, age and cost are kept as three separate
ceilings because hitting each is a different sentence to write in a log.

The estate's best rule is kept intact and tested: an unknown outcome with an
at-most-once effect is `after-remediation` AND is NOT deferred by an open
circuit — because the question is not whether the dependency is well, it is
whether the effect happened.

TWO MORE FAULT KINDS THAT COULD NEVER FIRE became invariants swept over 192
combinations with positive controls (a decider that never deferred and a
reclassifier that never changed anything would satisfy both trivially). Eighth
and ninth instances in this section.

A TRAP: the scan's check that the estate's note exists found NOTHING on its
first run, because the note is a WRAPPED block comment and the sentence is split
by newlines and leading asterisks. It flattens the comment now — a zero from a
query nobody wrap-proofed is not a measurement.

VERIFIED. 25 tests in src/retry-exhaustion-fixtures.spec.ts; kit suite 255 files
/ 15,719 tests green. tools/retry-exhaustion-fixtures-scan.mjs re-calls
classifyJobFailure (with a positive control that it DOES discriminate between
kinds), re-reads JobRetryContext's field list from jobs.ts, and re-sweeps both
invariants. 8 negative controls, exits 2 if any goes undetected. Five
registrations done; generator run: 259 entry points, 0 violations.
-->
  - [x] S11.12.e Race cancellation before lease, during stage/checkpoint/
        external effect, after success/before commit, with retry/parent-child,
        and with compensation. Evidence: cancellation fixtures.
<!--
S11.12.e — THE RULE: A CANCEL AFTER THE EFFECT AND BEFORE THE COMMIT DOES NOT
STOP ANYTHING — IT LOSES THE RECORD OF SOMETHING THAT ALREADY HAPPENED.

CREDIT FIRST. `requestCancel` refuses a cancel with no reason ("indistinguishable
from a bug in the caller, and the job it killed cannot be told apart from one
that failed"), and keeps `cancelling` as a state with a real duration:
"collapsing the two — flipping a running job straight to `cancelled` — tells the
operator the effects have stopped at the exact moment they have not, and they
act on that." `regionFaults` carries the best fault in the module: a
noncancellable region longer than the lease "turns a safety mechanism into the
failure it was protecting against."

MEASURED: `JobRecord` has NINETEEN fields and exactly ONE is about how far the
worker has got — a job-level `state` — and `requestCancel` takes only the job and
the request. So every position this cell names lives INSIDE one state, and the
four that matter get ONE answer from the state machine and cost FOUR different
things:

    during a stage             — nothing has escaped
    during a checkpoint        — stopping leaves it half-written
    inside an external effect  — the request has left and cannot be recalled
    after success, before commit — THE EFFECT ALREADY HAPPENED

That last one is worth the rule. Cancelling there stops nothing — the work is
done, the money moved, the file was written — and what the cancel destroys is
the RECORD. The job then looks cancelled to everybody while its effect stands.
The reverse of every other cancellation hazard, and why `stop` cannot be the
answer everywhere.

SHIPPED. libs/oshun/workbench-kit/src/cancellation-fixtures.ts —
WORKER_POSITIONS and POSITION_RULES, mapping each to one of the estate's OWN
three cancellation-point actions rather than inventing new ones. A checkpoint
write is CONTINUED rather than stopped, because a half-written checkpoint is
read on resume and believed; the two positions where stopping is wrong get
`escalate-and-fence`, which is what stops a second worker writing a conflicting
record while somebody decides. `cancelAt` asks `requestCancel` FIRST — verified
at ALL SIX positions: a reasonless cancel is refused wherever the worker is, and
a terminal job is terminal whatever the worker thinks it is doing. 10 fixtures,
8/8 races, 6/6 positions, 3/3 actions, 6 fault kinds; 4 settled by state, 6 by
position, 3 compensations owed.

A parent source and an actor source get the same answer at the same position,
and a deadline cancel owes the same compensation as a person's — because what is
owed depends on the POSITION and not on who asked.

TWO COSTS CANNOT ARRIVE THROUGH A POSITIONED DECISION, because their positions
only occur with a job state the estate settles alone (queued and terminal). The
module records WHICH positions and WHY rather than carrying an exclusion list
with no reason attached — the next reader could not otherwise tell it from a
case somebody forgot.

AND A TENTH UNREACHABLE FAULT KIND was removed here, which is enough instances
to state the pattern as a rule: **A FAULT KIND MUST RANGE OVER THE INPUT. A
statement about a module's own constants is an INVARIANT, and it belongs in a
sweep where it can fail.**

VERIFIED. 23 tests in src/cancellation-fixtures.spec.ts; kit suite 256 files /
15,743 tests green. tools/cancellation-fixtures-scan.mjs re-reads JobRecord's
FIELD LIST on every run (so adding a stage or phase fails loudly) and re-calls
requestCancel at all six positions for both the reasonless and the terminal
case. 8 negative controls, exits 2 if any goes undetected. Five registrations
done; generator run: 260 entry points, 0 violations.
-->
  - [x] S11.12.f Assert idempotent authoritative and external side effects,
        monotonic state/progress, correct result/audit/cost, preserved
        checkpoint, and no false success. Evidence: invariant suite.
<!--
S11.12.f — THE RULE: AN INVARIANT SUITE OVER FIXTURES CHECKS THE DECISIONS, NOT
THE SYSTEM — AND SAYING WHICH IS WHICH IS THE WHOLE VALUE OF IT.

The cell asks for nine assertions. A tenth is added — `decision-agreement` —
because it is the structural property the whole of S11.12 rests on: every cell
added a second decision beside an estate one and every one was built to ask the
estate FIRST, and nothing until now checked that the ordering actually holds.

S11.12.a–.e built five corpora of FAULTS, and a fault fixture is a DESCRIPTION
of a situation rather than a RUN of a system. So FOUR of the ten are evaluable
here and SIX are not, and the difference is not a matter of effort: a fixture
saying "the lease expired after the effect and before the checkpoint" carries no
effect that could be applied twice, no audit row, and no money. Asserting
idempotence over it would be asserting something about a system nobody ran.

REPORTING THAT IS THE POINT, and it is why this reuses S11.9.e's four verdicts —
`held`, `violated`, `unobservable`, `vacuous`. An invariant that could not be
evaluated must not look like one that was and passed, and A SUITE OF TEN GREENS
OVER FOUR EVALUABLE PROPERTIES IS THE SHAPE OF A CHECK NOBODY HAS EVER SEEN
FAIL.

    OBSERVABLE                      examined
      monotonic-state                  5    every stale token refused, every
                                            terminal job answered as terminal
      preserved-checkpoint             9    every skipped effect silent, every
                                            repeat loud
      no-false-success                24    across three corpora
      decision-agreement              29    every added decision asked first
                                     ---
                                      67

    UNOBSERVABLE — each recorded with what it would need
      idempotent-authoritative-effect  an effect that was actually applied
      idempotent-external-effect       a flight that actually left
      monotonic-progress               a progress reading no corpus carries
      correct-result                   work that finished
      correct-audit                    a row somebody wrote
      correct-cost                     a measured cost — and S11.11.f showed
                                       that is a floor whenever a counter is
                                       missing

SHIPPED. libs/oshun/workbench-kit/src/queue-invariants.ts — QUEUE_INVARIANTS,
INVARIANT_NEEDS stating per invariant what a fixture would have to carry AND
whether any corpus carries it, runSuite() over the five corpora, 4 fault kinds,
census and headline.

AN ELEVENTH UNREACHABLE FAULT KIND was removed here under the rule S11.12.e
stated after the tenth: a fault kind must range over the input, and a statement
about a module's own constants is an invariant.

VERIFIED. 20 tests in src/queue-invariants.spec.ts; kit suite 257 files / 15,764
tests green. tools/queue-invariants-scan.mjs re-runs the suite and RECOMPUTES
every examined count FROM THE CORPORA rather than reading it off the results —
so a suite that quietly stopped looking at a corpus would fail — and checks that
no unobservable invariant reports examined cases and no invariant returns a
verdict without looking at anything. 8 negative controls, exits 2 if any goes
undetected. Five registrations done; generator run: 261 entry points, 0
violations.
-->
  - [x] S11.12.g Reconcile unknown outcomes and prove authorized replay does not
        duplicate effect. Evidence: recovery journey.
<!--
S11.12.g — THE RULE: A REPLAY IS AUTHORIZED BY A CAPABILITY AND MADE SAFE BY A
RECONCILIATION, AND ONLY THE FIRST IS CHECKED.

Both halves already exist in the estate, in different files, and nothing joins
them.

THE RECONCILIATION HALF IS EXCELLENT. `mayRetryAfterUnknown`'s table is the best
writing in the library on the subject, and its fourth row is the whole point:
"reconcile, and INDETERMINATE -> refuse. This is the cell people fill in with
retry because the alternative needs a person, and it is the cell that produces
the duplicate charge." And `ReconciliationProbe` is keyed by the IDEMPOTENCY KEY
rather than any local id, "because after a connection loss the local id may
never have been written — which is the state being reconciled".

THE AUTHORIZATION HALF IS GOOD TOO — every dead-letter action gated on a
capability, refusing not-authorized, already-resolved, inputs-purged,
inputs-moved and nothing-has-changed.

AND THE REPLAY ITSELF IS CAREFUL, WHICH NARROWS THIS FINDING AND IS WORTH
GETTING RIGHT. `planDeadLetterReplay` PRESERVES THE JOB IDENTITY AND THE
IDEMPOTENCY KEY: "minting a new job id forces one of two wrong moves: reuse the
key, and the enqueue is answered with the dead job's outcome; drop it, and two
runs of the same work exist with nothing relating them." So a replay whose first
attempt COMPLETED is caught — same key, existing record, `already-completed`.

WHAT REMAINS, MEASURED:
  · `planDeadLetterReplay` takes no probe and consults no reconciliation
  · `DeadLetter` carries 18 fields including `failureKind` — which can be
    `unknown-outcome` — and NONE that says whether the effect landed
  · `mayRetryAfterUnknown` is called ZERO times from jobs.ts, with a positive
    control confirming the function is really there to be called

THE GAP IS PRECISELY THE UNKNOWN OUTCOME, and it is the one place the key does
not help: an attempt that died between applying its effect and writing its
record leaves no record for the key to find — S11.12.b's finding arriving here,
and exactly the state the probe is keyed by the idempotency key to resolve. The
replay proceeds, the claim finds nothing, and the effect is applied a second
time.

SHIPPED. libs/oshun/workbench-kit/src/recovery-journey.ts — RECOVERY_STEPS
asking authorization, then record state, then reconciliation. THE ORDER IS THE
ARGUMENT: authorization first, because telling an operator who may not replay
that the effect already landed is itself a disclosure; record state second,
because a resolved dead letter has nothing to replay; reconciliation last,
because it is the only one that requires asking somebody else. 10 fixtures, 3/3
steps, 4/4 outcomes, 3/3 reconciliation results, 5 fault kinds; 3 refused by the
estate alone, 3 allowed by it and stopped here, 0 replays of an effect that
really committed. A failure whose outcome is KNOWN skips the reconciliation
entirely — the positive control, since a module that refused every replay
without a probe would be useless.

THREE TRAPS IN ONE SCAN, ALL THE SAME KIND. A guessed function name matched
nothing and the "mentions no probe" check PASSED AGAINST AN EMPTY STRING, for
the same reason a clean file does. A non-greedy match to the first newline-brace
stopped at the closing brace of the INLINE PARAMETER TYPE and read 239
characters of signature, concluding the body never mentions the idempotency key.
And a slice to the next export ran INTO the following section's banner comment,
whose word "reconciling" reversed the answer again. A BOUNDARY THAT IS TOO LATE
IS AS WRONG AS ONE THAT IS TOO EARLY; IT JUST FAILS IN THE OTHER DIRECTION.

VERIFIED. 24 tests in src/recovery-journey.spec.ts; kit suite 258 files / 15,789
tests green. tools/recovery-journey-scan.mjs re-slices planDeadLetterReplay's
body between two anchors on every run, recounts the calls to
mayRetryAfterUnknown, and credits the idempotency-key preservation explicitly —
so if somebody adds the probe, which would be good news, this fails. 8 negative
controls, exits 2 if any goes undetected. Five registrations done; generator
run: 262 entry points, 0 violations.
-->
- [x] S11.13 Add golden bundle build/import/verify runners. Evidence: each
      domain profile registers a golden.
      <!-- a-g all closed. The evidence clause is met on the registry that
      exists: WORKED_BUNDLE_PROFILES has two members, bundle.story-package and
      bundle.dataset-release, and both register a golden --
      profilesWithoutARecordedGolden() returns empty and the scan re-checks it
      every run. There is NO broader "domain profile" registry in the kit: the
      58 entries in domains.json are Nx project domains and none of them carries
      a bundle profile, so "each domain profile" resolves to those two. The
      mechanism that keeps this true as the registry grows is the estate's own
      goldenCoverageFaults, which walks the SHIPPED registry rather than the
      coverage, so a profile added tomorrow with no golden is reported rather
      than invisible -- and S11.13.g runs it against WORKED_BUNDLE_PROFILES on
      every scan.

      WHAT THE SEVEN CELLS FOUND, IN ONE LINE EACH. .a two of my assumptions
      were wrong and checking them made the finding precise -- a real runner
      exists, and it calls 2 of the 7 checkers. .b ProfileFixture's eight fields
      carry no hash and no licence, so the registry records that a fixture
      EXISTS and never that it is the SAME one. .c a reproducibility check and a
      golden are different claims, and the estate has six controls of the first
      and none of the second. .d a parity comparison needs two independent
      reads, and the checker that says so had never been handed one; the format
      can carry four of its eight dimensions. .e the matrix had seven aspects,
      the cell names eight, and the missing one already had a checker. .f a
      rebuild from imported state must re-derive what it rebuilds from, and
      CONTROL 6 re-reads the bodies and keeps the layout. .g a golden that can
      be updated by editing it records whatever the last run did.

      FOUR OF THE FIVE UNFED CHECKERS NOW HAVE PRODUCERS. S11.13.a measured that
      golden-round-trip.ts has seven checkers and the runner called two.
      buildGoldenFaults (.c), importParityFaults (.d), verificationMatrixFaults
      (.e) and rebuildFaults (.f) each got their first producer in the repo;
      fixtureRegistryFaults got its first real registry in .b. All seven are now
      fed.

      Kit: 265 files / 15,961 tests green, 269 entry points, 0 violations, tsc
      clean and unfiltered; all seven scans re-run after their registrations. -->
  - [x] S11.13.a Define runner plugin for domain profile/version, fixture set,
        build inputs, expected layout/manifest/archive hash, import/migration,
        verification, and semantic/byte identity promise. Evidence: runner API.
<!--
S11.13.a — THE RULE: THE RUNNER PRODUCES BYTES AND THE CHECKERS CONSUME
DESCRIPTIONS, AND FIVE OF THE SEVEN DESCRIPTIONS HAVE NO PRODUCER.

TWO ASSUMPTIONS I MADE ABOUT THIS ESTATE WERE WRONG, AND BOTH ARE RECORDED
BECAUSE CHECKING THEM IS WHAT MADE THE FINDING PRECISE.

THE FIRST: that nothing performs a golden run. `tools/golden-round-trip.mjs` is
472 lines and performs a REAL one — it writes artefact bodies, serialises a
manifest, archives with GNU tar, hashes real bytes, and repeats the whole build
under shuffled input order, touched mtimes, a Turkish locale, a Kiritimati time
zone, a 0077 umask and interleaved writes, comparing tar HEADERS as well as
digests and asserting that `localeCompare` disagrees with byte order first so
the locale axis is known to be live. It extracts each archive and rebuilds FROM
THE EXTRACTED BYTES, byte-identical. And it reports honestly that "platform"
cannot be varied on one machine, NAMING it as never perturbed rather than
dropping it from the axis list. That is excellent work and S11.13.c and .f are
substantially done by it.

THE SECOND: that golden-round-trip.ts is only registers. It has SEVEN checkers
and they map one-to-one onto this section's seven cells.

WHAT IS ACTUALLY TRUE IS NARROWER AND CHECKABLE. The runner calls TWO of the
seven — `archiveReproducibilityFaults` and `goldenCoverageFaults`. The other
five take descriptions of a run:

    fixtureRegistryFaults      (profiles, ProfileFixture[])
    buildGoldenFaults          (BuildGolden)          ZERO producers
    importParityFaults         (fixtureId, ParityComparison[])
    verificationMatrixFaults   (VerificationRow[])
    rebuildFaults              (RebuildComparison)    ZERO producers

The checkers are ready and nothing hands them what a real run found.

SHIPPED. libs/oshun/workbench-kit/src/golden-runner.ts — a GoldenRunner contract
with ONE STAGE PER UNFED CHECKER, and each stage's return type named as the type
that checker takes. THE API IS READ OFF THE CHECKERS RATHER THAN DESIGNED: if a
checker's input changes, the stage that feeds it stops compiling, which is the
only kind of agreement worth having between two halves of a pipeline. Every
stage is required, because a checker handed nothing reports nothing — which is
indistinguishable from one handed a clean run.

THE IDENTITY PROMISE IS PART OF THE CONTRACT, because this cell names it. A
runner promising BYTE identity and one promising SEMANTIC identity are making
different claims and failing differently, and a rebuild comparison read without
knowing which was promised cannot be judged. A semantic promise with no
documented difference is refused — it is a byte promise somebody was not
confident enough to make — and a byte promise that documents one contradicts
itself.

AND THERE IS NOTHING TO PLUG INTO. `EXTENSION_POINTS` offers stage-model, gate,
block, preview, inspector, importer and publisher, and no golden-runner. This
cell's "runner plugin" is a shape nothing can yet be registered at. That fault is
reported on every run and IS the finding rather than a defect in this module,
with a positive control confirming the predicate would see a point if one
existed.

VERIFIED. 21 tests in src/golden-runner.spec.ts; kit suite 259 files / 15,811
tests green. tools/golden-runner-scan.mjs RECOUNTS which checkers the shipped
runner calls from its own source rather than trusting the register — so wiring
up a third fails loudly — enumerates golden-round-trip.ts's exported checkers to
catch one added or removed, and checks every stage's produced type against that
module's exports. 8 negative controls, exits 2 if any goes undetected. Five
registrations done; generator run: 263 entry points, 0 violations.
-->
  - [x] S11.13.b Register minimal/full/boundary/localized/provenance-rich/
        compatibility/invalid goldens per profile with licensed source and
        immutable hashes. Evidence: golden manifest.
<!--
S11.13.b — THE RULE: A GOLDEN IS A PROMISE THAT SOMETHING HAS NOT CHANGED, AND
`ProfileFixture` CARRIES NO HASH.

CREDIT FIRST. `GOLDEN_FIXTURE_CLASSES` is exactly the seven this cell names, and
`fixtureRegistryFaults` makes seven checks worth having — the best two being a
profile with NO refused fixture (a registry where nothing is declined has never
shown the validator can decline) and a fixture with no reason, because "a
fixture that starts passing names the rule".

MEASURED, on `ProfileFixture`'s EIGHT fields:

    hash fields    0
    licence fields 0

and `golden-round-trip.ts` does not use `Sha256Hex` anywhere in the module. Both
are things this cell names — "licensed source and immutable hashes" — and both
have a vocabulary elsewhere in the estate: `bill-of-materials.ts` exports
`ASSERTION_STATES` = `['asserted','none','unknown','not-applicable']`, which are
exactly the right four for a licence, because they separate "we checked and
there is none" from "nobody looked".

WHY THE HASH MATTERS MORE THAN IT LOOKS. A golden's entire job is to fail when
something changed. Without one the registry records that a fixture EXISTS and
never that it is the SAME fixture — so a golden edited in place goes on passing,
and the run that should have caught the change is the run that was changed to
accept it.

WHY THE LICENCE IS NOT A FORMALITY. A golden's content is MATERIAL,
redistributed in every clone and every container image, and a fixture whose
source nobody recorded cannot be shown to be redistributable at all.

SHIPPED. libs/oshun/workbench-kit/src/golden-manifest.ts — GoldenEntry with a
contentHash, a recomputedHash and a LicensedSource; `permitsRedistribution`,
which admits an asserted licence only when it names an identifier and admits
`not-applicable` because content this repository authored has no third-party
terms; 8 fault kinds; and a worked manifest of 14 entries over the 2 exported
profiles and all 7 classes.

THAT MANIFEST IS HANDED TO THE ESTATE'S OWN `fixtureRegistryFaults` AND COMES
BACK CLEAN — the first time anything has handed that checker a registry at all.
The scan carries a positive control confirming the checker really does object
when no fixture is expected to be refused, because an empty finding list from a
checker nobody has seen refuse anything is not a measurement.

VERIFIED. 25 tests in src/golden-manifest.spec.ts; kit suite 260 files / 15,837
tests green. tools/golden-manifest-scan.mjs re-reads ProfileFixture's OWN FIELD
LIST from golden-round-trip.ts on every run, so adding a hash or a licence field
— either would be good news — fails loudly. 8 negative controls, exits 2 if any
goes undetected. Five registrations done; generator run: 264 entry points, 0
violations.
-->
  - [x] S11.13.c Build under controlled/perturbed order/clock/locale/platform
        and compare layout/manifest/components/provenance/archive bytes.
        Evidence: build runner.
        <!-- A REPRODUCIBILITY CHECK AND A GOLDEN ARE DIFFERENT CLAIMS: ONE SAYS
        THE BUILDER AGREES WITH ITSELF, THE OTHER SAYS IT AGREES WITH A DECISION
        SOMEBODY WROTE DOWN, AND THE ESTATE HAS SIX CONTROLS OF THE FIRST AND
        NONE OF THE SECOND.

        CREDIT FIRST, AND IT IS MOST OF THIS CELL. tools/golden-round-trip.mjs
        already performs a real perturbed build: resolveLayout from the profile,
        artefact bodies written, a manifest serialised, the tree archived with
        GNU tar, bytes hashed -- then the whole build repeated under shuffled
        input order, touched mtimes, LC_ALL=tr_TR.UTF-8, TZ=Pacific/Kiritimati,
        a 0077 umask and interleaved writes, comparing the tar HEADERS as well
        as the digest. It asserts FIRST that localeCompare disagrees with byte
        order on a probe pair, so the locale axis is known live rather than
        assumed. It rebuilds from the EXTRACTED bytes, byte-identical. And it
        reports honestly that `platform` cannot be varied on one machine, naming
        it as never perturbed rather than dropping it from the axis list. Four
        of this cell's axes are driven there; the fifth is disclosed there. None
        of that is redone here.

        MEASURED, AND THE SCAN RETAKES THE COUNT EVERY RUN: checked-in expected
        values in that tool = ZERO. All six comparisons are sha256(perturbed
        .bytes) against sha256(baseline.bytes), both computed in one process,
        moments apart, by the same code. The estate's OWN checker names that
        shape: buildGoldenFaults calls an expectedSource of
        `regenerated-at-test-time` the case where "the expected value is
        produced by the same code that produces the actual value, so the
        comparison agrees with itself for every input and can never fail". So
        the tool detects nondeterminism -- which is what it was built for and is
        genuinely good at -- and CANNOT detect a change to the builder: rename
        every path, reorder the manifest's keys, drop provenance entirely, and
        both sides move together while all six controls pass.

        SHIPPED src/build-golden.ts. The four aspects BUILD_GOLDEN_ASPECTS names
        -- manifest, layout, component-inventory, provenance -- each projected
        to a canonical string, each compared against a checked-in literal, and
        the result handed to buildGoldenFaults. That makes this the FIRST
        PRODUCER of BuildGolden since that checker was written (S11.13.a
        measured it at zero). 29 spec tests; scan PASSES with 6 negative
        controls, all caught.

        THE RECORDED VALUE IS A READABLE STRING, NOT A DIGEST. A golden recorded
        as a digest tells whoever reviews the change that something moved and
        never what moved, which makes the review that is supposed to approve the
        new answer a rubber stamp on an opaque one. The eight literals run 145
        to 915 characters; a reviewer sees the path that changed.

        producedAt is EXCLUDED from the provenance aspect and the exclusion is
        NAMED in PER_RUN_PROVENANCE_FIELDS. Provenance is the one aspect not
        reproducible by construction -- it carries the instant the build ran, so
        a golden pinning it whole could never match twice, and the only way to
        make it pass is to freeze the clock, which is what the surrounding tool
        does and what a real build cannot. Pinning the stable part and naming
        the per-run part is a documented approximation; pinning nothing is what
        the estate does now. Related: `producedBy` is set on the REQUEST and is
        absent from the manifest the tool writes, so provenance never reaches
        the built bytes at all -- re-measured every scan run.

        ASPECT_INDEPENDENCE is five mutations that each declare which aspects
        must move AND which must not, every one verified through the estate's
        checker. If every mutation moved all four, four aspects would be
        decorative. A moved file changes layout and manifest and leaves the
        inventory alone; a changed digest leaves layout alone; a different
        producer moves provenance and nothing else -- the aspect the archive
        bytes cannot carry, because it is not in them.

        TWO DEFECTS THIS CELL FOUND IN ITSELF, BOTH ITS OWN SUBJECT, both kept
        as negative controls. (1) The inventory first sorted on a key of slotId
        and digest; bundle.dataset-release fills its attestation slot TWICE with
        entries agreeing on both, so the tie was broken by sort stability -- by
        the order the inputs arrived in -- INSIDE the projection meant to detect
        exactly that. It now sorts on the whole canonical element, which can
        only tie for equal elements. (2) The locale positive control first
        probed 'i' against 'I', where Turkish collation and byte order AGREE, so
        it failed; replaced with the pair the estate's own tool proved.

        ALSO CORRECTED HERE, and it is a process defect of mine worth recording:
        adding this cell's tsconfig path without a leading "./" raised TS5090,
        and fixing that unmasked 19 pre-existing type errors across 8 modules I
        shipped earlier this session -- invisible until now because I had been
        grepping tsc output for the current cell's filename each time. Two were
        substantive: queue-invariants filtered terminal jobs on the literal
        'failed', which is NOT a JobState, so that clause was always true and
        the set quietly lost 'dead-lettered' (now uses the estate's own
        TERMINAL_JOB_STATES); and toolCallFaults pushed five ToolCallFault kinds
        into MalformedFinding[], whose kind union declares none of them (now
        ToolCallFinding). A third: cancellation-fixtures' JobBudget carried
        maxAttempts/maxSeconds/maxCostMinorUnits, three names in no type in this
        repo, hidden by a blanket `as JobRecord`; JobBudget is unit/ceiling/
        spent. Kit: 261 files / 15,867 tests green, 265 entry points, 0
        violations, tsc clean.

        AND A LATENT SCAN DEFECT THE SAME PASS FOUND: malformed-fixtures' and
        lease-fixtures' scans text-searched for symbol names, so the kit's
        project.json "//" docs and the export-map generator's reason strings --
        registers of PROSE ABOUT the code, written during the five registrations
        -- counted as callers. Both scans had passed when run BEFORE those
        registrations existed, which is the order these cells are built in. Both
        now require an application or an import. RUN A CELL'S SCAN AGAIN AFTER
        ITS REGISTRATIONS. -->
  - [x] S11.13.d Import/preflight/migrate/validate/persist/cleanup and compare
        semantic entities/artifacts/refs/lineage/loss report. Evidence: import
        runner.
        <!-- A PARITY COMPARISON NEEDS TWO INDEPENDENT READS, AND THE CHECKER
        THAT SAYS SO HAS NEVER BEEN HANDED ONE.

        CREDIT FIRST, AND IT IS FOUR OF THE SIX STAGES.
        tools/importer-roundtrip.mjs is 438 lines driving a REAL round trip: a
        v2 bundle built with real tar, validated against a real JSON schema with
        ajv, migrated to v4 and re-validated -- with a control asserting the v2
        manifest FAILS the v4 schema FIRST, so the migration is known to have
        moved something rather than assumed to have. It measures a real gzip
        ratio, verifies a real P-256 signature and shows it does NOT survive a
        migration, catches a naive v5 reader silently downgrading a v6 manifest,
        and its CONTROL 4 IS THE LOSS REPORT: a real migration drops a field and
        the report is the diff. It calls preflightFaults and
        migrationIntegrityFaults for real. Import, preflight, migrate, validate
        and the loss report are covered there and none of it is redone here.

        WHAT IS NOT DRIVEN, MEASURED BY COUNTING: `persist` and `cleanup` appear
        ZERO times in that file, and so does every one of the eight
        IMPORT_PARITY_DIMENSIONS. importParityFaults is called by nothing and
        ParityComparison has NO PRODUCER anywhere in the repo -- so
        `bothSidesRead`, the field recording whether a comparison was a
        photocopy, has never carried a value. The estate's own words for what
        that field guards: "the photocopy in the one place it is hardest to see:
        comparing the imported object to the object the importer just produced
        from the same parse tests that a function is deterministic, not that the
        bundle survived."

        SHIPPED src/import-parity.ts + a scan that does the real thing. A bundle
        is built, bodies written, manifest serialised, tree archived with GNU
        tar, and then EXTRACTED TO A DIFFERENT DIRECTORY, where the manifest is
        re-parsed from its file and every digest recomputed from the extracted
        bytes. `bothSidesRead` is COMPUTED from the two origins, so a comparison
        that cheated would report itself; 8 comparisons across 2 profiles, all 8
        with both sides read. compareSides refuses two different dimensions
        outright. 24 spec tests plus 4 more below; scan PASSES with 8 negative
        controls, all caught.

        AND THE MEASUREMENT THAT DECIDES THE CELL'S SHAPE: THE CHECKER ASKS
        ABOUT EIGHT DIMENSIONS AND THE FORMAT CAN CARRY FOUR. Across
        BundleProfile, LayoutEntry and BundleRequest -- 22 fields, re-derived by
        the scan from the interfaces themselves, with externalRef as a positive
        control so the zeros are facts and not a broken search -- there is
        nothing holding a rights position, nothing for consent and nothing for a
        gate. Those are NOT reported as mismatches: a dimension with no second
        side to read would be a defect filed against a bundle that is exactly
        what its schema says it should be. They are left out, and the estate's
        own checker reports rights-or-consent-never-compared for two of them --
        separated in its words because "a bundle that arrives with its rights
        position flattened is content being used on terms nobody agreed to, and
        it imports perfectly."

        CARRIAGE_STATES KEEPS A DISTINCTION THE CHECKER'S VOCABULARY CANNOT.
        provenance is `carried-but-not-written` -- producedBy and producedAt are
        on the request and neither reaches the manifest, as S11.13.c measured --
        while rights is `absent-from-the-format`. To importParityFaults both are
        simply absent from the comparison list and come back as
        dimension-never-compared; a format with no field and a field nothing
        writes are different repairs and belong in different queues.

        TWO DEFECTS THIS CELL FOUND IN ITSELF, both this session's recurring
        shape. (1) planWroteNothing was derived from `persisted`, which is the
        EXECUTION's writes, so every import that successfully wrote its entities
        reported that its PREVIEW had written them -- two counts of files on
        disk, related because both are counts of files on disk, answering
        different questions. The plan is now listed at its own moment and
        nothing else can stand in for it. (2) residue was every surviving file,
        so the scan reported the two entities an import had just persisted as
        residue -- filing an import's own success as a defect. A SURVIVING FILE
        IS RESIDUE ONLY RELATIVE TO WHAT THE RUN CLAIMS HAPPENED:
        bundleImportFaults reads residue only when the outcome is `failed` and
        when a rollback claims a partial import, so residueForOutcome takes the
        outcome. Both kept as tests.

        ALSO REPAIRED, and it is a real finding in the S11.9 fuzz-schedule cell:
        its register counted its own family, so it failed TWICE in one session
        reporting growth as drift -- every cell adds one -scan.mjs and one
        verify:operations script, which makes an equality stale by
        construction. kitScans and declaredScripts are now FLOORS, which still
        catch the direction that loses coverage (a scan deleted). In their place
        the check the count was standing in for: TEN OF THE 115 KIT SCANS HAVE
        NO verify:operations SCRIPT -- audit-integrity, catalog-bypass,
        cost-attribution, degradation-plan, exhaustion-alert, game-day,
        log-leak, production, restore-consistency, router-vocabulary. They run
        in the workflow, which globs by path, and can be invoked by name
        nowhere. That number does NOT grow with the kit, so it is checked for
        equality and a scan wired into neither trips it.

        Kit: 262 files / 15,896 tests green, 266 entry points, 0 violations,
        tsc clean and UNFILTERED this time. -->
  - [x] S11.13.e Verify paths/schema/hash/signature/trust/dependency/profile/
        target compatibility and expected invalid/refusal outcomes. Evidence:
        verify runner.
        <!-- THE MATRIX HAD SEVEN ASPECTS, THIS CELL NAMES EIGHT, AND THE
        MISSING ONE IS THE ONE THE ESTATE ALREADY HAD A CHECKER FOR.

        VERIFICATION_ASPECTS was schema, paths, hashes, signature, trust,
        profile-semantics and target-semantics. This cell asks for
        paths/schema/hash/signature/trust/dependency/profile/target, and
        golden-round-trip.ts did not contain the string "dependenc" anywhere.

        It was not missing for want of an implementation. bill-of-materials.ts
        has EXTERNAL_COMPONENT_KINDS -- url, package, model, provider-endpoint,
        resolver, tool, plugin, font, license-document, dataset -- and nine
        external-component faults with externalComponentFaults deciding them,
        including unpinned-remote-required-at-runtime and
        pinned-remote-with-no-digest, which is exactly what verifying a
        dependency means. A whole checker, and no aspect to put its answer in.
        `dependency` is added to the estate's list here and wired to it.

        SHIPPED src/verify-runner.ts + scan. SIXTEEN RUNS: eight aspects in BOTH
        directions, every verdict from a real estate function or real crypto --
        validatorFaults, resolveLayout, stampFaults, bundleProfileFaults,
        targetAccepts, externalComponentFaults, a real sha256 and a real P-256
        key pair -- never from a table of expected answers. 8 refusals, each
        tripping the fault its binding names. 21 spec tests; scan PASSES with 8
        negative controls, all caught. verificationMatrixFaults had NO producer
        before this: VerificationRow appears in golden-runner.ts as a stage's
        return type, which names it without making one.

        THE THIRD OBSERVED VALUE IS LOAD-BEARING. The estate keeps
        aspect-that-did-not-run apart from a mismatch and says why: "an
        unreachable trust store and a valid chain both leave the signature
        aspect with nothing to report, and only one of them is fine." A runner
        that could only say accepted or refused renders a verification that
        never executed identically to one that passed.

        BOTH DIRECTIONS, because matrix-with-no-expected-refusals exists for a
        matrix of green rows nobody has ever seen say no, and
        expected-refusal-that-was-accepted is, in the estate's words, "the
        finding this whole matrix exists for. Everything else says the pipeline
        still handles what it handled; only an invalid fixture that got through
        says a check stopped running -- and a check that stopped running
        produces no error anywhere else, ever."

        FOUR DEFECTS THIS CELL FOUND IN ITSELF, AND ONE CHECK FOUND THREE OF
        THEM. The module's header promised that a refusal must name the finding
        it was refused FOR -- a refusal for the wrong reason is an acceptance
        wearing the right answer -- while the scan asserted only that a refusal
        happened. Adding the check the prose already demanded immediately
        showed: (1) the dependency refusal fixture used reachability 'public',
        which is in no ReachabilityState, so it was ACCEPTED, and only a
        negative control noticed; (2) the schema ACCEPTED case was refused,
        because the digest a validator was compiled from lives on ValidatorBuild
        and I had left it undefined; (3) two expected fault names were guesses
        no estate list contains -- the real ones are
        schema-changed-since-the-validator-was-compiled and
        target-requiring-a-slot-the-profile-does-not-declare. THE LESSON: A
        CHECK THAT SOMETHING WAS REFUSED IS NOT A CHECK THAT IT WAS REFUSED FOR
        THE REASON YOU MEANT, AND THE PROSE HAD SAID SO ALREADY.

        (4) The matrix was judged one direction at a time, and the accepted half
        came back matrix-with-no-expected-refusals -- correctly, because a half
        containing only acceptances is exactly what that fault is for. Splitting
        the matrix defeats the check that it has ever been seen to say no; the
        checker takes aspects as a Set for coverage, so two rows per aspect is
        what it is built for. Judged as one 16-row matrix.

        Kit: 263 files / 15,918 tests green, 267 entry points, 0 violations, tsc
        clean and unfiltered. Every scan re-run AFTER the five registrations. -->
  - [x] S11.13.f Rebuild from imported state and compare byte or documented
        semantic identity. Evidence: round-trip runner.
        <!-- A REBUILD FROM IMPORTED STATE MUST RE-DERIVE WHAT IT REBUILDS FROM,
        AND CONTROL 6 RE-READS THE BODIES AND KEEPS THE LAYOUT.

        CREDIT, AND IT IS A REAL REBUILD. tools/golden-round-trip.mjs CONTROL 6
        extracts each archive to a fresh directory, reads the manifest back off
        it, checks that manifest names the profile it should, re-reads every
        artifact body from the extracted tree, rebuilds, re-archives and asserts
        the two archives hash the same. Through real tar, and it passes.

        WHAT IT KEEPS, AND WHY IT MATTERS. The rebuild is assembled as
        `{ ...input, body: readFileSync(...) }` over the ORIGINAL inputs, and
        handed the ORIGINAL `entries` -- the resolved layout. So one field comes
        back off the disk and everything else is the object that was already
        there: the paths, the slot each artifact belongs to, its media type, its
        ordinal, its declared digest. Those are exactly the things an import has
        to reconstruct, and keeping them means the rebuild CANNOT FAIL on them.
        A manifest that omitted a path, recorded a slot wrongly or lost an
        ordinal would still rebuild byte-identically, because nothing ever asked
        the manifest for any of it. This is S11.13.d's one-side-derived-from-the-
        other arriving in the rebuild, and it is the difference between the
        "extracted state" the tool uses and the "imported state" this cell asks
        for: extracting is untarring, importing is reconstructing the entities.

        SHIPPED src/rebuild-identity.ts + scan. ReconstructedState is a type
        with NO field through which an original object can arrive, and
        rebuildFrom takes one and no second argument, so a rebuild written
        against it cannot quietly keep the layout it was supposed to re-derive.
        18 spec tests; scan PASSES with 9 negative controls, all caught.

        THE DEMONSTRATION IS THE WHOLE CELL IN TWO ARCHIVES. A bundle carrying
        one file that no manifest entry names, rebuilt twice: from the ORIGINAL
        layout it comes out byte-identical and reports nothing; from the
        MANIFEST it loses the file, and reconstructionFaults names it as
        file-in-the-archive-with-no-manifest-entry. Both halves asserted, so
        neither can quietly stop being true.

        AND THE PROMISE NOBODY MADE. rebuildFaults reports
        profile-that-promises-no-identity-at-all when the promise is null,
        because "a rebuild comparison against no promise passes whatever comes
        out". BundleProfile is profileId, profileVersion, manifestPath,
        manifestSchemaId, manifestSchemaVersion, slots, targets and limits --
        there is NO field for an identity promise, so every profile in the
        estate promises nothing and that fault fires for all of them.
        PROFILE_PROMISES is the declaration the profile cannot carry;
        promiseFor deliberately returns null for anything undeclared so the
        fault can still fire, because defaulting it would hide the one finding
        that says a rebuild test proves nothing. Both worked profiles promise
        byte identity, and the estate already demonstrates they can keep it.

        TWO DEFECTS OF MINE, FIXED HERE. (1) golden-runner.ts (S11.13.a)
        declared its own IDENTITY_PROMISES = ['byte', 'semantic'] beside the
        estate's ['byte-identical', 'semantically-identical'] -- the same two
        concepts under two spellings in one library, with RebuildComparison
        typed against one of them. It now re-exports the estate's. The estate
        warns about exactly this shape a few hundred lines from where it
        happened: "a second list of the things that vary between two machines
        would drift from the first, and the one that drifted would be the one
        nobody was reading." (2) RECONSTRUCTION_FAULTS shipped with
        manifest-that-names-another-profile emitted by NOTHING, and I wrote a
        comment in the spec excusing it -- the unreachable-fault-kind defect
        this section keeps finding in other people's code, THIRTEENTH INSTANCE,
        mine. reconstructionFaults now takes the profile the archive was opened
        as, and the kind is reachable and controlled.

        Kit: 264 files / 15,937 tests green, 268 entry points, 0 violations,
        tsc clean and unfiltered; every scan re-run after the registrations. -->
  - [x] S11.13.g Fail registry/CI when a profile/version lacks current golden or
        a golden update lacks reviewed source/semantic rationale. Evidence:
        coverage gate.
        <!-- A GOLDEN THAT CAN BE UPDATED BY EDITING IT IS A GOLDEN THAT RECORDS
        WHATEVER THE LAST RUN DID.

        CREDIT, AND THE FIRST HALF IS ALREADY DONE. goldenCoverageFaults walks
        the SHIPPED profile registry and asks what coverage exists for each --
        the argument order is the whole thing, in the estate's words, because a
        report built the other way round "finds every golden covered by
        definition, and a profile added tomorrow with no fixtures at all is
        precisely what it cannot see". It reports a registered profile with no
        coverage, a missing fixture class, a missing round-trip stage, coverage
        against an older profile version, and coverage for a profile nobody
        ships. tools/golden-round-trip.mjs calls it with WORKED_BUNDLE_PROFILES
        -- the real registry, not a fixture -- and CI runs that tool. "Fail when
        a profile/version lacks a current golden" is covered.

        THE SECOND HALF HAD NO VOCABULARY AT ALL. The words rationale,
        reviewedBy and updateReason appear NOWHERE in golden-round-trip.ts,
        golden-manifest.ts or build-golden.ts. Nothing models an UPDATE. The
        estate names the automated version --
        golden-that-rewrites-itself-on-mismatch, which "turns a checked-in
        literal into a recording of the most recent run on the first red build"
        -- and the manual version is the same thing at human speed. S11.13.c
        checked eight golden literals in precisely so a change would be visible
        in review, and VISIBLE IN REVIEW IS NOT GATED: today a red golden test
        is repaired by pasting the new value over the old one, and every check
        in this section goes green again against the new answer, with nothing
        recording that it moved or why that was allowed.

        SHIPPED src/golden-update-gate.ts + scan. A gate on SOURCE, RATIONALE
        and REVIEW -- all three, because any two let the third through: a
        reviewed regeneration is a reviewer approving a value the code chose, a
        rationale with no review is a note nobody read, and a review with no
        rationale is agreement to something unstated. The source vocabulary is
        IMPORTED from BuildGolden['expectedSource'] rather than restated,
        because how a value got here is the same question at record time and at
        update time, and S11.13.f had to remove one duplicate vocabulary from
        this kit already. The rationale is separate from the source because "the
        build now emits this" answers where the value came from and not why it
        is correct, and it must NAME the change it points at. reviewedBy equal
        to updatedBy is refused for the reason a signature over your own claim
        is not a second opinion. An update that moved nothing is reported rather
        than waved through, and stops there, because the remaining questions are
        about a change. 23 spec tests; scan PASSES with 10 negative controls.

        AND THE GATE CAN SEE AN UPDATE, which is what keeps it from being a
        shape. A gate taking updates as an argument is a gate somebody has to
        remember to feed, so the scan reads the CURRENT recorded literals out of
        build-golden.ts and the PREVIOUS ones out of `git show HEAD:`, and any
        literal that differs is a real update it has to judge. Nothing is passed
        in by hand. TWO CONTROLS GUARD THAT ZERO: the extractor's literal count
        must equal what the module exports (8 = 8), and a synthesised edit must
        be seen as exactly one update -- because a zero from a parser nobody
        proved can parse is not a measurement.

        Every one of the seven update fault kinds is reached from a distinct
        input, in both the spec and the scan, so none of them is the
        unreachable-fault-kind defect this section has now corrected fourteen
        times. -->
- [x] S11.14 Add provenance tamper and trust-chain fixtures. Evidence: valid,
      invalid, unknown, expired, and stripped states.
      <!-- a-g all closed. The evidence clause names five states and the corpora
      reach all five, verified by handing every fixture to the estate's own
      credentialState rather than asserting: valid-and-trusted (.a), the invalid
      family (.c, seven separable causes), unknown-or-unsupported (.d), expired
      (.e), and stripped plus partially-recoverable and missing (.a, .f).

      THE SECTION'S ONE FINDING, SEVEN TIMES. Everything a reader would act on
      differently arrives as one scalar, and each cell measured a different face
      of it: ten checks to six states (.b), seven of nine causes separable and
      only two by the state (.c), a six-month-old cache reading identically to a
      live lookup (.d), five lifecycle positions arriving as two states (.e),
      one perceptual match indistinguishable from several (.f). .g reads the
      seven dimensions side by side and names what the headline hides.

      AND THE SECOND SHAPE, FOUR TIMES: THE CONCEPT IS MODELLED A MODULE AWAY.
      .c's schema (manifest-schema.ts has a registered schema with a pinned ajv
      validator, for the OTHER manifest), .d's freshness (trust-results.ts
      EXPIRY_STATES), .e's consent and retention (approval-workflows.ts
      `withdrawn`, audit-snapshots.ts `retainUntilMs`), and .g's policy, which
      has no home anywhere. Recorded as modelled-elsewhere rather than absent
      throughout, because one is a wire and the other is design work.

      THE RESTRAINT, AND IT HELD FOUR TIMES. This section did NOT widen
      VALIDATION_CHECKS, CHECK_OUTCOMES or ExternalWitness. S11.13.e widened
      VERIFICATION_ASPECTS for the same kind of gap and that was right -- that
      list had zero consumers outside specs. These three are read by
      ProvenanceInspector.tsx, provenance-inspector.ts, suggestions.ts and a
      browser tool, so widening them changes a shipped product surface. The
      missing axis was declared beside each corpus instead, and every fixture
      still runs through the estate with the vocabulary the estate has.

      CREDIT, THROUGHOUT. tools/c2pa-signing.mjs is 493 lines of real OpenSSL
      certificate authorities and real P-256 signatures whose CONTROL 1 proves
      the naive "does the signature verify" returns TRUE for four of the five
      situations. content-credentials.ts is 1,293 lines that get the hard
      distinctions right -- untrusted is not invalid, an unsupported assertion
      must not invalidate, an unverified timestamp is no timestamp. And
      unavailable-revocation-treated-as-a-result reads the DETAIL STRING rather
      than the outcome, catching an outage whose message reads like an answer,
      which is the best single check in the library.

      Kit: 272 files / 16,120 tests green, 276 entry points, 0 violations, tsc
      clean and unfiltered; all seven scans re-run after their registrations. -->
  - [x] S11.14.a Add valid embedded/sidecar/ledger fixtures with complete and
        partial ingredients, supported assertions, trusted chain, timestamp, and
        hard/soft bindings. Evidence: valid corpus.
        <!-- A LEDGER-RECOVERABLE CREDENTIAL IS NEVER valid-and-trusted.

        CREDIT FIRST, AND IT IS REAL CRYPTOGRAPHY. tools/c2pa-signing.mjs is 493
        lines that generate two independent root certificate authorities and
        three leaf certificates with OpenSSL, build and verify the paths with
        OpenSSL, and sign and verify over the exact bytes with node:crypto. Its
        CONTROL 1 is the argument this whole section rests on: it asserts the
        naive check -- "does the signature verify" -- returns TRUE for FOUR of
        the five situations (valid, untrusted, expired, stripped) BEFORE
        asserting credentialState returns four different answers. Three of those
        four trues are wrong, and no test that only checked the strict function
        would show it. Not re-done here.

        THE CELL ASKS FOR EMBEDDED, SIDECAR AND LEDGER FIXTURES, AND THE ESTATE
        IS RIGHT THAT ONLY TWO OF THOSE ARE PLACES A CREDENTIAL IS EMBEDDED.
        EMBEDDING_MECHANISMS is embedded, sidecar, embedded-and-sidecar and
        none, with no ledger -- and that is not an omission. A credential is not
        embedded in a ledger; a ledger is somewhere it can be FOUND when it is
        embedded nowhere. The estate already says so in the type system:
        ExternalWitness carries ledgerHoldsCredentialForDigest, and
        credentialState reads it only on the branch where credentialPresent is
        false.

        THE CONSEQUENCE, and a corpus built on the cell's wording would get it
        wrong. Measured by calling the estate: a ledger-backed absence is
        `stripped`, a soft-binding match is `partially-recoverable`, and the
        same absence with no witness is `missing`. THREE DIFFERENT ANSWERS, and
        the scan asserts all three differ, so the witness is known to decide
        something rather than assumed to. "A valid ledger fixture" is a category
        the estate deliberately does not have.

        SHIPPED src/credential-corpus.ts + scan. RECOVERY_SOURCES is kept as a
        SEPARATE AXIS from the embedding, because keeping them as one list is
        exactly what would make a ledger look like a fourth kind of embedding.
        Eight fixtures covering every value of all four axes, reaching 4 of the
        9 credential states. EVERY FIXTURE'S EXPECTED STATE IS RE-JUDGED ON
        EVERY RUN by handing a real ValidationReport to the estate's own
        credentialState, so the corpus cannot drift from the thing it exists to
        exercise. 22 spec tests; scan PASSES with 9 negative controls.

        Two details the estate gets right and this corpus records rather than
        flattens: a soft binding leaves hard-binding `not-applicable` rather
        than failed -- the outcome vocabulary has a value for exactly that --
        and a PARTIAL ingredient chain is still valid-and-trusted, because the
        estate reports the gap and does not call the credential broken.

        Six corpus fault kinds, every one reachable from a distinct input in
        both spec and scan. -->
  - [x] S11.14.b Tamper artifact bytes, manifest/assertion/ingredient/action/
        actor/tool/policy fields, signature, certificate chain, timestamp,
        ledger record, and binding. Evidence: tamper corpus.
        <!-- TEN CHECKS, SIX STATES, AND THREE CHECKS THAT FAIL WITHOUT MOVING
        THE STATE AT ALL. A TAMPER CORPUS THAT ASSERTS ON THE STATE IS BLIND TO
        THREE OF THEM AND CANNOT TELL TWO PAIRS APART.

        CREDIT: c2pa-signing.mjs CONTROL 2 already performs two REAL tamper
        routes with real cryptography -- "a flipped asset byte breaks the hard
        binding while the signature over the manifest still verifies, and a
        flipped manifest byte breaks the signature". Demonstrated on real bytes,
        not described. Not re-done here.

        MEASURED by failing each of the ten VALIDATION_CHECKS one at a time and
        asking credentialState:
          manifest-structure      -> unknown-or-unsupported
          hard-binding            -> tampered
          signature               -> tampered
          revocation              -> revoked
          certificate-validity    -> expired
          certificate-chain       -> cryptographically-valid-untrusted
          trust-list              -> cryptographically-valid-untrusted
          timestamp               -> valid-and-trusted   (decidedBy NULL)
          assertion-support       -> valid-and-trusted   (decidedBy NULL)
          ingredient-availability -> valid-and-trusted   (decidedBy NULL)

        THE THREE ARE NOT A DEFECT AND THIS CELL DOES NOT REPORT THEM AS ONE. An
        assertion the validator does not support must not invalidate a
        credential -- that is the whole point of having an assertion-support
        OUTCOME rather than a failure. An ingredient that cannot be fetched says
        nothing about the signature over what is there. And a timestamp is
        load-bearing only once the certificate has expired, which is exactly the
        case CONTROL 4 of the estate's tool covers. All three are right to leave
        the state alone.

        What follows is a fact about the CORPUS, not the estate: the state is a
        summary and a tamper is a fact about the REPORT. So every case declares
        the check it trips, and the scan asserts on the check and on decidedBy,
        never on the state alone -- asserting on the state would give the
        thirteen targets six answers, three of which are the answer an
        untampered credential gives.

        SHIPPED src/tamper-corpus.ts + scan. Thirteen targets in the cell's own
        words over FIVE distinguishable routes, with EIGHT of the thirteen --
        manifest, assertion, ingredient, action, actor, tool, policy and the
        signature itself -- landing on the signature check, because everything
        inside a signed manifest breaks the same way and the report cannot say
        which field moved. Each of those eight still records what its field
        MEANS (an action is what was done, an actor who did it, a tool what made
        it, a policy what was agreed), because the reason they are
        indistinguishable is precisely that they matter differently. 18 spec
        tests; scan PASSES with 9 negative controls.

        AND THE ONE TARGET OUTSIDE VALIDATION. The LEDGER RECORD lives outside
        the asset, so the signature still verifies and the binding still holds,
        and ExternalWitness is a boolean pair with no integrity of its own -- an
        edited ledger answers exactly like an untouched one. Reported as
        `outside-the-validators-reach` rather than as a passing tamper, because
        "we checked and it survived" and "nothing looked" are different
        sentences and only the second is true. (A tampered trust list is the
        same shape; S11.14.d owns that.)

        Every number above is RECOMPUTED by the scan from the estate rather than
        read off the register, with a positive control that at least one check
        does move the state. -->
  - [x] S11.14.c Add structurally/schema/signature/binding/chain/revocation/
        timestamp/assertion/ingredient invalid variants with separable causes.
        Evidence: invalid corpus.
        <!-- SEPARABILITY IS A PROPERTY OF THE REPORT, NOT OF THE STATE.

        "Separable" is the whole requirement here: nine fixtures that all come
        back "invalid" have tested that the validator says no, not that it says
        why, and the repair for a broken chain and the repair for a stale
        revocation list are done by different people on different days.

        COMPUTED by calling credentialState for each variant rather than
        declared. SEVEN of the nine are separable, and only TWO of those by the
        STATE alone -- chain-invalid and revocation-invalid. The other five need
        the per-check report, because S11.14.b measured that ten checks collapse
        to six states. TWO ARE NOT SEPARABLE AT ALL, AND THEY ARE A PAIR:
        structurally-invalid and schema-invalid. Inseparability is a property of
        the PAIR rather than of one member -- nothing can say which of the two
        happened, so both are unidentified. This register first said "one is
        not" and the computation corrected it.

        THE CAUSE. VALIDATION_CHECKS is manifest-structure, hard-binding,
        signature, certificate-chain, certificate-validity, trust-list,
        revocation, timestamp, assertion-support and ingredient-availability.
        There is NO schema check, and the whole 1,293-line module contains the
        word "schema" once. So a credential manifest that PARSES and violates
        its schema is reported as manifest-structure -- the same answer a
        corrupt one gets.

        Those are different failures with different repairs. A structurally
        broken manifest is corrupt or truncated: a transport problem, fixed by
        fetching the bytes again. A schema-invalid manifest parses perfectly and
        says something the specification does not allow -- a wrong field type, a
        missing required member, a version the writer and reader disagree about
        -- and no refetch will ever help.

        AND THE KIT ALREADY KNOWS HOW TO TELL THEM APART, FOR THE OTHER
        MANIFEST. manifest-schema.ts carries BUNDLE_MANIFEST_SCHEMA with an id,
        a version, a path, an owner, a generation command and a pinned ajv
        validator, plus schemaRegistryFaults and validatorFaults to police it.
        The bundle manifest gets a schema registry; the credential manifest gets
        one check called manifest-structure.

        WHAT THIS CELL DELIBERATELY DOES NOT DO. It does not add a schema member
        to VALIDATION_CHECKS. S11.13.e widened VERIFICATION_ASPECTS for the same
        kind of gap and that list had no consumers outside specs; this one is
        read by ProvenanceInspector.tsx, provenance-inspector.ts, suggestions.ts
        and a browser tool, so widening it changes a SHIPPED PRODUCT SURFACE and
        makes every existing report emit check-never-asked for a check nothing
        performs. That is a product decision, not a fixture-corpus one. The gap
        is reported instead, and the scan re-measures the absence every run so
        the day a schema check appears this register goes red.

        SHIPPED src/invalid-corpus.ts + scan. 19 spec tests; scan PASSES with 8
        negative controls, including the positive control that matters: breaking
        the pair makes the inseparability finding GO AWAY, so it is a fact about
        this corpus rather than something the function always says. Five fault
        kinds, every one reachable. -->
  - [x] S11.14.d Add unknown signer/root/trust list/assertion/algorithm/format/
        version/ledger and offline or stale trust-list variants. Evidence:
        unknown corpus.
        <!-- A TRUST LIST THAT ANSWERS FROM A CACHE IS NOT A TRUST LIST THAT
        ANSWERED, AND THE ESTATE HAS `unavailable` FOR "COULD NOT ASK" AND
        NOTHING FOR "ASKED SOMETHING OUT OF DATE".

        CREDIT, AND IT IS THE SHARPEST CHECK IN THE MODULE.
        unavailable-revocation-treated-as-a-result does not look at the outcome
        at all -- it reads the DETAIL STRING, and fires when a revocation the
        validator could not reach carries a message reading "not revoked",
        "good" or "valid". The estate's own words: "an outage becomes a trust
        decision, silently, for every asset validated during it." A check on
        what a report SAYS rather than on what it concluded is a rare and good
        idea, and it is exactly this cell's offline hazard. Measured both ways:
        silent on an honest detail, loud on a lying one, and both give the same
        state -- so only the report separates them.

        MEASURED ACROSS ALL TEN CHECKS. An `unavailable` outcome collapses the
        state to unknown-or-unsupported and produces NO report fault, except
        that one. A `not-performed` outcome gives the same state AND a report
        fault (not-performed-reported-for-a-credential-that-is-present). So the
        estate already separates "nobody asked" from "we could not reach it", in
        the report, which is the right place for it.

        WHAT HAS NO VOCABULARY AT ALL IS THE THIRD ONE. A stale cache does not
        fail and is not unavailable: it RETURNS AN ANSWER, so the check reports
        pass, and a pass from a list fetched six months ago is byte-identical to
        a pass from one fetched a second ago. Every certificate revoked since
        the cache was filled validates cleanly.

        COMPUTED, not asserted, by comparing state AND report faults across five
        trust-list conditions: live, offline-cache-fresh and offline-cache-stale
        come back as ONE answer. `unreachable` IS distinguishable, which is the
        positive control that makes that group a measurement rather than a
        broken comparison.

        AND THE VOCABULARY EXISTS IN THE KIT ALREADY, ONE MODULE AWAY.
        trust-results.ts exports EXPIRY_STATES = fresh, expired, never-expires
        with a temporalFaults to police them. TRUST_LIST_FRESHNESS here
        re-exports those values rather than inventing a parallel set -- the same
        shape as S11.14.c's schema gap, where the concept is modelled elsewhere
        in this library and not wired to the credential validator.

        As in S11.14.c this does NOT add a member to CHECK_OUTCOMES, which
        ProvenanceInspector.tsx and suggestions.ts read. The freshness lives
        beside the corpus, so a fixture can say how old its answer was even
        though a ValidationCheckResult cannot.

        SHIPPED src/unknown-corpus.ts + scan. Eight unknown targets over four
        states, each recorded for what makes it different: unknown-signer is
        untrusted rather than invalid; unknown-assertion must not invalidate and
        does not; unknown-algorithm can be neither verified NOR refuted, so
        `unavailable` is the only honest outcome and a `fail` would be a claim
        the validator cannot make; unknown-format and unknown-version share a
        check, the same collapse S11.14.c found. 20 spec tests; scan PASSES with
        8 negative controls. -->
  - [x] S11.14.e Add expired/not-yet-valid/revoked certificate, timestamp,
        trust-list, assertion/policy, consent/rights, and manifest retention
        variants. Evidence: lifecycle corpus.
        <!-- FIVE LIFECYCLE POSITIONS COMPUTED, AND NOTHING JOINS THE FUNCTION
        THAT KNOWS TO THE FUNCTION THAT DECIDES.

        CREDIT, AND IT IS A GOOD FUNCTION. validityAtSigning takes a notBefore,
        a notAfter, an optional timestamp and a validation instant, and returns
        FIVE positions: valid-now, valid-at-signing, expired, not-yet-valid and
        unknown-without-a-timestamp. All five are reachable and all five are
        driven here. The last two show it was thought about: a certificate whose
        notBefore has not arrived is NOT expired, and an expired certificate
        with NO VERIFIED TIMESTAMP is not expired either -- it is unknown,
        because nothing establishes when the signature was made. An unverified
        timestamp gets the same answer as none at all, which is correct and
        easily missed. CONTROL 4 of c2pa-signing.mjs drives the rescue case with
        a real certificate whose real notAfter has passed.

        THE GAP. credentialState does not take a validityAtSigning result. It
        takes a ValidationReport, on which the whole certificate lifecycle is
        ONE CheckOutcome for certificate-validity -- so a not-yet-valid
        certificate has to be encoded as a `fail`, and S11.14.b measured that a
        failing certificate-validity produces `expired`.

        MEASURED: expired, not-yet-valid AND unknown-without-a-timestamp all
        arrive as the same state, and CREDENTIAL_STATES has no not-yet-valid
        member at all, so the position could not be carried even by a caller
        that computed it.

        The estate itself says why this matters, about a different pair: revoked
        and expired are kept apart because "a lapsed certificate is a calendar
        problem whose content is probably fine and a revoked one puts everything
        that key ever signed in doubt. The two go to different people with
        different urgency, and a system that reported them alike would route the
        second one to whoever handles renewals." A NOT-YET-VALID certificate is
        a third thing again -- usually a clock wrong somewhere, quite possibly
        the validator's own -- and reporting it as expired routes a clock
        problem to whoever handles renewals for a certificate that has not
        started yet. validityAtSigning is exported from the barrel and called by
        the signing tool: it is not dead, it is UNJOINED, which is a different
        repair.

        THE POSITIVE CONTROL THAT MAKES THAT A FINDING RATHER THAN AN ARTEFACT:
        the two VALID positions collapse too, and that collapse is CORRECT. So
        the comparison is known to be able to report a collapse that is not a
        defect, rather than reporting its own filter.

        THE OTHER FIVE SUBJECTS, and they are not equally modelled. timestamp
        has its own check and feeds validityAtSigning. trust-list has a check
        and no freshness (S11.14.d). assertion-support is a check and a lapsed
        POLICY is not -- an assertion the validator can read under a policy that
        expired is supported and out of date at once. And consent/rights and
        manifest retention have NO model here at all, while the kit carries
        `withdrawn` in approval-workflows.ts and `retainUntilMs` in
        audit-snapshots.ts. THAT IS THE THIRD TIME THIS SECTION HAS FOUND THE
        CONCEPT A MODULE AWAY AND UNWIRED, after S11.14.c's schema and
        S11.14.d's freshness, and it is recorded as
        `modelled-elsewhere-in-the-kit` rather than absent because one is a wire
        and the other is design work. The scan opens each named module and
        checks it really contains the symbol claimed.

        SHIPPED src/lifecycle-corpus.ts + scan. 22 spec tests; scan PASSES with
        9 negative controls. -->
  - [x] S11.14.f Strip embedded metadata through realistic channel/transcode/
        resize/crop/recompress/rerecord and test sidecar/soft-binding/ledger
        recovery, ambiguity, and failure. Evidence: stripped corpus.
        <!-- A SOFT BINDING IS A SEARCH AND `softBindingRecovered` IS A BOOLEAN.

        CREDIT: the estate's wording about a soft binding is exactly right. When
        a credential is absent and a soft binding matched, credentialState
        returns partially-recoverable and says why -- "there is a claim about
        what this probably is and NOTHING THAT BINDS IT TO THESE BYTES." That is
        the whole difference between a hash and a perceptual match in one
        sentence, and soft-binding-alone-where-a-hard-one-is-available is a
        fault kind for using the weaker route when the stronger was on offer.

        WHAT THE FIELD NAME ALREADY TELLS YOU, AND IT DECIDES HALF THIS CELL.
        ExternalWitness.ledgerHoldsCredentialForDigest is keyed ON THE DIGEST,
        so a ledger can only answer for bytes that still hash to what was
        registered. That splits the six channels cleanly:

          channel-strip   metadata removed, pixels untouched -> the digest still
                          matches, so THE LEDGER CAN ANSWER
          transcode  \
          resize      |   different bytes, so a different digest, so THE LEDGER
          crop        |   IS USELESS -- not because anything was lost, but
          recompress  |   because the recovery route is keyed on the one thing
          re-record  /    these channels change

        FIVE OF SIX DEFEAT THE LEDGER. A corpus that tested "stripping" with a
        single strip-only fixture would find the ledger works and stop.

        THE AMBIGUITY. A perceptual match over a corpus returns nothing, exactly
        one thing, or SEVERAL, and several is strictly worse than none: a wrong
        answer displays as confidently as a right one. A boolean says "something
        matched"; it cannot say "two different credentials both claim these
        bytes", which is the case where a reader is most likely to be shown
        provenance belonging to something else.

        COMPUTED: the one-match and several-match cases produce the same state
        AND the same witness. THE POSITIVE CONTROL is that ZERO matches IS
        distinguishable -- which is exactly what a boolean can carry, and what
        makes the collapse a measurement rather than a broken comparison.

        MATCH_CARDINALITY lives beside the corpus rather than widening
        ExternalWitness, which credentialState itself and ProvenanceInspector.tsx
        read. Every fixture is still run through the estate with the boolean the
        estate has -- the third time this section has reported a gap rather than
        changing a shipped surface.

        SHIPPED src/stripped-corpus.ts + scan. 23 spec tests; scan PASSES with 9
        negative controls. Also recorded: re-record is the one channel where
        even a perceptual match should fail, because geometry, colour and noise
        all move at once; and a SIDECAR survives a strip with the credential
        PRESENT, which is why shipping both is worth the trouble. -->
  - [x] S11.14.g Assert crypto validity, trust, binding, assertion, ingredients,
        policy, and recovery states independently in API/UI/gates. Evidence:
        state matrix.
        <!-- THE CELL ASKS FOR SEVEN DIMENSIONS ASSERTED INDEPENDENTLY, AND
        EVERY MEASUREMENT IN S11.14 HAS BEEN OF THE SAME SCALAR.

          .b  ten checks reach six states, and three fail without moving it
          .c  seven of nine invalid variants separable, only two by the state
          .d  a live lookup, a fresh cache and a stale cache are one answer
          .e  five lifecycle positions arrive as two states
          .f  one perceptual match and several are one answer

        Each is the same shape: credentialState returns ONE CredentialState and
        everything a reader would act on differently arrives flattened. THAT IS
        NOT A DEFECT IN credentialState -- collapsing to one headline is what a
        headline is for, and `decidedBy` exists precisely because the author
        knew the headline was not enough. What was missing is the other half.

        CREDIT, AND THE ESTATE ALREADY NAMES THIS EXACT FAILURE.
        VALIDATION_SURFACE_FAULTS in provenance-inspector.ts (2,642 lines)
        includes one-badge-instead-of-the-matrix,
        state-rendered-without-the-check-that-decided-it,
        ingredient-availability-summarised-without-naming-the-ingredients and
        unavailable-detail-that-reads-as-an-answer. validationSurfaceFaults is
        called for real by apps/oshun/web/tools/provenance-inspector-browser.mjs,
        and that module's CREDENTIAL_CARRIAGE even includes the `ledger`
        S11.14.a found missing from EMBEDDING_MECHANISMS -- correctly, because
        carriage and embedding are different questions. RenderedCheck and
        RenderedCredentialState had NO producer anywhere in the repo.

        SHIPPED src/state-matrix.ts + scan. Seven dimensions read side by side,
        each cell reading only its own checks so a failing signature does not
        make the ingredients cell say anything. dimensionsTheHeadlineHides names,
        per failing check, which dimension a valid-and-trusted headline is
        covering up: MEASURED AT 3 OF 10. 28 spec tests; scan PASSES with 8
        negative controls.

        TWO OF THE SEVEN ARE NOT CHECKS. `policy` is answered by NOTHING -- no
        check, no outcome, no report field -- so a credential can be
        cryptographically perfect and used on terms nobody agreed to. THIRD
        SIGHTING: S11.13.d found rights and consent uncarried by the bundle
        format, S11.14.e found consent modelled in the approval workflows and
        not here. It reads `no-input-in-the-report` rather than failing, because
        a dimension nothing can answer and one answering badly are different
        repairs. `recovery` reads the ExternalWitness, and that is CORRECT:
        whether provenance can be got another way is a fact about the world
        outside the asset.

        AND ONE CHECK THE SEVEN HEADINGS DO NOT NAME. manifest-structure is not
        cryptography, trust, a binding, an assertion, an ingredient, a policy or
        a recovery -- it is whether the document could be read at all, upstream
        of every other question, and S11.14.c found it doing double duty for a
        schema violation too. Reported through check-no-dimension-reads rather
        than forced into a heading it does not belong to.

        TWO DEFECTS THIS MODULE'S OWN CHECKER FOUND IN IT ON ITS FIRST RUN.
        (1) `recovery` read `does-not-hold` for a credential that was PRESENT,
        putting a red cell beside six green ones on a perfectly good credential
        -- and worse, making every headline-hides comparison name recovery for
        every input. A DIMENSION THAT DOES NOT APPLY IS NOT A DIMENSION THAT
        FAILED; it now reads `does-not-arise`. (2) `timestamp` was read by no
        dimension until it was put where it belongs, with crypto-validity, since
        it establishes WHEN the signature was made -- which is exactly what
        validityAtSigning needs it for.

        The rendered matrix is handed to the estate's own validationSurfaceFaults
        and accepted, with a positive control that a NO-ROW surface is REFUSED
        with eleven findings. -->
- [x] S11.15 Add performance budgets for route load, interaction latency, save,
      search, compare, and virtualized scrolling. Evidence: CI trend report.
      <!-- a-g all closed. The evidence clause is the CI trend report, and
      S11.15.g measures it end to end: real repetitions asserted against the
      median with traces attached, and an arithmetic ceiling on what those
      repetitions can distinguish.

      THE SECTION'S SHAPE, SIX TIMES. Every cell found a real instrument
      measuring something adjacent to what the cell asked for, and named the
      difference rather than crediting the near-miss:

        .a  a real budget file CI enforces, at ONE device/network profile, and
            memory is a dimension no Lighthouse budget can carry
        .b  A POTENTIAL IS NOT A LATENCY -- max-potential-fid is measured on a
            page nobody interacted with; 14 time bounds in 176 specs, each a
            literal in the spec that asserts it
        .c  the save pipeline has STATES AND NO CLOCK, and a retry ceiling is a
            count: five attempts with no delay and five over four minutes are
            the same observation
        .d  A COMPLEXITY BUDGET BOUNDS WORK, NOT TIME -- the best-budgeted part
            of the estate, budgeting cost, freshness and backoff, and never the
            wait
        .e  every second in the compare module is a COORDINATE IN THE CONTENT,
            and there is no status for "too large"
        .f  renderedRowCount is RECORDED AND COMPARED TO NOTHING -- the number
            virtualization exists to keep small
        .g  three runs give two distinct statistics, and a threshold is not a
            trend

      THE DISCIPLINE THAT MADE IT HONEST: NO CELL INVENTED A NUMBER. A budget
      nobody measured is a number somebody will later read as a limit that was
      checked, which is this repository's bright line. Every register carries
      instruments and coverage; the numbers stay where the measurement is, and
      each module has a fault kind that fires if a digit appears in it. S11.15.f
      refined that guard after it fired on a cell reference and a spec filename
      -- it now requires a digit to OPEN a token, catching "200 ms" and letting
      S11.15.a through, tested both ways.

      AND THE VALUES THAT KEPT THE FINDINGS FAIR: a-load-time-potential apart
      from nothing (.b), an-order-or-count-check apart from nothing (.c), five
      bound kinds rather than two (.d), covered-as-correctness and
      recorded-and-unbounded as distinct states (.f), and
      runs-but-cannot-distinguish apart from not-met (.g). In every case
      collapsing them would have credited a real check as the wrong kind of
      check, or called a properly-guarded thing uncovered.

      Kit: 279 files / 16,275 tests green, 283 entry points, 0 violations, tsc
      clean and unfiltered; all seven scans re-run after their registrations. -->
  - [x] S11.15.a Define route navigation/load/usable timing, JS/CSS/image/font/
        data transfer, requests, main-thread/CPU/memory, and layout shift
        budgets by device/network. Evidence: route budgets.
        <!-- EVERY NUMBER IN THE BUDGET FILE IS ONE POINT IN A TWO-DIMENSIONAL
        SPACE.

        CREDIT, AND IT IS A REAL GATE THAT REALLY FAILS CI.
        apps/oshun/web/lighthouse-budget.json is not a document about budgets --
        it is the file Lighthouse CI enforces. Three path entries (/*, /search,
        /domains/nisaba), each carrying six timing budgets
        (first-contentful-paint, largest-contentful-paint, max-potential-fid,
        cumulative-layout-shift, total-blocking-time, speed-index), five
        resource-size budgets (total, script, stylesheet, image, font) and four
        request-count budgets (total, script, stylesheet, third-party). Between
        them those cover TWELVE OF THE THIRTEEN dimensions this cell names, and
        lighthouserc.cjs runs the whole thing three times per route behind a
        real authenticated session.

        THE GAP. The cell asks for budgets BY DEVICE AND NETWORK, and
        lighthouserc.cjs sets `preset: 'desktop'` with one throttle -- rttMs
        150, throughputKbps 1638.4, cpuSlowdownMultiplier 4 -- with no
        formFactor and no mobile config in either the web or the admin runner.
        So "by device/network" has no second value to be "by".

        That is NOT a criticism of the numbers, which are sensible, and the
        throttle is a real one rather than an unthrottled best case: a 4x CPU
        slowdown at roughly 1.6Mbps is a deliberately unkind desktop. It means
        something narrower and worth saying plainly -- the file states what a
        throttled desktop must achieve, and says nothing about the phone on a
        bad connection, which is the device a slow route hurts most and the one
        whose numbers would differ in KIND rather than in degree.

        AND ONE DIMENSION NO LIGHTHOUSE BUDGET CAN CARRY. The cell names MEMORY.
        resourceSizes is bytes over the wire, not bytes resident, and the budget
        file mentions memory zero times -- so a route that leaks 200MB into the
        heap passes every line of it. Marked needs-a-different-instrument with
        the instrument NAMED (a CDP heap sample from the existing Playwright
        inspect harness) rather than left looking covered.

        WHAT THIS CELL REFUSES TO DO IS INVENT NUMBERS. A budget nothing
        measures is a number somebody will later mistake for a limit that was
        checked. Every dimension is either bound to the real budget file -- with
        the value READ from it by the scan, never restated in the register -- or
        marked with the instrument it would need and NO NUMBER AT ALL, and a
        fault kind fires if a digit ever appears in the register.

        SHIPPED src/route-budgets.ts + scan. 20 spec tests; scan PASSES with 9
        negative controls, and re-reads both estate files every run, so adding a
        mobile profile or a memory budget -- both good news -- turns this
        register red. -->
  - [x] S11.15.b Define input/selection/command/panel/table/tree/graph/timeline/
        media interaction latency and dropped-frame/long-task budgets. Evidence:
        interaction budgets.
        <!-- A POTENTIAL IS NOT A LATENCY.

        CREDIT, AND THERE IS A REAL INSTRUMENT.
        apps/oshun/web/e2e-inspect/phase-7-4-long-run.spec.ts installs a genuine
        PerformanceObserver on `longtask`, drives forty frames through
        requestAnimationFrame, and bounds the elapsed time of a full-document
        scroll -- and it is honest about its own limits in a comment: "Not every
        engine exposes longtask; the wall clock still counts." That is the right
        way to carry an instrument that is not available everywhere. Long tasks
        and dropped frames ARE measurable here, on a real browser, today.

        THE FIRST HALF. The Lighthouse assertions cover interactivity with
        max-potential-fid, total-blocking-time and interactive, and ALL THREE
        ARE LOAD METRICS. max-potential-fid is an estimate derived from the
        longest task during page load, on a page NOBODY HAS INTERACTED WITH: it
        answers "if a user had clicked at the worst moment, how long might the
        page have taken to respond", which bounds the page's capacity to be slow
        rather than measuring any interaction that happened. Nine kinds of
        interaction are named by this cell and none of them is what that number
        is about.

        THE SECOND HALF, MEASURED ACROSS ALL 176 SPECS IN e2e-inspect: FOURTEEN
        toBeLessThan bounds in the entire suite. Several are not times at all --
        0.4 and 1.5 are contrast ratios, 1024 is bytes, 2 and 24 are counts --
        leaving roughly eight genuine time bounds, every one a literal written
        in the spec that asserts it, no two the same, and CAP_MS the only named
        constant among them. (A first pass counted 33 specs with a bound; the
        precise regex found 14 bounds in 12 specs, and the register is built on
        the second number.)

        That is NOT a criticism of those specs -- a spec bounding the thing it
        drives is exactly right. It means the estate has interaction ASSERTIONS
        and no interaction BUDGETS, and the difference matters three ways.
        Nothing says what the budget for a table interaction IS, so two specs
        may disagree and neither is wrong. Changing a budget means finding every
        literal. And a kind with no spec is INDISTINGUISHABLE from a kind whose
        spec is generous: unbudgeted and untested render identically.

        SHIPPED src/interaction-budgets.ts + scan. Coverage per kind across
        three instruments -- a-driven-interaction, a-load-time-potential and
        nothing -- kept as THREE because a page carrying a max-potential-fid
        budget is not unmeasured, it is measured for a different question, and
        calling that coverage is the mistake this cell exists to name. Measured:
        5 of 11 kinds driven, 3 with only a potential, 3 with nothing.

        The scan OPENS every spec the register names and confirms it CONTAINS a
        bound rather than merely existing, because a named file that is not the
        instrument it is named as is the same defect as no instrument at all. As
        in S11.15.a it invents no numbers, and a fault kind fires if a digit
        appears in any entry. 16 spec tests; scan PASSES with 8 negative
        controls. -->
  - [x] S11.15.c Define local feedback, autosave dispatch, durable ack, offline
        queue, retry, and conflict timing budgets. Evidence: save budgets.
        <!-- THE SAVE PIPELINE IS MODELLED AS A SEQUENCE OF STATES AND CARRIES
        NO CLOCK.

        CREDIT, AND THE MODELLING IS EXCELLENT. block-editors.ts S7.8.f has
        AUTOSAVE_STATES of idle, pending, in-flight, saved, failed,
        queued-offline and conflicted -- every stage this cell names, under
        different words. QUEUE_DURABILITY separates in-memory and
        session-storage from local-storage and indexed-db, and DURABLE_STORES
        names which two actually survive a reload. And
        `saved-shown-before-the-commit` is the best check in the section: it
        fires when the surface says "saved" and commitAcknowledged is false,
        which is the difference between telling a writer their work is safe and
        it being safe.

        MEASURED ON THE MODULE: ZERO time-typed fields anywhere in it.
        AutosaveObservation's eleven fields are a state, two id lists, four
        booleans, a durability, and retryAttempts and retryCeiling -- which are
        COUNTS. All eight AUTOSAVE_FAULTS are about order, presence, durability
        or a count.

        So the estate checks that the states happen in the right ORDER and
        nothing checks how long any of them TAKES. A save that reaches `saved`
        thirty seconds after the keystroke, with the commit genuinely
        acknowledged and every entry carrying its idempotency key, passes all
        eight -- and it is the failure a writer actually experiences, because
        the thing they are waiting for is the acknowledgement, not its ordering.
        `retries-without-a-ceiling` is the closest the module comes, and it is a
        ceiling on the NUMBER of attempts: five with no delay and five spread
        over four minutes are the same observation.

        WHY THE ORDER CHECK IS STILL THE MORE IMPORTANT ONE, and this cell says
        so rather than implying otherwise: a slow save loses time, and a "saved"
        shown before the commit loses work. The estate spent its checks on the
        worse failure. What follows is that the timings are missing, not that
        the modelling is wrong.

        SHIPPED src/save-budgets.ts + scan. Six stages, each tied to the
        AUTOSAVE_STATE it reaches so the register is BOUND to the estate rather
        than parallel to it; each naming the estate check that covers its
        ordering (four do) or recording that nothing does (two); each naming the
        instrument that would bound its duration. Measured: 0 of 6 stages timed.

        The scan re-counts the absence with a POSITIVE CONTROL that the same
        duration search finds many names in jobs.ts -- because a zero from a
        search nobody proved can find anything is not a measurement -- and
        checks every state and check name against the estate's own lists. 22
        spec tests; scan PASSES with 9 negative controls. Invents no numbers,
        and a fault kind fires if a digit appears in a reason or an
        instrument. -->
  - [x] S11.15.d Define search typeahead/debounce/query/server/first result/full
        result/facet/pagination and expensive query rejection budgets. Evidence:
        search budgets.
        <!-- THE SEARCH STACK BUDGETS EVERYTHING EXCEPT HOW LONG A SEARCH TAKES.

        CREDIT, AND THIS IS THE BEST-BUDGETED PART OF THE ESTATE. Unlike the
        save pipeline, the search stack bounds things, and hard things:

          complexityBudget         query-grammar.ts scores a query BEFORE
                                   running it, with per-field cost multipliers
                                   and a comment that wildcards are expensive,
                                   refusing over budget with
                                   complexity-budget-exceeded. That is this
                                   cell's expensive-query rejection,
                                   IMPLEMENTED -- and the comparison is a real
                                   comparison, not a declared field.
          staleToleranceSeconds    how out of date an index may be before a
                                   reader is told, which is the freshness
                                   question search stacks usually leave to hope.
          initial/maxDelaySeconds  a real backoff schedule for re-indexing
                                   rather than a fixed retry.

        MEASURED across query-grammar.ts, search-index-events.ts and
        search-permission-projection.ts: no `latency`, no responseMs, no
        queryMs, no firstResult -- nothing naming the elapsed time between a
        keystroke and a result. Every duration in those three modules is a
        FRESHNESS tolerance or a BACKOFF delay, both about the index, and the
        reader is waiting on the query.

        AND A COMPLEXITY BUDGET IS A BOUND ON WORK, NOT ON TIME. That is the
        whole cell. `cost > context.complexityBudget` is evaluated against a
        static score for the query SHAPE, so it promises how much work the query
        ASKS FOR -- and work is a proxy for time only when the machine is not
        busy. A query comfortably under budget, run against a shard that is
        re-indexing or serving someone else's wildcard, is slow, and nothing in
        the stack notices: the cost check passed, the index was inside its
        staleness tolerance, no retry was needed, and the reader waited anyway.
        It is a genuinely good check, and it is a check on the QUERY. The nine
        stages are about the RUN.

        SHIPPED src/search-budgets.ts + scan. FIVE bound kinds rather than two
        -- latency, cost, freshness, backoff, nothing -- because collapsing them
        would credit a cost check as a latency check. Measured: 0 of 9 stages
        have a latency budget, 3 are bounded by something else, 6 by nothing.

        first-result and full-result are separate stages because the estate's
        own TOTAL_EXACTNESS admits a `lower-bound` total, so a surface CAN show
        something early -- and whether it does is a latency trade nothing
        records.

        The scan re-measures the absence with a positive control in
        service-objectives.ts, and confirms every credited symbol is really in a
        search module. 21 spec tests; scan PASSES with 8 negative controls.
        Invents no numbers. -->
  - [x] S11.15.e Define compare load/alignment/diff/render/navigate/filter for
        text/metadata/media/3D/graph/gate and large input bounds. Evidence:
        compare budgets.
        <!-- EVERY SECOND IN THE COMPARE MODULE IS A COORDINATE IN THE CONTENT,
        NOT A DURATION OF THE WORK.

        CREDIT: comparisons.ts covers every content kind this cell names and
        then some. COMPARE_ADAPTERS is text, metadata, image, video, audio,
        stage-3d, graph and gate -- the cell's six with media split into its
        three real cases, which is the right split, because aligning two videos
        and aligning two audio tracks are different problems. And
        COMPARISON_STATUSES separates computed, partial, unsupported, withheld
        and unavailable, so a comparison that could not run says which kind of
        could-not.

        MEASURED: six duration identifiers across comparisons.ts and diff.ts,
        and every one is about the material. atSeconds is documented as "Frames
        sampled at declared SECONDS, so two rates can be aligned at all" -- a
        position in a video. durationSeconds is how long the media is.
        windowSeconds is the RMS window a waveform picture is drawn from.
        measuredAtEpochMs is WHEN a measurement was taken, not how long it took,
        and that third subject is kept separate because calling a timestamp a
        timing would credit an instant as a measurement.

        So the module is full of time and none of it is its own. A 3D stage
        comparison that takes forty seconds to align two scenes produces exactly
        the same `computed` status as one that takes forty milliseconds, and the
        seconds in the result are the scene's.

        AND THERE IS NO STATUS FOR TOO LARGE. The cell asks for large-input
        bounds. COMPARISON_STATUSES has five values and none is a refusal for
        size: unsupported is about the adapter, unavailable about reachability,
        withheld about permission, and partial about some of it being computed
        -- which is what a size-limited comparison would produce BY ACCIDENT,
        reported as though the missing part were incidental rather than
        declined.

        That matters more here than anywhere else in S11.15, because comparison
        is the one operation whose cost grows with the SQUARE of its input in
        the general case. A diff of two large documents, an alignment of two
        long videos and a graph comparison over two big graphs are the three
        places a workbench most easily becomes unusable, and the module has no
        vocabulary for declining any of them.

        SHIPPED src/compare-budgets.ts + scan. The scan EXTRACTS the duration
        identifiers from the source and fails if the register does not classify
        one, so a name added later must be classified rather than assumed -- and
        its first run caught its own extractor matching a bare `Seconds` out of
        a doc comment, which is prose about the content and not a field; it now
        requires an identifier. 21 spec tests; scan PASSES with 8 negative
        controls. Invents no numbers. -->
  - [x] S11.15.f Define virtualized initial/scroll/page/jump/filter/resort/
        selection/focus/memory/DOM count and accessibility semantic budgets.
        Evidence: scrolling budgets.
        <!-- THE VIRTUALIZATION MODEL IS ABOUT WHAT THE WINDOW MISREPRESENTS,
        NOT WHAT IT COSTS.

        CREDIT, AND THE CORRECTNESS MODEL IS EXCELLENT. data-tables.ts S7.5.h
        has the right numbered rules: "a selection that only counts rendered
        rows is a lie virtualization tells"; "aria-rowcount IS THE TOTAL, NOT
        THE WINDOW"; "print and export need the nonvirtual path". And
        VIRTUALIZATION_FAULTS covers aria-rowcount-reports-the-window,
        aria-rowindex-not-absolute, dynamic-row-heights-assumed,
        focus-lost-when-a-row-is-recycled, scroll-anchored-to-a-pixel and
        print-and-export-render-the-window. That is this cell's
        accessibility-semantic budget, its focus dimension and its selection
        dimension, ALL COVERED as CORRECTNESS -- which is the right way to cover
        them and worth more than any timing. Every one of the six is about the
        gap between the WINDOW and the TOTAL.

        MEASURED on VirtualizationObservation: eight fields, four counts and
        four booleans, ZERO durations and ZERO memory. Both re-taken by the scan
        with positive controls (57 duration names in jobs.ts, 8 byte names in
        artifact-rehash.ts).

        AND THE SHARPEST INSTANCE OF THIS SECTION'S SHAPE: renderedRowCount IS
        RECORDED AND COMPARED TO NOTHING. It is the number virtualization exists
        to keep small. It sits on the observation, it is printed into a fault
        detail as renderedRowCount/totalRowCount, and no check anywhere asks
        whether it is too big -- so a table that virtualizes correctly by every
        one of the six rules, with absolute indices and a total rowcount and
        focus surviving recycling, and renders nine thousand rows into the DOM,
        passes all of them. The window is honest about being a window, and
        nothing bounds the window.

        Memory is the second absence and the same one: S11.15.a found the route
        budgets cannot carry memory because Lighthouse measures bytes over the
        wire, and here the observation has no field for it either -- so a
        virtualized table's whole argument, that it holds a bounded amount, is
        bounded nowhere.

        SHIPPED src/scrolling-budgets.ts + scan. FIVE coverage values rather
        than two, because covered-as-correctness and recorded-and-unbounded are
        real and distinct states: calling a properly-guarded dimension uncovered
        would be wrong, and calling it budgeted would be wronger. Measured: 0
        budgeted, 5 covered as correctness, 1 recorded and unbounded, 5 with
        nothing. 23 spec tests; scan PASSES with 10 negative controls.

        A REFINEMENT THIS CELL MADE TO ITS OWN GUARD: the no-numbers check fired
        on a cell reference and a spec filename, neither of which is a budget.
        It now requires a digit to OPEN a whitespace-delimited token, which
        catches "200 ms" and "50 rows" and lets S11.15.a through -- tested both
        ways. And its memory positive control failed on the first module it
        tried, which is a control working: it said the search was unproven, and
        it was. -->
  - [x] S11.15.g Run controlled repetitions, record median/p75/p95/worst/
        variance, compare baseline/trend, fail significant regression, and
        attach traces. Evidence: CI trend report.
        <!-- THREE RUNS CANNOT PRODUCE A p75, A p95 AND A WORST THAT DIFFER.

        CREDIT: the repetitions are real. lighthouserc.cjs sets numberOfRuns and
        every route is measured that many times behind a real authenticated
        session under a fixed throttle, with Lighthouse CI asserting against the
        MEDIAN rather than the best or the last -- the right choice, and the one
        that makes the repetitions worth taking. The traces are attached too, so
        a red gate arrives with its evidence.

        THE ARITHMETIC, and it is arithmetic rather than opinion. With three
        sorted samples and nearest-rank quantiles the median is rank 2 and every
        one of p75, p95 and worst is rank 3 -- the same observation reported
        under three names. The cell asks for five statistics; three runs yield
        TWO distinct values, and a variance estimated from three points has so
        wide an interval that it cannot separate a regression from a noisy
        morning.

        DERIVED, NOT ASSERTED. distinctStatisticsAt computes this for any n, and
        runsNeededForDistinctStatistics derives that TWENTY runs are needed
        before all four read different samples -- which is also where the usual
        variance rule of thumb lands, and that rule is exposed as a named
        constant a reader can disagree with rather than buried in a comparison.
        The scan READS numberOfRuns from the runner, so the arithmetic is about
        the gate that exists, and raising it turns this register red.

        AND `temporary-public-storage` IS NOT A BASELINE. The upload target is
        named for what it is: somewhere the reports go and do not stay. Nothing
        in the workflow keeps a previous run and nothing compares against one,
        so what the gate compares against is the budget file, which is a set of
        CONSTANTS -- and "fail a significant regression" is not what happens. A
        route that was well inside its budget and has since quadrupled passes,
        because the budget is all there is to compare with. A CONSTANT CATCHES A
        ROUTE THAT BECAME BAD; A BASELINE CATCHES A ROUTE THAT IS BECOMING BAD,
        and those are different failures caught at different times.

        A THRESHOLD IS A CLAIM ABOUT ONE RUN AND A TREND IS A CLAIM ABOUT A
        SEQUENCE. That is why the two halves are related: with three runs there
        is no distribution to speak of, and with temporary storage there is no
        sequence to compare against. Fixing either alone leaves the other.

        SHIPPED src/trend-report.ts + scan. THREE requirement states rather than
        two, because a statistic that is COMPUTED AND DEGENERATE is not a
        statistic nobody computed, and the repairs differ: one is more runs, the
        other is new machinery. Measured: 4 requirements met, 3 that run and
        cannot distinguish (p75, p95, variance), 3 not met (baseline, trend,
        fail-significant-regression). 25 spec tests; scan PASSES with 8 negative
        controls. -->
- [x] S11.16 Add high-volume API and job load profiles. Evidence: limits and
      backpressure activate before resource exhaustion.
      <!-- WHAT THE ESTATE CAN DECIDE AND WHAT IT CAN ONLY DESCRIBE, SEPARATED
      SEVEN TIMES.

      The section asked for load profiles and the evidence that limits activate
      before exhaustion. The limit vocabulary turned out to be complete and
      several of the limiters really run -- .d drove the rate limiter and
      watched five actors stopped by the TENANT bucket, and .f found eight
      assertion families of which most are callable. What is absent is the other
      half of every sentence: the ARRIVALS a profile describes, and the
      MEASUREMENT that would say a limit fired before something ran out.

      .e is the finding the rest of the section leans on: the deployed estate
      emits no telemetry, so nothing observes traffic. That is why .a could not
      state a traffic proportion, why .c has no key-skew vocabulary, and why
      .g's false-success checkers can only read descriptions of a run. A limit
      that refuses is decidable from the code; that it refused BEFORE
      exhaustion is a measurement nobody takes.

      NO CELL IN THIS SECTION INVENTED A NUMBER, and each module has a fault
      kind that fires if a digit opens a token in its register. The numbers that
      do appear were computed by driving the estate: .d's admission decisions,
      .f's payload verdicts, .g's scope keys and claim outcomes.

      Seven cells, seven scans, all passing with negative controls; the section
      closes with the kit at 290 entry points and 0 violations. -->
  - [x] S11.16.a Define realistic/surge/abuse API mixes for reads/search/count/
        upload/mutation/bulk/stream/health and tenant/actor/key distributions.
        Evidence: API profiles.
        <!-- THE ESTATE HAS EVERY DIMENSION A LOAD PROFILE WOULD BE BUCKETED BY,
        AND NO PROFILE.

        CREDIT, AND THE LIMIT VOCABULARY IS COMPLETE. limits.ts has
        RATE_DIMENSIONS -- sustained-rate, burst, concurrent-requests,
        concurrent-streams, concurrent-uploads, concurrent-jobs and
        concurrent-provider-calls -- every axis a load profile would be measured
        against, including the three concurrency kinds that behave differently
        under the same request rate. And BUCKET_KINDS is actor, tenant, route,
        client-class and trust-tier, which is this cell's tenant/actor/key
        distributions as the dimensions a limit is bucketed by.

        THE GAP. A rate rule is a CEILING; a mix is a DISTRIBUTION OF ARRIVALS.
        Neither derives from the other, and the measurement shows why: the eight
        operations exhaust SIX different resources first -- connection pool,
        cpu, disk, memory, network egress, nothing in particular. So a limit
        says nothing about whether the traffic underneath it is mostly reads
        with a few uploads or the reverse, and those exhaust completely
        different things at the SAME total rate. One fills a connection pool;
        the other fills a disk.

        A NOTE ON A WORD, KEPT FRESH BY THE SCAN. capacity-plugins.ts uses
        "distribution" twenty-four times and means CONTENT DELIVERY by it --
        distribution.channel-slots, distribution.egress-mbps,
        distribution.delivery. Searching the estate for that word and concluding
        arrival distributions are modelled would be wrong.

        SHIPPED src/api-profiles.ts + scan. The SHAPE of the three mixes: which
        operations each contains, which bucket dimensions each varies, what
        distinguishes it. A SURGE SHARES THE OPERATION SET OF REALISTIC, because
        a surge is a RATE and not a shape -- one that also changed the mix would
        be two experiments at once. ABUSE IS A SHAPE AND NOT A RATE: the four
        operations whose cost is set by the caller, concentrated in few actors
        so per-tenant bucketing does not spread it, and omitting health because
        an abuser has no reason to send one.

        AND WHAT IT REFUSES. It states NO proportions. "Seventy per cent reads"
        is a measurement of real traffic and there is no telemetry in this
        repository to take it from -- writing one down would invent the thing
        the profile exists to represent, and every later run would validate a
        guess against itself. Every mix carries proportionsFrom:
        `not-yet-observed` and the register reports it, which is the finding
        rather than a defect in the register.

        Health is marked as the operation that MUST NOT BE SHED: its answer
        decides whether the instance stays in rotation, so shedding it under
        load removes the instance that was coping. 22 spec tests; scan PASSES
        with 9 negative controls. -->
  - [x] S11.16.b Define job mixes for short/long/GPU/provider/render/evaluator/
        import/export/publish/parent-child/retry/cancel and priority/tenant
        distributions. Evidence: job profiles.
        <!-- EVERY DIMENSION OF A JOB MIX IS MODELLED SOMEWHERE, AND THE JOB'S
        OWN KIND IS A STRING.

        CREDIT, AND IT IS BROADER THAN IT LOOKS. jobs.ts carries
        expectedAttemptSeconds and estimatedAttemptCost per job, so short and
        long are ONE measured axis with a threshold rather than two labels --
        and the queue already reads the duration to decide whether a deadline
        can be met. job-graphs.ts is 1,076 lines of parent and child with
        FAILURE_POLICIES (fail-parent, degrade-to-partial),
        CANCELLATION_PROPAGATIONS (cancel-children, let-children-finish),
        PARENT_OUTCOMES and REUSE_DECISIONS, all closed sets -- and it IMPORTS
        JobState from jobs.ts, so the two are joined rather than parallel.
        capacity-plugins.ts carries cpuCores, memoryGb, storageGb and gpuCount
        on RenderWorkerLoad, so a GPU job's demand is a modelled quantity rather
        than an adjective. And PRIORITY_BAND is a real validated band with a job
        outside it REFUSED.

        THE GAP. JobRecord.type is `string` and JobRecord.queue is `string`, and
        nothing in jobs.ts compares either against anything -- measured at ZERO
        comparisons against job.type, with a POSITIVE CONTROL that the same
        shape of search finds the priority comparisons that DO exist, so the
        zero is a fact rather than a broken query.

        Five of the twelve kinds therefore exist as free text. Two spellings of
        one kind are two kinds. A kind that needs a GPU worker and a kind that
        needs a provider quota are the same shape to the queue. And a mix
        expressed as proportions over kinds cannot be checked against what
        actually ran, because "what kind was that" has no closed answer. The
        priority band shows what the alternative looks like: a dimension a
        profile can be written in AND verified against.

        WHY THIS IS NOT A COMPLAINT ABOUT `string`. A queue that shipped a
        closed union of job types would have to be edited by every team that
        adds a job, and that is a worse design -- the estate is right that the
        queue should not own the catalogue. What is missing is that NOBODY ELSE
        OWNS IT EITHER. A registry the queue validates against, held wherever
        job types are actually decided, is the shape that gives a mix something
        to be a mix OVER.

        SHIPPED src/job-profiles.ts + scan. modelled-in is kept SEPARATE from
        expressibility, because `provider` is modelled in jobs.ts and is still
        only a label: where a kind lives and whether a profile can be checked
        against it are two questions. Measured: 5 of 12 kinds expressible only
        as labels, 4 modelled nowhere at all. 21 spec tests; scan PASSES with 8
        negative controls. States no proportions, for the reason S11.16.a
        gives. -->
  - [x] S11.16.c Define data/artifact sizes, hot/cold keys, pagination/search
        complexity, provider/dependency latency/error, and production-like
        capacity/cost constraints. Evidence: load manifest.
        <!-- KEY SKEW IS THE ONE DIMENSION WITH NO VOCABULARY, AND THE WORD THAT
        SOUNDS LIKE IT MEANS A CLOCK.

        CREDIT, AND EIGHT OF THE NINE DIMENSIONS ARE ALREADY MODELLED.
        PAYLOAD_LIMIT_KINDS covers sizes including the decompression RATIO --
        the dimension a zip bomb exploits and the one a size limit alone misses.
        COMPLEXITY_DIMENSIONS covers pagination and search with nine scored
        axes. PROVIDER_FAULTS is a closed fault set. COST_RESOURCE_CLASSES and
        COST_UNITS make spend expressible per resource.

        AND THE PART THAT MAKES "PRODUCTION-LIKE" REAL RATHER THAN
        ASPIRATIONAL: the deployed estate declares its own memory and cpu limits
        in infra/hetzner/docker-compose.yml -- 26 of them, mostly small
        containers -- and contention-constraints-scan.mjs ALREADY READS THAT
        FILE. The numbers are on disk and a tool already goes and gets them, so
        a manifest citing production capacity cites a fact rather than a guess.

        THE GAP. Nothing anywhere describes a key distribution: no hot key, no
        cold key, no partition, no shard. So a manifest cannot say that one
        tenant owns most of the traffic, or that one document is read by
        everybody -- the two shapes that turn a comfortable average into an
        outage.

        It matters more than it sounds, because every limit in the estate is
        BUCKETED. BUCKET_KINDS lets a limit be per-tenant or per-actor, which is
        exactly the machinery that makes skew survivable, and that machinery
        cannot be exercised by a manifest with no way to say the traffic is
        skewed. A LOAD RUN OVER UNIFORMLY DISTRIBUTED KEYS TESTS THE LIMITS AND
        NEVER TESTS THE BUCKETING.

        THE SECOND MISLEADING WORD IN S11.16. S11.16.a found "distribution" in
        capacity-plugins.ts means CONTENT DELIVERY; here every one of the eleven
        `skew` identifiers in the kit is a CLOCK or a VERSION skew. Both are
        recorded rather than left to be rediscovered, because a search returning
        hits for the right word with the wrong meaning is worse than one
        returning nothing: the first tells you to stop looking.

        SHIPPED src/load-manifest.ts + scan. The scan CLASSIFIES every skew
        identifier and checks the list is COMPLETE, so one added later must be
        READ rather than assumed. Getting there took three attempts, each
        corrected by its own control: a line-level clock word missed
        `if (skew < -1)`, where the variable IS a clock skew but the line does
        not repeat it; an identifier regex missed SkewMatrix, whose meaning
        lives in the section header "the version-skew matrix"; and the
        completeness check then found clockSkewSeconds, which nobody had
        classified. 18 spec tests; scan PASSES with 9 negative controls. -->
  - [x] S11.16.d Ramp sustained, burst, spike, soak, recovery, noisy-neighbor,
        single-tenant abuse, dependency slowdown, worker loss, and regional
        capacity scenarios sequentially. Evidence: run results.
        <!-- A LIMITER RUN PROVES THE DECISION AND NOT THE SURVIVAL.

        This cell asks for run RESULTS, so the first question is what can
        actually be run. The answer is more than nothing and much less than
        everything, and both halves are the finding.

        WHAT CAN BE RUN, AND IT IS A REAL RUN. limits.ts exports consume(rules,
        request, counter), a genuine rate decision that charges EVERY bucket a
        request falls into in a fixed order -- tenant, actor, client-class,
        trust-tier, route -- first breach winning. The estate explains the order
        in its own words: "charging only the actor is the rotation bypass -- ten
        identities in one tenant get ten times the allowance -- and charging
        only the tenant lets one actor consume everybody else's", with tenant
        first "because a tenant breach is the one another caller in the same
        tenant can do nothing about".

        WHAT THE RUN SHOWED, every number computed by that function:
          * SINGLE-TENANT ABUSE is first refused on the TENANT bucket after ten
            requests -- five actors each under the actor limit, which would have
            passed twenty if only the actor bucket were charged. The rotation
            bypass is closed, and now DEMONSTRATED rather than described.
          * NOISY NEIGHBOUR: the quiet tenant is served in full (its counter
            reaches three, all admitted) while the loud one is refused eight
            times.

        AND WHAT THE RUN FOUND THAT THIS REGISTER DID NOT PREDICT.
        RateRule.windowSeconds IS TOLD TO THE CALLER AND NEVER USED TO DECIDE.
        consume reads it on exactly ONE line, to compute retryAfterSeconds; the
        decision itself is `current + cost > permitted`, and
        Counter.current(dimension, bucket, key) takes NO INSTANT -- so nothing
        in the interface lets a counter know which window a request falls in, or
        that a window has passed.

        That is a defensible design, since windowing belongs to a store with a
        TTL. What the signature does not do is REQUIRE it, so a counter that
        looks correct and never expires produces a limiter that never forgives,
        and no type would catch it. The consequence is demonstrated: driven
        sequentially, RECOVERY ADMITS NONE OF ITS ARRIVALS, because the count
        from the burst before it only goes up. Recovery is drivable in shape and
        degenerate in result -- the same distinction S11.15.g needed.

        THE OTHER FIVE are not drivable for a reason worth naming: spike and
        soak need a CLOCK, worker-loss and regional-capacity need a TOPOLOGY,
        and dependency-slowdown needs a REAL DEPENDENCY, which S11.16.c found
        the estate has no vocabulary for.

        AND "SEQUENTIALLY" IS A REAL CONSTRAINT, not a detail. Recovery is
        defined relative to state a previous scenario left behind, so the
        sequence is ordered with every dependant after what it follows, and the
        scan ASSERTS that ordering rather than trusting it.

        SHIPPED src/load-scenarios.ts + scan. 19 spec tests, six of them real
        runs; scan PASSES with 9 negative controls. -->
  - [x] S11.16.e Measure latency/throughput/error, queue age/depth, saturation,
        DB/cache/search/blob/provider, retries/dead letters, fairness, cost,
        SLO, and data correctness. Evidence: dashboards/reports.
        <!-- EVERY MEASUREMENT IS MODELLED AND THE DEPLOYED ESTATE EMITS
        NOTHING.

        CREDIT, AND THE MEASUREMENT VOCABULARY IS THE BEST-DEVELOPED IN THE KIT.
        service-objectives.ts has SLI_CLASSES of availability, correctness,
        latency, deadline, queue-age, durability and freshness -- with QUEUE-AGE
        as its own class rather than folded into latency, which is right,
        because a request that waited and one that was slow are different
        failures with different repairs; and CORRECTNESS as its own, which is
        unusual and right, because a system can be fast, available and wrong and
        only that class notices. And it has OBSERVATION_POINTS:
        synthetic-probe, client-commit, edge, server-handler, worker,
        downstream-ack. Naming WHERE a measurement is taken is the thing most
        systems leave implicit, and it is the difference between a latency that
        includes the queue and one that does not.

        MEASURED on infra/hetzner/docker-compose.yml: 33 service blocks --
        postgres, redis, minio, the bff, web, admin, tenant-admin, a content
        service, a metis stack and nine psyche services -- and ZERO of seven
        telemetry markers (prometheus, opentelemetry, otel, statsd, metrics,
        grafana, jaeger). No exporter, no collector, no scrape target, no trace
        backend, with a positive control that the same search finds them in a
        probe that declares them.

        SO ALL SIX OBSERVATION POINTS ARE EQUALLY UNAVAILABLE. The vocabulary
        for saying where you would measure is complete, and there is nothing at
        any of those places to read. This is not a gap in the kit; it is a gap
        between the kit and the deployment, and the kit is the half that is
        finished.

        IT ALSO EXPLAINS THE REST OF S11.16. S11.16.a could not state a traffic
        proportion because nothing observes traffic. S11.16.c found no key-skew
        vocabulary, and a skew is something you notice by measuring. S11.16.d
        could drive only a pure limiter and could not show anything being
        exhausted. All three are the same absence seen from different sides:
        THE ESTATE CAN DECIDE AND CANNOT OBSERVE.

        And it is why the parent's evidence clause -- "limits and backpressure
        activate BEFORE resource exhaustion" -- cannot be established today. The
        first half is decidable, as S11.16.d showed by running it. The second
        needs a number from a running process, and no running process emits one.

        SHIPPED src/load-measurements.ts + scan. Twelve categories, each mapped
        to what describes it and to an SLI class where one fits -- with
        THROUGHPUT deliberately mapped to NONE, because an objective is about
        what a request gets and throughput is a fact about the system. 19 spec
        tests; scan PASSES with 9 negative controls. The day an exporter is
        added this register goes red and is rewritten, which is the good
        outcome. -->
  - [x] S11.16.f Assert rate/concurrency/payload/query/storage/compute/spend/
        queue limits, load shedding, circuit, reservation, and backpressure
        activate before unsafe memory/CPU/storage/provider exhaustion. Evidence:
        threshold assertions.
        <!-- "ACTIVATES BEFORE EXHAUSTION" IS A COMPARISON BETWEEN TWO NUMBERS
        IN DIFFERENT UNITS, AND NOTHING CONVERTS.

        This is the cell the parent's evidence clause is really about, and that
        sentence is a COMPARISON, so everything turns on what is on each side.

        THE LEFT SIDE IS REAL AND CHECKED, and the scan CALLS it rather than
        describing it: the rate limit admits exactly its permitted count of four
        requests, and checkPayload accepts a body inside every limit and refuses
        one over the compressed-bytes ceiling. Circuit breakers are modelled
        with states and positions, spend has reserve/settle/available over a
        budget, query cost has scoreComplexity -- most of the twelve mechanisms
        exist and several are callable, and S11.16.d already DROVE the rate
        limit through five scenarios.

        THE RIGHT SIDE IS REAL TOO, AND IT IS IN A DIFFERENT FILE AND A
        DIFFERENT UNIT. infra/hetzner/docker-compose.yml declares 26 memory
        limits: genuine numbers, in bytes, for the processes that would actually
        die.

        THE BRIDGE IS ONE QUANTITY -- how much of the resource one admitted unit
        of work consumes. The estate even has the UNIT for it: COST_UNITS
        includes memoryByteSeconds, cpuMilliseconds, gpuMilliseconds,
        storageByteSeconds and egressBytes, so "this request cost N
        memory-byte-seconds" is a sentence the kit can already say. What it
        cannot do is fill in N, because filling it in is a measurement and
        S11.16.e found nothing in the deployment emits one.

        So this cell's assertion is not merely unmade. It is UNMAKEABLE with
        today's inputs, and PRECISELY ONE MISSING QUANTITY BLOCKS IT -- which is
        a far more useful thing to report than a list of mechanisms.

        SHIPPED src/threshold-assertions.ts + scan. FOUR assertion depths rather
        than two -- nothing-to-assert, exists, activates-at-its-threshold,
        activates-before-exhaustion -- because a mechanism that activates
        correctly and cannot be related to an exhaustion point is not a
        mechanism that is missing, and the repairs differ. Measured: 0 of 12
        assertable to exhaustion, 0 of 4 bridges with a value, and TWO
        mechanisms implemented by nothing: load-shedding, and BACKPRESSURE,
        which the parent names by name. A limit REFUSES a request and
        backpressure SLOWS a producer, and nothing here slows anybody.

        Two are closest to complete and the register says why: SPEND, where the
        limit and the exhaustion point are already in the same unit -- money
        against a budget -- and PAYLOAD, whose threshold is already in bytes.
        PROVIDER is marked as having no local number at all: not a missing
        measurement but a missing relationship, since a provider's exhaustion
        happens where this estate cannot see.

        24 spec tests; scan PASSES with 10 negative controls. (The scan's own
        payload fixture was corrected by its first run: checkPayload takes
        (limits, observation), not the reverse, and the observation names
        longestArray and deepestNesting -- getting it wrong made a payload
        inside every limit come back refused on five kinds.) -->
  - [x] S11.16.g Assert no cross-tenant starvation/leak, duplicate/lost effect,
        priority inversion, false success, or unrecovered state after load.
        Evidence: post-run integrity report.
        <!-- PRIORITY INVERSION CANNOT BE VIOLATED HERE, BECAUSE NOTHING
        SELECTS.

        This is the one cell in S11.16 where most of what is asked can be
        VERIFIED rather than described, because integrity properties are
        decidable by pure functions in a way resource exhaustion is not. Two of
        the six were established by CALLING the estate in this scan, and the
        answers are good ones.

        VERIFIED BY RUNNING IT. scopeKey puts tenantId FIRST in the tuple it
        serialises, so two tenants with otherwise identical scopes -- same
        client, same operation, same version, same target -- produce different
        keys. A cross-tenant idempotency collision is therefore structurally
        impossible rather than merely unlikely, and the scan shows it by calling
        the function on three tenants and comparing the three keys, with a
        positive control that the same probe reports allDistinct false when the
        scopes really do collide. decideClaim against a completed record returns
        already-completed rather than deciding it the way it decided the first
        attempt, so a duplicated effect is refused by a function, not by a
        convention. Cross-tenant starvation was DRIVEN in S11.16.d, where the
        quiet tenant was served in full while the loud one was refused by the
        TENANT bucket. And S11.13.d's residueForOutcome reports what survived
        relative to what a run CLAIMS happened, which is what unrecovered
        means -- an import's own successful output is not residue.

        THE ONE THAT CANNOT BE VIOLATED. PRIORITY_BAND is real, JobRecord
        carries a priority, and the estate validates it against the band. What
        does not exist anywhere in jobs.ts is a function that chooses which job
        runs NEXT: no dequeue, no scheduler, no ordering, no comparator;
        claimJob claims a job somebody already named. So the property is not
        violated, and it is not upheld either, because a priority inversion is
        a statement about an ORDER and there is no order to be wrong. That is a
        different report from "we checked and it holds", and collapsing the two
        would credit an absence as a guarantee -- which is the failure this
        whole ledger is about. The register carries a value for exactly that,
        unviolatable-because-unimplemented, and a fault kind that fires if
        anybody ever marks such a property complete. The scan re-measures the
        claim from jobs.ts each run rather than trusting this comment, so the
        day something starts selecting, this cell fails and has to be rewritten
        rather than quietly continuing to say an inversion is impossible.

        FALSE SUCCESS IS THE PARTIAL ONE, AND THE ESTATE IS GOOD AT IT.
        saved-shown-before-the-commit catches a surface that says saved before
        the commit landed, and partial-import-reported-as-a-failure catches the
        opposite -- an import that really landed reported as failed. Both are
        real checks and both read a DESCRIPTION of a run. What neither can do
        is notice a false success that nothing described, which after a load run
        is the likely kind, because the thing that would describe it is the
        telemetry S11.16.e found the deployment does not emit.

        Measured: 6 properties, 3 completely established (2 of those by calling
        the estate), 1 unviolatable because nothing implements what would break
        it, 3 findings all of the same kind -- property-not-completely-
        established. 22 spec tests; scan PASSES with 9 negative controls,
        including one that every fault kind is reachable from some input, after
        S11.16.b shipped a kind that could never fire. -->
- [x] S11.17 Add accessibility parity tests for canvas/table, graph/list,
      timeline/list, waveform/transcript, and 3D/metadata views. Evidence:
      semantic-equivalence assertions.
      <!-- a-g all closed. THE SECTION'S SHAPE: PARITY IS A TWO-SIDED CLAIM, AND
      THE ESTATE MAKES IT WELL FOR ONE PAIR AND ONE-SIDEDLY FOR THE REST.

      .a measured it: of fifteen facets, five support an equivalence assertion
      and two of those only for graphs, because the one complete parity audit in
      the repository takes nodeColumns and edgeColumns. Seven support presence
      and nothing stronger, and one -- range -- is described by neither
      rendering.

      EVERY CELL FOUND THE SAME KIND OF THING AND A DIFFERENT INSTANCE OF IT: a
      fact that exists, is correct, and does not reach the reader who cannot see
      the picture. .b: the estate's spatial doctrine is right about a computed
      layout and inverted on an authored canvas, and compareGraphs hands a
      non-visual reader "1 difference (0 to the work, 1 to its presentation)"
      for a box somebody dragged across the page. .c: pathListed and
      impactListed are BOOLEANS beside twenty-three fact-by-fact comparisons,
      and the group tree is declared, cycle-checked and flattened to one label
      on BOTH sides. .d: TimelineDocument.markers is readonly number[] while
      three keyboard commands move a reader between them -- navigable and
      unreadable -- and every "readonly value" in 3,236 lines is a boolean. .e:
      integratedLoudness computes seventeen block values and returns one number,
      so loud-then-quiet and quiet-then-loud measure 0.0002 LU apart. .f: a
      local 4x4 is correct, complete and unreadable, and variantSelections holds
      the choice and not the options.

      THE VOCABULARY THAT KEPT IT FAIR. Each cell needed a third or fourth
      state, and collapsing any of them would have credited an absence as a
      guarantee or called a careful thing broken: above-both-renderings (a value
      decided before either view, where a parity test would be theatre),
      as-prose-inside-a-string and outside-the-signature (present-but-unassertable,
      and a missing argument rather than a missing field), asserted-as-a-boolean
      (a list was rendered, not that it agrees), computed-and-discarded (correct
      arithmetic, missing return value), authored-and-unreadable, and
      unobserved-because-no-reader-runs-here.

      .g CLOSED IT BY MUTATION: 33 visual-only facts and actions removed or
      altered across all five pairs, 33 killed, over a harness that REFUSES to
      report when the honest baseline is already noisy. And it says plainly that
      the screen-reader half was not performed -- measured on the host, not read
      from a register -- rather than marking a journey nobody listened to.

      Seven cells, seven scans, all passing with negative controls; the section
      closes with the kit at 297 entry points and 0 violations. -->
  - [x] S11.17.a Define a shared parity contract for entity IDs, values/units,
        relationships/order/hierarchy/time, status, selection/focus/filter/
        range, operations, errors, and protected/inaccessible state. Evidence:
        parity schema.
        <!-- PARITY IS A TWO-SIDED CLAIM, AND ALMOST EVERY PIECE THE ESTATE HAS
        DESCRIBES ONE SIDE.

        An equivalence assertion needs both sides in one place, so a test can
        compare them and fail when they diverge. A record that describes only
        the alternative supports the weaker claim that a row EXISTS, and no
        amount of care makes it support the stronger one, because there is
        nothing to compare the row against.

        CREDIT FIRST, BECAUSE THREE PIECES ARE GENUINELY TWO-SIDED AND ONE IS
        EXCELLENT. graph-views.ts has a complete parity audit from S7.10.h:
        every visual encoding declares the fact it carries and the
        semanticColumn stating the same fact, every action declares its
        canvasGesture and the semanticControl that performs it, and parityAudit
        walks the encodings ACTUALLY IN USE rather than the ones somebody
        remembered to declare -- reporting graphOnlyFacts and graphOnlyActions
        with emptiness as the acceptance. That is exactly what this section
        asks for. It answers for ONE of the five pairs, and the reason is in the
        signature: two of its four required inputs are nodeColumns and
        edgeColumns, and EncodingDeclaration.encoding is a closed union of
        twelve values named for node-link diagrams, three of them declared edge
        encodings. A waveform has no edges. That is not a criticism -- it was
        written for graphs and it is right about graphs -- it is why the cell
        asks for a SHARED contract rather than pointing at the one that exists.

        domain-views.ts adds two more. ViewCommand carries pointerGesture and
        formAlternative in the same record, so "this can only be done by
        dragging" is decidable. And reconcileSelection compares a selection held
        in one view against the ids ANOTHER view can address, splitting them
        into carried, withheld and dropped with not-representable-in-this-view
        as a first-class reason. It is the one piece of parity machinery in the
        repository that is not shaped like the view it was written for: it takes
        ids and reasons. The scan calls it with timeline, waveform and
        scene-graph input and it answers unchanged, three times.

        THE ALTERNATIVE IS REQUIRED, AND IT CARRIES CONTENT RATHER THAN FACTS.
        plugins.ts makes a non-visual rendering mandatory and the comment is
        right that a projection whose only rendering is visual has no export, no
        API and no screen-reader path. But a SemanticNode is four fields over
        eleven document kinds. Order and hierarchy survive that projection
        honestly, because arrays are ordered and children nest. An entity's
        identity, a number's unit, a status and an instant all arrive as words
        inside text -- which is not absence but a third state: present, read
        aloud, and impossible to assert on, compare, export or filter. The
        estate has the most careful time model it could have, in flicks and
        rational rates and drop-frame timecode, and a SemanticNode has no time
        field, so the instant reaches the reader as characters.

        AND FOUR FACETS ARE OUTSIDE THE SIGNATURE. toSemanticNodes is a pure
        function of the model; selection, focus, filter and range are states of
        the VIEW. Recording those as "nothing carries it" would invite the wrong
        repair -- a field in the node tree -- when the repair is a second
        argument. TWO NEED NO PARITY AT ALL: ProjectionState is loading / ready /
        empty / unauthorized / not-configured / failed and is decided BEFORE
        either rendering runs, so there are not two descriptions to keep in step,
        and a contract demanding a test there would be asking somebody to prove
        that one value equals itself. Four sidedness values, four claims: a
        boolean would have merged the equivalence you must test with the one
        that cannot break.

        WHAT THIS CELL ADDS BEYOND THE REGISTER, AND THE LIVE DEFECT IT FOUND.
        SemanticNode states three rules about itself in its own comments -- text
        is never markup, text is never a translation key, a text alternative is
        required on a figure and refused elsewhere -- and NOTHING IN THE
        REPOSITORY CHECKED ANY OF THEM. semanticNodeFaults is the first checker,
        and the scan calls all four shipped implementations of
        NonvisualAlternative rather than reading them: TWO OF THE FOUR LEAK A
        TRANSLATION KEY. incident-console.archetype.ts emits
        `${entry.at} ${entry.labelKey}` as the text of a list item, so a reader
        on the alternative hears "2026-08-16T00:00:00.000Z
        timeline.incident.mitigationIneffective" where the visual timeline shows
        a local time and a translated sentence; story-desk's inspector emits
        model.headlineKey as a term. Present, required, shipped, and not
        equivalent -- which is this cell's whole point in one line.

        The compounds are SPLIT: the cell writes two items where there are
        eight, and they do not share an answer -- order survives a document
        projection and time does not, selection is reconciled by a real function
        and range is described nowhere. Measured: 15 facets, 5 comparable, 2
        equal by construction, 7 presence-only, 1 (range) described by neither
        side; 9 carried across all five pairs, 2 comparable for graphs alone.
        31 spec tests; scan PASSES with 12 negative controls, including one that
        the key checker stays SILENT on ordinary prose. (Its first run caught my
        own field regex counting `readonly SemanticNode[]` as a fifth field of
        SemanticNode.) -->
  - [x] S11.17.b Assert canvas objects/spatial facts/selection/edit operations
        are present and synchronized in a navigable table/form alternative.
        Evidence: canvas/table fixtures.
        <!-- A GRAPH'S POSITION IS COMPUTED AND MEANS NOTHING. A CANVAS'S
        POSITION IS AUTHORED AND IS THE CONTENT. THE ESTATE HAS ONE DOCTRINE AND
        IT IS THE GRAPH'S.

        The kit states it three times and is right every time about the surface
        it was written for. spatialMeaningFaults reports
        position-mapped-to-a-coordinate-column when a table answers a positional
        encoding with an x column -- "it satisfies the parity check while stating
        nothing a reader can use". ComparableGraphNode.position is commented
        "Where the layout put it. Never compared". compareGraphs returns
        layoutOnly when the only thing that moved was the drawing. All three
        rest on S7.10's finding that a coordinate states nothing, because a
        layout run put it there.

        Every one becomes false the moment a PERSON rather than a simulation
        decides where the object goes. On an authored board, moving a box is the
        edit, overlapping two shapes is the meaning, and a table that omits the
        coordinates has dropped the content.

        THE SCAN SHOWS BOTH BY RUNNING THEM, each beside its graph-shaped twin,
        so the same function is seen being right and being wrong on the same
        day. The same spatialMeaningFaults call is correct for a layout and
        wrong for a board. And compareGraphs classifies a box dragged from
        (60,40) to (700,500) as significance=presentation, handing a non-visual
        reader the sentence "1 difference (0 to the work, 1 to its
        presentation)." On an authored canvas that sentence is false, and it is
        the sentence the reader who cannot see the board is given.

        THE REPAIR IS ONE BIT OF PROVENANCE, NOT A NEW ALGORITHM. POSITION_ORIGINS
        says where the coordinates came from, and that single fact decides which
        of the estate's existing rules applies. It sits on the OBJECT rather
        than the surface, because a real board mixes them: a person places the
        boxes and a router lays out the connectors between them, and the
        connector's coordinates state nothing while the box's are the whole
        point. The fixture is deliberately mixed for that reason, and one table
        carries both.

        PRESENT AND SYNCHRONIZED ARE TWO QUESTIONS, AND THE SECOND SORTS ITSELF
        BY WHERE THE STATE LIVES. An object's existence, its position, its parts
        and an applied edit are in the MODEL both renderings project, so they
        cannot disagree -- synchrony is free, and a test for it would be asking
        whether one value equals itself. A SELECTION is held by each RENDERING
        and nothing in the estate joins them, so it is the one subject that can
        drift silently, and the only one a test earns its keep on.
        reconcileSelection is exactly the missing joint and it already exists:
        the canvas selection is compared against the ROWS the table can address,
        not against the table's own selection, which would have asked a
        different question and returned an answer that looked identical.

        THE ANCHOR NAMES WHAT AND NEVER WHERE. StableAnchor has a canvas kind
        with objectId and an optional part, and resolveCanvas is careful with
        it: a part that dies while its object survives is deleted rather than
        coarsened up to the object, because rebasing "this handle" to "this
        shape" is a silent move dressed as a rescue. The anchor has no field for
        a place, so a comment pinned to a canvas object names the shape and can
        never name the region -- verified by parsing one and listing its fields.

        The table carries the numbers AND a sentence: two coordinates are the
        fact, "top left" is the fact made usable, and a table with one of them
        fails differently from a table with neither. The description is a named
        approximation -- thirds on each axis, by the object's centre -- written
        down with the numbers travelling beside it, and there is a check that
        the sentence agrees with its own coordinates.

        Measured: 11 parity faults over 4 subjects, 1 subject whose synchrony
        can fail. 27 spec tests; scan PASSES with 14 negative controls. (Its own
        NUL check caught 2 NUL bytes in the module I had just written -- the
        fifth occurrence of that trap this initiative -- and the RevisionRef it
        builds was refused until it carried all seven declared fields.) -->
  - [x] S11.17.c Assert graph nodes/edges/properties/groups/paths/impact and
        relationship operations are present in list/table/tree alternatives.
        Evidence: graph/list fixtures.
        <!-- A BOOLEAN SAYS A LIST EXISTS. IT DOES NOT SAY THE LIST AGREES WITH
        THE PICTURE.

        THIS IS THE ONE PAIR OF THE FIVE THE ESTATE ALREADY ANSWERS, AND IT
        ANSWERS MOST OF IT VERY WELL. Saying that plainly is half this cell's
        job. nodeParityFaults compares a drawn node with its row fact by fact --
        label, type, status, group, every annotation, both degrees, the details
        target -- over twelve fault kinds, and refuses a degree that counts a
        node the view does not hold. edgeParityFaults does the same over eleven,
        including a direction the arrowhead asserts and the list contradicts,
        and an endpoint named by id where the line shows a label.
        relationshipCommandFaults covers ten. traversalFaults asks whether a
        node can be reached by keyboard at all. synchronizationFaults keeps five
        surfaces in step. The scan DRIVES the first two rather than citing them:
        silent on an honest pair, and loud when the table says blocked where the
        picture says ready, or the list contradicts an arrowhead.

        TWO OF THE SEVEN SUBJECTS ARE ANSWERED BY ONE BIT EACH.
        ViewportObservation has pathListed and impactListed, and viewportFaults
        reports path-shown-only-as-a-line and impact-shown-only-as-a-shade when
        either is false. Both checks are right and both are booleans: they say a
        list was RENDERED, not that the list is the path. The estate's own
        detail string names the comparison it is not making -- "the hops, their
        order and their edge types are the answer; the line is the picture of
        it". The scan puts ONE path, listed in the wrong order, to both: 0
        findings from viewportFaults with pathListed true, 4 from
        pathParityFaults, same path, same run. A path through the wrong edges
        between the right nodes passes the boolean too, and so does an impact
        set that omits the one thing that breaks.

        AND THE GROUP TREE IS DECLARED, CHECKED FOR CYCLES, AND FLATTENED TO ONE
        LABEL ON BOTH SIDES OF THE GLASS. GraphViewContract.groupParents is a
        genuine tree with a group-cycle fault refusing a loop. Both renderings
        then carry groupLabel: string | null, one flat string. So the picture
        and the table AGREE about the group, neither states the hierarchy, and
        group-differs-between-the-picture-and-the-table passes on two renderings
        that are equally silent -- which is exactly why the existing check
        cannot find it. Run against a three-deep tree stated as flat labels, the
        new comparison reports both nested groups. The cell asks for
        "list/table/tree alternatives" and GRAPH_SURFACES is graph, node-table,
        edge-table, inspector, deep-link. There is no tree, for the one subject
        whose shape is a tree.

        WHAT THIS CELL ADDS is the three comparisons the estate's own comments
        describe -- the hops and their edge types, the impact set and whether it
        is offered before the change, and the ancestry each rendering states --
        and it replaces none of the estate's checks. There is a register fault,
        deep-coverage-that-also-adds-something-here, that fires if it ever
        duplicates one, because two checkers over the same graph is how they end
        up disagreeing.

        THE REGISTER NAMES ITS ESTATE FAULT KINDS RATHER THAN COUNTING THEM. Its
        first scan run caught the reason: group claimed 1 and the derivation
        said 12, because a count was a number about the whole list and the claim
        was about the subject. Now every attributed kind is checked for
        membership in the estate list it came from, no kind may be claimed
        twice, and the attributions must PARTITION the node and edge lists
        exactly -- so a fault kind cannot be quietly dropped from the register.
        The same run also refused the scan's own fixture with
        unknown-neighbourhood-reported-as-zero, correctly: the nodes had no
        neighbourhoodLoaded and the rows stated a degree anyway.

        Measured: 7 subjects, 4 compared fact by fact by the estate, 1 as one
        label, 2 as a boolean, 3 deepened here; 12 node and 11 edge kinds
        partitioned, 10 relationship-command kinds, 2 of 10 viewport kinds.
        35 spec tests; scan PASSES with 12 negative controls, four of which
        check that the new comparisons stay SILENT on renderings that match. -->
  - [x] S11.17.d Assert timeline tracks/events/start/end/duration/value/status/
        overlap and trim/move/split/zoom/seek operations are present in semantic
        lists/forms. Evidence: timeline/list fixtures.
        <!-- THE TIMELINE MODELS SPANS WITH LABELS. TWO OF ITS OWN SIX TRACK
        KINDS ARE NEITHER A SPAN NOR A LABEL.

        CREDIT FIRST, AND IT IS OWED IN QUANTITY. timelines.ts is 3,236 lines
        and timelineParityFaults is the best parity check in the repository. It
        compares a drawn clip with its row over thirteen fault kinds, by real
        arithmetic rather than string equality: the drawn x against
        timeToPixels(start) within one device pixel, the drawn width against the
        stated duration, the end timecode against start plus duration in the
        contract's own frame rate. It knows the asymmetry runs one way -- an
        item scrolled off the canvas is legitimately undrawn, and an item INSIDE
        the visible window that the table lists and the canvas omits is
        graph-only information in the other direction. It has
        row-with-no-operations-and-no-reason, which is the disabled-field rule
        one level up: a genuinely read-only track offering nothing is fine, and
        offering nothing without saying why renders a protected item identically
        to a broken control. The scan DRIVES it: silent on an honest clip, and
        reporting drawn-position-disagrees-with-the-stated-time when the
        rectangle moves 40px from its timecode. Every fact this cell names for a
        CLIP -- track, start, end, duration, label, status, overlap, and the
        trim/move/split operations -- is already compared by something that
        runs. Zoom is bounded by clampZoom and pointerPrecision; seek is a
        playhead key in TIMELINE_LINK_KEYS.

        AND THEN THERE IS TRACK_KINDS, WHICH HAS SIX MEMBERS.

        A MARKER IS AN INSTANT WITH A MEANING, HELD AS A BARE NUMBER.
        TimelineDocument.markers is readonly number[]. Not items: numbers. So a
        marker has no id, no label, no track, no row and no operation, and the
        clip comparison -- which walks drawn clips against rows -- cannot see
        one. Meanwhile TIMELINE_COMMANDS has add-marker, next-marker and
        previous-marker with real key bindings, and SNAP_TARGETS has marker. A
        reader can add a marker, jump to the next one, and snap a clip edge to
        it, and can never be told which markers exist or what any of them means.
        Navigable and unreadable. Two markers at one instant are two equal
        numbers, so nothing can name one of them, move one of them, or say which
        one a comment is about.

        AN AUTOMATION POINT IS A VALUE AT AN INSTANT, AND THERE IS NO VALUE.
        automation is a declared track kind with its own native unit -- samples,
        because a volume ramp is authored against audio and not against frames,
        which the module is right about. TimelineItem carries start, duration,
        sourceIn, speed, three flags, a link group and a lane. Every "readonly
        value" in the file is the boolean on set-enabled, set-muted and
        set-locked; the scan counts them (3) and asserts none is numeric.
        TIMELINE_OPERATIONS has thirteen members and none changes a number. So
        the one thing an automation track exists to carry has no field, no
        column and no operation.

        WHY THIS IS NOT A COMPLAINT ABOUT A MISSING FEATURE. The cell asks for
        "value" among the facts a list must state. A span-shaped row can hold a
        marker only by inventing a duration, and an automation point only by
        dropping the number -- and BOTH would pass a check comparing what the
        picture draws with what the table lists, because neither rendering has
        the fact. That is the same shape S11.17.c found in the group tree: the
        two agree, and both are silent.

        Measured: 8 facts, 1 (value) with no field anywhere, 2 (track, event)
        answered for spans only, 3 shapes across 6 track kinds; 12 new fault
        kinds over markers and automation. 23 spec tests; scan PASSES with 12
        negative controls, five of which check the new comparisons stay SILENT
        on a marker or a point stated in full. (Its first run was refused by the
        estate's own comparison for a contract missing originFrames, which made
        the estate's expected timecode NaN -- the check working on the scan's
        fixture. A second run caught a value regex reading past a field, because
        the operation-request union members end in a brace, not a semicolon.) -->
  - [x] S11.17.e Assert waveform/audio duration/channels/time/range/markers/
        loudness/events/alerts and navigation are present in transcript/data/
        event views. Evidence: waveform/transcript fixtures.
        <!-- A RANGE IS TWO INSTANTS, AND EVERY TIME ANCHOR IN THE ESTATE HOLDS
        ONE.

        StableAnchor has eight kinds and two are about position. temporal is
        {timeBase, position} -- ONE instant, in seconds or frames, with the time
        base carried beside the number so a frame position cannot be read
        against the wrong rate. range is {blockId, start, end} -- two offsets
        INSIDE A BLOCK, which is a span of text and not a span of time. So "from
        1:02 to 1:09", the selection a waveform reader makes constantly and the
        unit a transcript view exists to align with, cannot be named by any
        anchor in the repository: not commented on, not carried across a
        revision, not handed to another surface. S11.17.a recorded range as the
        one facet neither rendering describes; this is the structural reason,
        read from both types in the scan.

        AND THE LOUDNESS SERIES IS COMPUTED AND DISCARDED.

        integratedLoudness is real BS.1770-4: K-weighting through two biquads,
        400ms blocks at 75% overlap, an absolute gate at -70 LUFS and a relative
        gate at -10 LU, the channel weights and the -0.691 dB offset. It is
        careful enough to return -Infinity for digital silence, "which is the
        true value", and to flag measuredAtReferenceRate when the coefficients
        were applied at a rate they were not derived for. It computes a block
        loudness for every 400ms window, and returns blockCount, gatedBlockCount
        and one number.

        THE SCAN RUNS IT TWICE. Two seconds of a 1 kHz tone, loud then quiet:
        -9.7287 LUFS. The same two seconds quiet then loud: -9.7289. 0.0002 LU
        apart -- not a distinction anybody can act on. The measurement is right
        both times and it is the same measurement, because integrated loudness
        is a property of the programme rather than of any moment in it. A
        waveform shows a reader WHERE the loud part is. The seventeen blocks
        that would say so were computed inside the function and are a local
        variable. True peak is the same shape: both arrangements measure -6.02
        dBTP, so a clip at the start and a clip at the end are one reading.

        computed-and-discarded is therefore a state of its own in this register,
        distinct from not-modelled. The arithmetic exists and is correct; what
        is missing is a return value. That is a different repair and a much
        smaller one, and calling it "no loudness data" would hide both facts.
        momentaryLoudnessSeries supplies it, built from the estate's own
        exported kWeight, channel weights, block length and offset rather than a
        second filter, so the two cannot drift in their arithmetic. It is
        BS.1770 MOMENTARY loudness, ungated, named as such: gating is how you
        get one number for a programme, and applying it per block would discard
        exactly the quiet stretches a reader wants located. Run over the same
        fixtures it puts the loud part at 0.1s and at 1.1s respectively.

        CREDIT, BECAUSE THE ESTATE ALREADY NAMES THIS FAILURE. Of its eight
        media comparison faults, one is audition-with-no-text-alternative --
        "listening to two takes is the whole comparison for a hearing reader and
        none of it for" anybody else -- and another is
        channel-counts-differ-with-no-downmix-policy. Channels are declared by
        layout and weighted by name; an LFE at weight zero really is ignored,
        which the spec checks. The audio model is one of the strongest things in
        the kit. What it has no way to say is WHEN.

        Measured: 9 facts -- 2 computed and discarded (loudness, event), 4 not
        modelled (range, marker, alert, navigation), 1 derivable and unnamed
        (duration, a division away from fields that exist), 2 computed and
        returned. 25 spec tests; scan PASSES with 12 negative controls,
        including one that the loudness check stays SILENT when a row states the
        truth, and one that a recording shorter than a block yields no blocks
        rather than a fabricated one. -->
  - [x] S11.17.f Assert 3D hierarchy/objects/transforms/geometry/materials/
        animation/variants/refs/validation and selection/navigation are present
        in metadata/tree/table views. Evidence: 3D/metadata fixtures.
        <!-- A MATRIX IS SIXTEEN NUMBERS. A READER NEEDS A PLACE.

        CREDIT FIRST, AND THE MODEL IS A REAL USD STAGE. StagePrim carries a
        path, a type name, a local transform, mesh topology, a material binding,
        variant selections, references, the composition arc that supplied the
        strongest opinion, a skeleton joint count and free attributes. Stage
        carries the up axis, metres per unit and time codes per second.
        COMPOSITION_ARCS is the real strength order. And
        STAGE_COMPARISON_FAULTS is seven kinds of hard-won care: transforms
        across a differing up axis are refused rather than attempted, geometry
        across a differing unit scale likewise, a variant switch is not reported
        as a geometry change, a reference retarget is not reported as a local
        edit, and identical topology with reversed winding is caught -- the bug
        that renders as a black object and compares as no change at all.

        Transform.matrix is commented "Row-major 4x4. Local to the parent, never
        world", and world-transform-change-attributed-to-the-prim exists because
        the module knows exactly what that costs. It is the RIGHT choice: a
        local matrix is the authored fact, and a world matrix is a derived one
        that changes when an ancestor moves.

        AND IT MEANS A TABLE ROW CANNOT SAY WHERE ANYTHING IS. Sixteen numbers,
        relative to a parent the row does not name, is a complete description
        that no reader can act on -- and the scan prints one to make the point.
        The world position is a product of the chain: derivable from what the
        stage already holds, stated nowhere, and therefore recomputed by every
        surface that wants it, in whichever convention that surface guessed.
        worldTransform composes it, with the convention STATED once and PROVED
        rather than assumed -- a three-deep fixture (world at x=10, set at x=1,
        chair at y=2) whose answer a person can predict: [11, 2, 0]. It refuses
        rather than substituting an identity for a missing ancestor, because
        that would place an object where nobody authored it. readTransform gives
        a place and a size, takes scale as the length of each basis row, and
        REFUSES to name a rotation: from a general matrix it is ambiguous
        without an agreed order, and an angle under the wrong convention is
        worse than no angle. It reports orthogonal=false when shear is present,
        so a caller never has to guess whether the numbers mean what they
        usually mean.

        A SELECTION IS NOT A CHOICE UNTIL YOU KNOW WHAT ELSE WAS AVAILABLE.
        variantSelections is Record<string, string> -- the set name and the
        selected variant -- and nothing anywhere holds the OPTIONS. The scan
        reads StagePrim's ten fields and asserts none of them is one. So a
        metadata view can say "lod = high" and can never say "lod is one of
        high, medium, low": a reader on the table cannot tell a deliberate
        choice from the only possibility, and cannot discover that a variant
        exists to be switched. In the viewport that information is a dropdown.

        AND ANIMATION IS A RATE AND A JOINT COUNT. timeCodesPerSecond says how
        fast time runs and skeletonJointCount says how many joints there are;
        attributes are static strings and no field anywhere is a time sample.
        Nothing distinguishes a rigged character standing still from one that
        moves, so a metadata view has nothing to list under animation but a
        number of joints.

        Measured: 11 subjects -- 7 stated and readable, 1 authored and
        unreadable (transform), 2 derivable and unstated (variant, validation --
        seven fault kinds judge two stages against each other and none judges
        one on its own), 1 not held at all (animation). 28 spec tests; scan
        PASSES with 11 negative controls, three of which check the new
        comparisons stay SILENT on a view that states everything. A register of
        path faults with no producer was written and then deleted rather than
        shipped. -->
  - [x] S11.17.g Mutate each visual-only fact/action and require parity test
        failure; test keyboard/screen-reader full journeys on alternatives.
        Evidence: semantic mutation report.
        <!-- A CHECK THAT HAS NEVER FAILED IS A CHECK NOBODY HAS TESTED.

        S11.17.b through .f each built a comparison and asserted it stays SILENT
        on an honest pair. Silence is the cheap half. This cell takes each
        visual-only fact and each visual-only action across all five view pairs,
        removes or alters it in the alternative, and requires the comparison to
        report. 33 mutations, 33 killed, printed one line each with the fault
        that caught it -- and a mutant that SURVIVES is a hole named rather than
        counted.

        THE GUARD THAT MAKES A KILL MEAN ANYTHING. Before any mutation runs, the
        unmutated alternative is checked and must produce nothing. Without that
        a baseline that already reports would kill every mutant for free, and
        the report would read as complete coverage of a suite that never looked
        at the mutation at all. runMutations THROWS NoisyBaselineError rather
        than returning outcomes somebody would read, and names the noisy
        findings so the fixture can be repaired. Both directions are
        positive-controlled: a deliberately blind checker produces a survivor
        with caughtBy null, and a noisy baseline is refused outright. killRate
        is exercised at 0.5 as well as 1, so a hundred percent is a measurement
        rather than the only value the function returns.

        Three mutation kinds are kept apart because they fail differently: a
        dropped fact is a missing row or column, an altered one is a row that
        disagrees, and a suite can catch one and not the other. All three are
        exercised, and every one of the five pairs is covered.

        THE KEYBOARD HALF IS DECIDABLE HERE AND IS CHECKED. An alternative that
        states a fact nobody can move focus to has not made it available; an
        operation with no keyed route is a pointer gesture wearing a button; and
        an action that does not declare where focus lands drops a keyboard
        reader at the top of the document.

        THE SCREEN-READER HALF WAS NOT PERFORMED, AND THAT IS THE HONEST REPORT.
        at-reconciliation.ts declares six readers and every one of them
        runnableOnThisHost: false, with reasons that are facts about platforms
        rather than excuses -- NVDA, JAWS and Narrator are Windows, VoiceOver is
        Apple and driven through its own scripting, TalkBack needs a device, and
        Orca needs a desktop session and a speech service a headless host does
        not have. The scan does not trust that register: it MEASURES the host,
        looking for five reader binaries on PATH, and fails if one is present
        while the register says none runs. Result: 0 of 5 found, 0 of 6 runnable,
        AT_MATRIX 0 of 36 cells observed, and this cell changes none of them.

        Recording that as unobserved rather than as a pass is the whole reason
        that module exists. Marking this cell without saying so would be the
        exact defect this section is about: a claim that a reader was served,
        made by somebody who never listened.

        18 spec tests; scan PASSES with 8 negative controls.
- [x] S11.18 Add an adversarial stub scan tailored to provider, score, fixture,
      and success-state fabrication. Evidence: reviewed zero-blocker report.
  - [x] S11.18.a Define executable scan sources for production code/config,
        adapters, routes, UI, generated code, feature flags, fixtures imports,
        tests excluded by rule, and built bundles. Evidence: scan scope.
        <!-- A SCAN'S SCOPE IS THE SET OF FILES IT CAN OPEN. THIS ONE OPENS
        THIRTEEN SUFFIXES, AND THE LANGUAGE THE TOOLING IS WRITTEN IN IS NOT
        AMONG THEM.

        CREDIT FIRST, BECAUSE THIS SCAN IS REAL AND RUNS.
        scripts/stub-indicator-scan.sh is wired into the pre-commit hook, a CI
        job over the whole PR diff, a separate silent-random job, a nightly
        trend run and a per-PR comment. It has three modes, an inline allow-list
        that carries a stated reason (// stub:legitimate <why>), and it excludes
        tests BY RULE rather than by accident, with the argument written down:
        fixtures and assertions are expected to contain stub vocabulary. It even
        handles pure renames -- an R100 move carries no new markers, and without
        that carve-out a directory move re-litigates every pre-existing hit,
        which it did once and is recorded in the script. It caught this session
        twice, on the word "would" in two doc comments.

        THE SCOPE IS AN EXTENSION LIST, AND THAT IS THE FINDING. The filter is a
        case over suffixes: nine are languages the product is written in and
        four are V3 asset JSON. Everything else in the repository is invisible.
        Measured over the whole tracked tree -- 136,871 files -- 66,488 are in
        scope, and of the rest 5,172 carry code somebody runs: 3,638 .mjs, 526
        .js, 269 .cs, 223 .tf, 202 .sql, 197 .sh, 71 .java, 38 .cjs, 8 .go.

        .mjs is what every verification tool in this initiative is written in,
        every generator under scripts/, and every scanner under tools/ --
        INCLUDING THE SCANNER THAT PRINTS THAT NUMBER, which the run checks by
        name and reports out of scope. A fabricated result inside the machinery
        that produces evidence would not be seen by the scan that exists to find
        fabricated results. Configuration is unreachable for the same reason:
        only four dotted V3 suffixes reach the scan out of everything ending in
        .json, so a value fabricated in configuration is read by the product and
        by nothing else.

        AND THREE CLASSES ARE EXCLUDED ON PURPOSE. Generated code, built bundles
        and anything under dist/, build/, .next/ and target/ are dropped by path
        before the suffix filter. That is defensible for a pre-commit hook over
        files nobody commits, and it is not defensible for the question this
        cell asks, because the bundle is the only artifact that actually runs in
        front of a person: a stub introduced by a transform, a define or a
        bundler replacement exists in exactly one file, and it is one this scan
        never opens.

        excluded-by-a-stated-rule is kept distinct from
        unreachable-no-suffix-matches because an exclusion somebody wrote down
        is a decision that can be argued with, and a class the filter never
        matches is not a decision at all -- nobody has had the argument. And
        reached-only-through-another-suffix is the third: an adapter, a route
        and a feature-flag registry are covered because they happen to be
        TypeScript, so a file moved to another format takes the coverage with
        it.

        THE REGISTER IS CHECKED AGAINST THE SCRIPT, NOT AGAINST ITSELF. The
        suffix list and the exclusions are PARSED out of the shell source, so
        the day somebody adds .mjs this register is wrong out loud; the parser
        returns null rather than a guess when the filter changes shape, and the
        scan fails on that. A class recorded as reached must arrive as a suffix
        the script accepts, and a class recorded as excluded must arrive as a
        pattern the script really excludes. (The first version of that second
        check grepped this register's own prose for words like "deliberate" and
        failed on a perfectly good reason -- a check that reads meaning out of
        wording, which is the thing this initiative keeps finding in other
        people's registers. It is structural now.)

        Measured: 11 source classes, 5 reached, 3 excluded by a stated rule, 3
        unreachable. 21 spec tests; scan PASSES with 12 negative controls. -->
  - [x] S11.18.b Detect fake/mock/demo/stub/in-memory/fallback provider binding,
        deterministic canned outputs, catch-and-success, and unconfigured-to-
        success paths. Evidence: provider rules/fixtures.
        <!-- THE SCAN LOOKS FOR THE WORD. THE DEFECT IS A SHAPE.

        scripts/stub-indicator-scan.sh is a regular expression over lines, and
        it is a good one: "in production, this", "would use", "not yet
        implemented", "placeholder", "hardcoded", "fake", "TODO:". Every one of
        those is somebody ADMITTING what they did, in English, in a comment. Its
        real power is that developers write those admissions honestly, and it
        works -- it caught this session twice.

        The four fabrications this cell names contain none of those words:

            catch { return { ok: true }; }
            if (!apiKey) return { configured: true };
            export const provider = new InMemoryProvider();
            export const score = (input) => 0.92;

        Four lines that fabricate a result, and a vocabulary scan reads them as
        ordinary code. THE RUN PUTS ONE FIXTURE THROUGH BOTH INSTRUMENTS, with
        the pattern PARSED out of the shell script rather than copied so the
        comparison is against what really runs: ZERO hits against FOUR findings,
        same file, same run.

        WHY THIS IS NOT A CRITICISM OF THAT SCAN. A grep for admissions and a
        search for shapes are different instruments and the estate should have
        both. One catches the developer who wrote down what they were doing; the
        other catches code that says nothing about itself. Neither subsumes the
        other, and the second is the half that does not exist.

        THE APPROXIMATION IS STATED RATHER THAN DISCOVERED LATER. No TypeScript
        parser: a lexical pre-pass blanks comments and string bodies while
        PRESERVING every offset and newline -- so a reported line is a real line
        a reader can open -- and then the detectors match on brace-balanced
        regions. The limits are written into the module, every finding carries
        its line and the text that fired, and the spec exercises the pre-pass
        against comments, strings, template literals and escaped quotes.

        EVERY DETECTOR IS SILENT ON THE HONEST SHAPE, which is what makes a
        finding worth reading. A catch that returns a typed failure, a default
        or an empty list produces nothing; so does one that rethrows. A
        not_configured throw, a fail-closed 503 and a configured:false produce
        nothing -- the estate's own honest seams. A zero-argument constant is a
        constant. A function that uses its argument is a function. The PARAMETER
        is the evidence for a canned output: a computation that cannot read its
        input did not happen.

        An inline opt-out exists (fabrication:legitimate <reason>) and REQUIRES
        a reason; a bare marker still reports. It is kept separate from the
        estate's stub:legitimate on purpose, because putting that marker on a
        line the vocabulary scan never flags would send a reader looking for a
        word that is not there.

        SWEPT OVER THE KIT'S OWN 302 PRODUCTION SOURCES: one finding, at
        negative-corpus.ts:446 -- `catch { return true; // not JSON at all; the
        whitespace question does not arise }`. Read by hand: the shape is real
        and the code is right, because `true` there means the question does not
        arise rather than that anything succeeded, and the reason is on the
        line. Reported rather than silenced, and the estate file was not edited
        to make this cell's number nicer.

        Measured: 4 fabrications, all shapes, none caught by a word. 35 spec
        tests; scan PASSES with 15 negative controls, seven of which check a
        detector stays SILENT. -->
  - [x] S11.18.c Detect numeric/boolean pass/default score, random/hash/length/
        keyword heuristics presented as domain evaluation, UI-computed truth,
        and missing-to-zero/green. Evidence: score rules/fixtures.
        <!-- A PROVENANCE IS A CLAIM ABOUT WHERE A NUMBER CAME FROM, AND EVERY
        CHECK IN THE ESTATE IS ABOUT ITS SHAPE.

        CREDIT, AND scores.ts IS ONE OF THE BEST-MODELLED THINGS IN THE KIT. A
        reading is measured or one of eight NON-VALUE STATES -- absent,
        not-applicable, insufficient-data, unsupported, unavailable, failed,
        stale, redacted -- with not-applicable and unsupported marked NOT
        COUNTED, because "this does not apply" and "we do not know" are
        different answers and averaging them together is how a dimension nobody
        could measure becomes a zero. Every reading carries a ScoreProvenance
        naming the evaluator and its version, the corpus and its version, the
        model, the provider (required whenever a model is named), the
        preprocessing, the policy the verdict would be derived under, the input
        revision, the input hash, the run and the instant. Nineteen violation
        kinds check the vector, down to an uncertainty interval that excludes
        its own value and a sample of one carrying a standard deviation.

        EVERY ONE OF THOSE CHECKS IS ABOUT THE RECORD. The scan builds a reading
        produced by dividing a length, gives it a provenance naming a model
        served by a provider, and puts it through the estate's OWN
        validateVector: ZERO violations out of nineteen -- the record is
        impeccable. It then reads the PRODUCER and reports the heuristic. Same
        number, same run.

        FOUR DETECTORS, NONE CATCHABLE BY A WORD.

        A judgement that DEFAULTS to a passing value: `?? 0` on a risk score is
        a claim that nothing is wrong, made by the operator that exists because
        something was missing. `?? 0` on a latency produces nothing, because the
        NAME is what separates them, and a default to a non-passing value
        produces nothing either -- defaulting to the worst case is a different
        and often correct decision.

        A heuristic over a length, a hash or a character code presented as a
        domain evaluation. It requires the body to read NOTHING ELSE, because a
        length among other inputs is a legitimate feature and the defect is
        length being the whole of it.

        A verdict computed inside the component that draws it. This is a
        fabrication rather than a style preference because such a verdict has no
        evaluator, no corpus, no policy and no run behind it: the number on the
        screen is real and its history does not exist, so it cannot be
        reproduced, compared across revisions, or explained to the person it is
        about.

        An absence resolved to a number. This is the one that matters most and
        the cheapest to get wrong: NON_VALUE_STATES exists, is complete, and is
        a keystroke further away than `?? 0`. Nothing in the estate makes the
        honest answer the easy one.

        THE SWEEP OVER 303 KIT SOURCES FOUND THREE FALSE POSITIVES AND EVERY ONE
        TIGHTENED A DETECTOR RATHER THAN AN ALLOW-LIST.

        `counts.set(verdict.owner, (counts.get(verdict.owner) ?? 0) + 1)` -- a
        counter, and defaulting a count to zero is correct. Only the assignment
        target and the operand's ROOT count now, walked backwards over one
        balanced group so an argument cannot supply the name.

        `good += creditOf(spec.grades, observation.outcome) ?? 0` -- same fix,
        same reason.

        And provider-registry's unprobed-provider branch, which returns
        `{ situation: 'probe-failed', status: 503, retryHelps: true, ... }` --
        code whose own detail says "an unprobed provider is not a healthy one,
        and reporting it as healthy is the failure this state exists to
        prevent". Matching ANY field with a passing value had read
        `retryHelps: true` as a claim of success. Advice about whether to retry
        is not a score, so only a field carrying the ANSWER counts now. Both
        corrections are locked in by spec tests naming the real lines.

        The sweep now reports zero. (Its first run also refused this scan's own
        fixture with non-finite-value, because the reading was hand-written with
        the wrong field name -- the estate's check working on my fixture. It is
        built with the estate's own `measured()` constructor now.)

        The lexical pre-pass, its limits and the inline opt-out are S11.18.b's,
        reused rather than reimplemented. Measured: 4 fabrications, all
        checkable only over the producer, 3 of them with an honest answer that
        already exists in the estate. 28 spec tests; scan PASSES with 13
        negative controls, six of which check a detector stays SILENT. -->
  - [x] S11.18.d Detect fixture/sample/demo seed/interception/test builder in
        production dependency graph or unlabeled runtime output. Evidence:
        fixture rules/fixtures.
        <!-- A DEPENDENCY ON A FIXTURE IS NOT A DEFECT. A PATH FROM A NAME THAT
        PROMISES SOMETHING ELSE IS.

        S11.18.a recorded this class as unreachable by the estate's scan and
        gave the reason: whether production code imports a fixture is a property
        of the GRAPH and not of any line, so a grep over text cannot see it at
        all. This cell is the graph. 305 modules, 623 sibling import edges,
        walked breadth-first so the path returned is the SHORTEST one -- which
        is the edge a reader is most likely to be able to cut.

        WHAT IS NOT A FINDING. A kit that publishes its fixture modules as entry
        points BY NAME is being honest: a consumer who writes
        @oshun/workbench-kit/tamper-corpus has asked for fixtures, in those
        words, and got them. So the 25 declared fixture entry points are LISTED,
        not reported. Without that distinction the report is 25 findings that
        are all correct behaviour, and nobody reads the 26th. A checker whose
        subject is a corpus must import the corpus; a module about fixture MODE
        is about a mode, not a fixture.

        WHAT IS. queue-invariants -> lease-fixtures. A consumer importing a
        module called queue-invariants has asked for invariants; whether a
        corpus arrives with them is a fact about the graph nobody can read off
        the import statement. AND THE FINDING IS A PATH, NOT A PAIR: "X depends
        on a fixture" is unactionable, and X -> Y -> corpus names the edge
        somebody has to cut, so every finding carries the whole chain.

        AND THE MARK EXISTS AND NOTHING APPLIES IT. synthetic.ts ships
        SYNTHETIC_SEGMENT_PREFIX, syntheticMark() and isSyntheticId -- a real
        runtime boundary, which anti-mock.ts names as one of its strongest
        instruments, because a mark somebody deliberately put there is a
        POSITIVE trace whose presence and absence mean different things.
        Measured: 0 of 25 fixture modules import it. That is the
        "unlabeled runtime output" half of this cell, and it is not a missing
        feature -- the feature is written, exported and documented, and what is
        missing is the wire between the modules that manufacture data and the
        one module that would let a reader downstream tell that data apart.
        Whether a VALUE carries the mark is a runtime question; whether the
        module that made it can even apply one is a static edge, and that is
        what this measures.

        INTERCEPTION IS RECORDED AS ANSWERABLE ONLY BY A RUN. A responder
        installed in a test process is not in the import graph of the code under
        test; the estate answers it from the harness with an outbound-request
        record. There is a register fault that fires if a runtime-only source
        ever claims a static detector, because swapping the two is how a suite
        reports coverage it does not have.

        The specifier reader deliberately runs over the RAW source, since a
        specifier IS a string literal and S11.18.b's blanking pre-pass would
        erase exactly what this needs -- the two passes want opposite things
        from the same text. Its own spec then caught that a bare side-effect
        `import './x.js';` has no `from` and was invisible to the walk, which is
        an edge nobody could cut. The counts live in the run rather than in the
        module's prose, so they cannot go stale.

        Measured: 6 sources, 5 answerable by the walk and 1 only by a run. 25
        spec tests; scan PASSES with 12 negative controls, including that a
        cycle terminates, that the SHORTEST path is returned rather than the
        first found, and that renaming the marking module does not silently make
        every fixture look compliant. -->
  - [x] S11.18.e Detect fabricated success/verified/published/saved/healthy/
        configured/complete state from request dispatch, HTTP 2xx, empty value,
        build env, optimistic UI, or swallowed error. Evidence: success rules.
        <!-- A SUCCESS IS A CLAIM THAT SOMETHING HAPPENED. EVERY FABRICATION IN
        THIS CELL REPORTS A DIFFERENT, EARLIER EVENT INSTEAD.

            dispatch        we asked                      -> "it is saved"
            a 2xx status    the server answered           -> "it is published"
            an empty value  nothing came back             -> "it is healthy"
            a build value   it was true when we compiled  -> "it is configured"
            optimism        we expect it to               -> "it is complete"
            a caught error  we stopped looking            -> "it succeeded"

        That framing is what makes six shapes one cell rather than six unrelated
        greps. Each names a real moment that really occurred, and the defect is
        the distance between that moment and the one the word claims. It also
        gives every finding an obvious repair -- report the event you actually
        have -- which is why the substituted event travels IN the finding: "do
        not fabricate success" is advice, and "you have a dispatch and you are
        printing a save" is a change somebody can make this afternoon.

        TWO OF THE SIX ARE ALREADY NAMED BY THE ESTATE, AT RUNTIME. anti-mock
        has empty-success -- "a well-formed answer carrying nothing, from a
        question nobody asked" -- and build-time-guess -- "a value decided when
        the bundle was built, presented as current" -- each with the absence it
        claims written out and an instrument that refutes it. This cell asks the
        same two questions of the SOURCE, plus four the estate does not name.
        The register records which is which, with faults that fire if an
        estate-named source cites no masquerade class or a cell-only source
        cites one, and the run checks the cited classes against
        MASQUERADE_CLASSES rather than trusting them. Crediting one instrument
        with the other's reach is how a suite reports coverage it does not have.

        THE SUCCESS WORDS ARE THE CELL'S, NOT MINE: success, verified,
        published, saved, healthy, configured, complete. A boolean called `sent`
        set after a dispatch is accurate and produces nothing; a boolean called
        `saved` set after the same dispatch is not.

        AND THE DISCARD IS THE EVIDENCE. `const receipt = await send(x)` may
        well be checked, and `await send(x)` on its own line means nothing looked
        at what came back. A status branch that opens the body produces nothing.
        An empty list answered with "unknown, no check has run" produces
        nothing. A catch returning a typed failure produces nothing.

        THE RUN SHOWS ALL SIX ON ONE FIXTURE AND ZERO ON THE HONEST REWRITE OF
        THE SAME SIX FUNCTIONS, because a detector that has only been shown
        firing has not been shown to be quiet.

        Swept over the kit's production sources: one finding, at staleness.ts,
        where `truncated.length === 0` returns `complete: true`. Read by hand
        and CORRECT: there the emptiness of `truncated` IS the completeness
        rather than a substitute for it. That distinction is not one a detector
        can make in general, which is exactly why this module says a finding is
        a place to look and not a verdict. Reported rather than silenced.

        (Its own fixture caught that the optimistic detector saw no assertion in
        `setSaved(true)` -- a setter call is how a component says it, and the
        first version matched only fields and assignments, so the detector found
        nothing in the one shape it exists for.)

        Measured: 6 sources, 2 already named by the estate at runtime, 4 only
        here. 28 spec tests; scan PASSES with 14 negative controls, six of which
        check a detector stays SILENT on the honest form. -->
  - [x] S11.18.f Classify blocker/review/allowed test-only/known false-positive
        with source, dataflow/context, owner, disposition, expiry, and linked
        proof; no broad ignore patterns. Evidence: triage schema.
        <!-- A SUPPRESSION WITH A REASON AND NO EXPIRY IS PERMANENT.

        CREDIT FIRST, AND THE ESTATE'S ALLOW-LIST IS THE RIGHT SHAPE. It is the
        narrowest one available: an end-of-line comment, // stub:legitimate
        <reason>, suppressing exactly the line it sits on. Nothing about it can
        be widened by accident, it travels with the code it excuses, and it
        requires somebody to write a sentence. Against a config file listing
        paths to ignore, that is a much better design.

        IT CARRIES ONE OF THE SIX FIELDS THIS CELL ASKS FOR. There is a reason.
        There is no owner, no disposition, no expiry, no linked proof, and no
        statement of the dataflow that makes the line safe. So a suppression
        written on a Tuesday to get a branch through is indistinguishable,
        forever, from one a team argued about.

        MEASURED OVER THE WHOLE REPOSITORY: 8,129 markers across 3,234 files.
        24 carry an owner (0.3%). 27 carry a linked proof (0.3%). TWO carry no
        reason at all. ZERO carry an expiry -- so all 8,129 are permanent, and
        nothing will ever ask about any of them again.

        A REASON THAT PROMISES A FUTURE IS AN EXPIRY WITH NO DATE. 206 of them
        say "until the real pose-estimation pipeline lands", "will call the
        actual model inference", "V2-deferred", "for now". Each is a claim that
        the suppression is TEMPORARY, made by somebody who did not say when --
        which is the missing field, written in prose where nothing can read it.
        That detector is a documented approximation over a small explicit word
        list, and it accepts any reason carrying a date whatever else it says:
        it cannot tell a good reason from a bad one, which nothing can by
        machine, and it can tell a reason that promises a future from one that
        does not, which is the checkable half.

        AND BREADTH IS A PROPERTY OF THE DIRECTIVE, NOT OF THE REASON. A
        line-scoped suppression excuses one line. A file-scoped one excuses
        every line that will ever be added to that file, including lines nobody
        has written yet, and the person who adds them never sees the comment at
        the top. The cell says "no broad ignore patterns", and the only way to
        hold that is to make breadth a field and judge it. Of 1,603
        eslint-disable directives, 763 are FILE-SCOPED block comments -- and
        they go through the SAME triageFaults as everything else, because a
        number printed beside a schema is not a number judged by it. That
        produced 3,815 findings over 763 directives and is what makes
        suppression-wider-than-a-block a finding rather than a fault kind
        nothing in this repository exhibits.

        THE EXPIRY CHECK TAKES THE INSTANT AS AN ARGUMENT rather than reading a
        clock -- the run uses the newest commit date -- so two runs of the same
        tree agree, and a test that asks "is this expired" does not answer
        differently tomorrow. And an allow-list may not hold a blocker, which is
        its own fault kind.

        Across every suppression in the repository, 7 of the 9 fault kinds are
        exhibited; record-expired and blocker-that-is-suppressed are not, which
        is reported rather than left as an unexplained gap.

        Measured: 6 fields, 1 carried by the marker, 1 (disposition) with
        nowhere to go at all -- so a needs-review and a known-false-positive are
        written identically and a reviewer cannot sort them -- over 4
        dispositions the marker cannot tell apart. 23 spec tests; scan PASSES
        with 14 negative controls, including that a complete record produces
        nothing and that the same promise WITH a date is accepted. -->
  - [x] S11.18.g Run static patterns plus AST/import/bundle checks and targeted
        runtime/adversarial tests, then independently review zero-blocker
        report. Evidence: signed scan report.
        <!-- A SIGNATURE SAYS WHO WROTE A NUMBER. IT SAYS NOTHING ABOUT WHETHER
        THE NUMBER IS RIGHT.

        Three of this cell's four asks are ordinary work and the run does them
        for real. It EXECUTES all eight scans -- the estate's own stub scan plus
        S11.18.a through .f plus S11.17.g's mutation harness -- collects their
        exit codes, reads each one's negative-control result out of its own
        output, builds a canonical preimage, digests it, signs it with a freshly
        generated ed25519 key, verifies the signature, and shows that one
        altered field breaks verification. The preimage covers the REVIEW STATE,
        so a signature cannot be lifted from an unreviewed report onto a
        reviewed one, and it does not depend on the order the scans arrive in.

        THE ZERO THIS CELL ASKS FOR DOES NOT EXIST FOR THIS REPOSITORY. The
        estate's own whole-repo stub scan reports 3,341 actionable hits. The
        report states the number and names the scan that produced it, and the
        run PASSES while reporting it -- because refusing to publish an honest
        non-zero would be the exact defect this section is about, committed by
        the cell that closes it. A non-zero is a success of the instrument. The
        count is read from the scan's own output rather than collapsed from a
        non-zero exit code, because the difference between one blocker and three
        thousand is the whole content of a report.

        (That number is not an indictment of the estate's scan. The hook scans
        STAGED files and CI scans the PR DIFF, so the scan is a ratchet on new
        code rather than a gate on the tree -- which is a deliberate design, and
        is why the R100 rename carve-out exists. What it means is that the
        whole-repo zero this cell asks for has never been required, and this is
        the first run that asked for it.)

        AND INDEPENDENT IS A FACT ABOUT WHO. The fourth ask cannot be done by
        the party that produced the report -- not for want of a tool, but
        because there is one party here, and a zero produced, signed and
        reviewed by the same hand is the claim and the evidence in one hand. A
        signature over that proves only that the same hand held both. So the
        report carries unreviewed-because-the-producer-is-the-only-party as a
        VALUE rather than a footnote, and reportFaults refuses any zero-blocker
        report whose named reviewer is its own producer: the only way to get the
        strong claim is for somebody else to sign it. A report that plainly
        admits it was not reviewed produces no finding at all, because being
        unreviewed is a state and claiming otherwise is the fault.

        WHAT A ZERO IS WORTH WITHOUT THAT should also be said, and it is quite a
        lot: every scan in this section carries negative controls that exit 2 if
        a check cannot fail, so a zero from a scan that ran is a zero from an
        instrument that has been shown to work. What it is not is INDEPENDENT,
        and those are different claims that are easy to write as one sentence.

        A SCAN THAT DID NOT RUN IS NOT A SCAN THAT FOUND NOTHING, and a report
        holding one may not claim zero at all -- the same rule S11.17.g used for
        its mutation baseline, for the same reason.

        `bundle` is the one instrument the cell names that nothing exercises,
        for the reason S11.18.a measured: generated paths are dropped before the
        suffix filter, so no scan in this section opens a built bundle. Reported
        as an unexercised instrument rather than quietly dropped from the list.

        (Two of this scan's own negative controls MISSED on the first run,
        because they were derived from the live report -- which has 3,341
        blockers -- and the producer-reviewed check only fires on a ZERO-blocker
        report. A control working. They are built from an explicitly clean
        fixture now.)

        Measured: 8 scans, 4 of 5 instruments, 3,341 blockers, signed and
        verified, NOT independently reviewed. 26 spec tests; scan PASSES with 13
        negative controls. -->
S11.18 parent rollup — closed by a–g (note only; the checkbox is the `S11.18` line above, §2.5.11).
      <!-- THE SECTION'S SHAPE: THE ESTATE HAS ONE INSTRUMENT AND IT LOOKS FOR
      ADMISSIONS.

      scripts/stub-indicator-scan.sh is real, runs in five places, has three
      modes, an allow-list that carries a reason, a documented test exclusion
      and a rename carve-out learned from an incident. It caught this session
      three times. What it is, is a grep for somebody ADMITTING what they did --
      "in production, this", "would use", "placeholder", "TODO:" -- and it works
      because developers write those admissions honestly.

      EVERY CELL IN THIS SECTION FOUND A DEFECT THAT SAYS NOTHING ABOUT ITSELF.
      .b: four provider fabrications with zero scan vocabulary between them, put
      through the estate's own pattern for 0 hits and through the shape
      detectors for 4. .c: a reading produced by dividing a length passes all 19
      of the estate's vector checks, because a provenance is a claim about where
      a number came from and every check is about its shape. .d: whether
      production code imports a fixture is a property of the GRAPH, invisible to
      any grep -- 305 modules and 623 edges walked to find it. .e: six ways to
      report success, each substituting a different earlier event. .f: 8,129
      suppressions, ZERO with an expiry.

      AND .a MEASURED WHY: the scan's scope is thirteen suffixes, and 5,172
      out-of-scope tracked files carry code somebody runs -- 3,638 of them .mjs,
      which is what every verification tool in this initiative is written in,
      INCLUDING THE ONE THAT PRINTS THAT NUMBER.

      THE DISCIPLINE. Every detector here is shown SILENT on the honest form as
      well as loud on the defect, because a detector only shown firing has not
      been shown to be quiet. Three sweeps over the kit's own sources found four
      false positives, and every one tightened a detector rather than an
      allow-list -- including provider-registry's unprobed-provider branch,
      whose retryHelps:true was read as a claim of success in code whose own
      comment says reporting it as healthy is the failure that state exists to
      prevent.

      .g closed it by running all eight scans, signing the report, and saying
      the two things the report cannot say: the whole-repo zero does not exist
      (3,341 hits), and nobody independent has looked.

      Seven cells, seven scans, all passing with negative controls; the section
      closes with the kit at 304 entry points and 0 violations. -->
- [x] S11.19 Add dependency-license, secret, SAST, and supply-chain scans to the
      release evidence index. Evidence: critical findings block release.
  - [x] S11.19.a Scan direct/transitive runtime/build/dev/container/native/
        model/plugin/DCC/tool dependencies for vulnerabilities, reachability,
        maintenance, source, hash, and advisories. Evidence: dependency report.
        <!-- A GATE IS THE INTERSECTION OF WHAT BLOCKS AND WHAT IS COVERED.

        CREDIT, AND THE PIPELINE IS SUBSTANTIAL. security-deps-and-secrets.yml
        runs a pnpm audit with a documented baseline and a severity threshold,
        the OSV scanner recursively over the whole tree, gitleaks and
        trufflehog, and aggregates them into a release gate.
        sbom-and-sign.yml runs syft for an SBOM, grype over it, cosign for
        signatures and attest-build-provenance. Most repositories do not have a
        supply-chain posture like this.

        THE GATE REQUIRES THREE OF THE FOUR SCANNERS, and says so in a comment:
        "OSV is informational on this gate; it stays in artifacts." That is
        honest labelling and deserves credit rather than a finding on its own.

        WHAT IT MEANS IS THE FINDING. pnpm audit reads a pnpm lockfile and
        nothing else, so OSV -- run with --recursive ./ -- is the only scanner in
        the pipeline that sees a Cargo.lock, a poetry.lock or a go.mod. And it
        is the one that cannot fail the build. Measured against the tracked
        tree: 6 of the 7 present ecosystems have NOTHING that can fail the build
        reading them -- 94 Cargo lockfiles, 34 python manifests, 3 Go modules,
        156 Dockerfiles, 102 .uplugin, 2 .csproj. Neither half of that is a
        mistake. The intersection is empty, and an intersection is what a gate
        is.

        Which jobs the gate REQUIRES is PARSED out of the workflow rather than
        restated here, because a register naming the blocking scanners is a
        second copy of a fact that lives in a YAML file, and the two drift. The
        run also reports when the gate runs at all: only on main and release
        branches.

        AND THE CELL NAMES SIX ATTRIBUTES, OF WHICH THREE ARE DATABASE
        QUESTIONS, TWO ARE LOCKFILE QUESTIONS AND ONE IS A GRAPH QUESTION.

        Vulnerabilities, advisories and maintenance are facts about the outside
        world that arrive from a database somebody else maintains; this
        repository can check that it ASKS, and cannot answer them itself. Source
        and hash are in the lockfile and checkable offline -- 6,502 of 6,503
        resolutions carry an integrity hash, which is a real guarantee, enforced
        by the INSTALLER rather than by the gate.

        REACHABILITY is neither. It is a property of the import graph,
        computable with no network and no database, and no tool in the pipeline
        computes it -- so a critical advisory against a package nothing imports
        and a critical advisory against a package on the login path arrive at
        the same severity, in the same report, with nothing to tell them apart.
        S11.18.d already built the walk; this cell records what joining it to a
        dependency name would take, and does not pretend the join exists.

        Measured: 6 attributes -- 1 asked by something that blocks, 3 asked only
        by something informational, 2 asked by nothing, 3 answerable inside this
        repository. 9 dependency kinds, 7 ecosystems. 15 spec tests; scan PASSES
        with 10 negative controls, including that making every scanner blocking
        reports FEWER ungated ecosystems, so the count is a measurement rather
        than a constant. -->
  - [x] S11.19.b Generate license/notice/SBOM and detect prohibited/unknown/
        missing/incompatible/transitive/model/data/font/media license or
        attribution. Evidence: license report.
        <!-- AN SBOM IS A BILL OF SOFTWARE. FOUR OF THE NINE THINGS THIS CELL
        NAMES ARE NOT SOFTWARE.

        CREDIT: sbom-and-sign.yml generates a real SBOM in both CycloneDX and
        SPDX from syft, scans it with grype, signs it with cosign and attaches a
        build-provenance attestation. Both formats carry a licence field per
        component, so for PACKAGES the question this cell asks is answered by
        machinery that exists and runs. The formats are parsed out of the
        workflow rather than asserted here.

        A FONT IS LICENSED. An image is licensed. An audio file is licensed. A
        model weight is licensed, usually on terms stricter than any code in the
        tree -- non-commercial, or research-only, or requiring a named
        attribution in the product. None of them is a package, so syft
        enumerates none of them, grype scans none of them, and neither SBOM has
        a component to attach a licence to.

        MEASURED FROM git ls-files: 545 tracked files carry terms and appear in
        no SBOM component -- 448 media (266 .png, 111 .svg, 66 .jpg, 3 .mp3, 1
        .wav, 1 .webm), 85 data (60 .csv, 25 .jsonl), 9 fonts (5 .ttf, 4
        .woff2), and 3 .onnx weights.

        The register's distinction is a-file-in-the-tree-with-no-manifest versus
        a-package-manifest-an-sbom-reads, because a thing with no manifest is
        not a component the scanner MISSED -- it is a component the scanner has
        no way to know about, and the repair is an inventory rather than a
        configuration change. There is a fault kind that fires if an entry ever
        claims an SBOM component for something with no manifest.

        AND THE ROOT OF THE TREE HAS A LICENSE AND NO NOTICE. LICENSE is
        Apache-2.0: what this repository GRANTS to whoever takes the code. A
        NOTICE points the other way -- what this repository OWES to the people
        whose work it ships -- and Apache-2.0 section 4(d) is explicit that a
        redistributed work carrying a NOTICE must carry its contents onward. So
        a tree with dependencies and no NOTICE has answered one direction only.
        Conflating the two directions is easy and expensive, so they are
        separate values in the register.

        WHY "TRANSITIVE" IS ITS OWN SUBJECT: a direct dependency's licence is
        something a person chose, and a transitive one arrives with it. An SBOM
        lists both identically, which is right for a bill of materials and wrong
        for a review queue -- one remedy is a decision to revisit and the other
        is a conversation with whoever owns the parent.

        Measured: 5 licensed subjects, 1 in the SBOM and 4 outside it over 545
        files; 1 of 2 required attribution artifacts present; 6 defect kinds the
        cell names. 16 spec tests; scan PASSES with 10 negative controls,
        including that a subject whose files are ABSENT from the tree is not
        reported, so the count is a measurement rather than a constant. -->
  - [x] S11.19.c Scan source/history/config/build artifacts/images/bundles/log/
        test snapshots for credentials/tokens/keys/certs/signed URLs/secret
        patterns and verified canaries. Evidence: secret report.
        <!-- A SCANNER THAT FINDS NOTHING AND A SCANNER THAT IS NOT RUNNING
        PRODUCE THE SAME REPORT. A CANARY IS THE ONLY THING THAT TELLS THEM
        APART, AND THERE IS NONE.

        CREDIT, AND BOTH SCANNERS BLOCK. gitleaks and trufflehog each run on
        every release, each with fetch-depth: 0, so both read the whole HISTORY
        and not only the working tree -- which is the half of secret scanning
        that matters most, because a key deleted in the next commit is still in
        the pack file forever. The release gate requires both. The flags and the
        checkout depths are PARSED from the workflow rather than restated here.

        AND TRUFFLEHOG RUNS --only-verified. That flag means a secret is
        reported only when the credential could be CONFIRMED live by calling the
        provider, and it is a deliberate, defensible answer to alert fatigue: a
        queue full of unverified findings is a queue nobody reads.

        It also means the class of things that do not fail the build is larger
        than it looks. 4 of the 6 kinds this cell names cannot be verified by
        anybody. A private key has no endpoint to present it to. A signed URL
        grants access by existing, and calling it would be USING it, so
        verification is not something a scanner should do. A certificate and a
        pattern match authenticate nothing. And a key rotated last week no
        longer verifies while still sitting in the history for whoever had it
        before it was rotated.

        Neither half is a defect on its own. Writing both down is what stops
        "the secret scan passed" being read as "there are no secrets".

        4 OF THE 8 SURFACES ARE NOT PRESENT WHEN THE SCAN HAPPENS. A build
        artifact, an image layer and a bundle are produced later; a runtime log
        is never on that machine at all. That is a fact about where the surface
        IS, rather than a gap in a scanner -- the same shape S11.18.a found for
        the stub scan and the bundle.

        AND THE CELL ASKS FOR VERIFIED CANARIES, WHICH DO NOT EXIST. Without
        one, a green secret scan and a misconfigured secret scan are
        indistinguishable, and the difference is discovered by the incident.

        A CANARY MUST NOT BE A WORKING CREDENTIAL is the first of the five
        requirements, and it is checked by an explicit invalidating marker
        rather than by guessing at shape: deciding "is this a real key" by
        appearance is exactly the guess this repository refuses elsewhere, and
        the cost of being wrong is a live credential in a git history. A
        proposed canary is run against all five and fails exactly one -- nobody
        has seen a scanner report it -- so it is a plan for a control rather
        than one. There are also fault kinds for a canary planted in a surface
        no scanner reaches, and for a canary of a kind --only-verified filters
        out, because either would be silent whether the scanner worked or not.

        Whether either scanner would find a canary is UNOBSERVED here: the run
        measures the host and finds neither binary. This cell does not claim
        otherwise.

        Measured: 6 kinds (2 reported under the flag, 4 not), 8 surfaces (4
        present when scanned, 4 not), 0 canaries. 15 spec tests; scan PASSES
        with 10 negative controls, including that a register WITH a canary for a
        reportable kind stops reporting the gap. -->
  - [x] S11.19.d Run language/framework SAST for injection, auth, tenant, SSRF/
        upload/path, crypto, deserialization, traversal, unsafe subprocess,
        privacy, and unsafe API use with reachability. Evidence: SAST report.
        <!-- CODEQL'S REACHABILITY IS REAL, AND IT STOPS AT THE LANGUAGE
        BOUNDARY.

        CREDIT, AND THE CONFIGURATION IS A GOOD ONE. codeql.yml runs with
        +security-extended,security-and-quality -- not the default suite, the
        wide one -- over libs, apps and lilith/services, on every push, every
        pull request and a weekly cron, excluding node_modules, dist and test
        files by path. And CodeQL is a DATAFLOW engine: the cell asks for
        reachability, and this is the one instrument in the whole pipeline that
        genuinely has it, because a taint path from a request parameter to a
        query is a far stronger claim than a pattern match.

        THE PATHS ARE NOT THE GAP. Measured against the tree, those three roots
        hold 50,482 of the 51,512 non-test TypeScript files -- 98.0% -- and the
        remainder is mostly generated evidence. This cell says so rather than
        reaching for the easy finding.

        THE LANGUAGE IS. matrix.language is ["javascript-typescript"] alone. The
        estate has 9,306 Rust files, 3,556 Python, 2,871 C/C++ and 269 C#, and
        none of them gets any security analysis. cargo clippy -D warnings is a
        correctness and style lint -- valuable, and not a taint analysis, and
        saying which it is matters.

        AND THE BOUNDARY IS CROSSED CONSTANTLY. 361 files hand a value to a
        subprocess and 656 reference a native binding. Every one is a place
        where a value leaves the analysed language, and an engine that is right
        about everything it can see returns nothing about the step across --
        which is exactly where an unsafe-subprocess finding would matter most.

        TWO OF THE ELEVEN CATEGORIES ARE NOT LANGUAGE DEFECTS. Injection, path
        traversal, SSRF, unsafe deserialization, weak crypto and unsafe API use
        are properties of code that any good engine knows about, and the wide
        suite covers them. TENANT ISOLATION and PRIVACY are not: which argument
        is a tenant, and which field is personal data, are facts about THIS
        estate that no generic query pack has ever seen. A pack cannot know that
        a column called handle is personal and one called slug is not.

        The estate HAS the vocabulary for the first -- a tenant id type and a
        scope key that puts the tenant first, both verified by running them in
        S11.16.g -- so a custom query is writable. There are ZERO .ql files in
        the repository. The register carries a fault that fires if any entry
        ever claims a shipped suite covers a rule only this estate knows.

        Measured: 11 categories (9 properties of code, 2 rules no pack knows),
        1 language analysed of 6 present, 1,017 boundary crossings, 0 custom
        queries. 11 spec tests; scan PASSES with 9 negative controls, including
        that the same boundary count with SEVERAL languages analysed is not
        reported, so the finding is about the pair rather than about the
        crossings. -->
  - [x] S11.19.e Verify lockfile/integrity/signatures/provenance, pinned
        actions/ tools/images, build isolation, generated artifacts, package
        confusion, typosquat, install scripts, and signing chain. Evidence:
        supply-chain report.
        <!-- A POLICY THAT LIVES ONLY IN THE PATTERN OF PAST DECISIONS CANNOT BE
        VIOLATED -- ONLY DEPARTED FROM UNNOTICED.

        CREDIT, AND TWO OF THESE ARE EXCELLENT. The lockfile carries an
        integrity hash for effectively every resolution and the INSTALLER
        verifies it, so the bytes that arrive are the bytes that were locked.
        And package.json declares onlyBuiltDependencies -- an explicit
        allow-list of the 35 packages permitted to run an install script, beside
        ignore-scripts=false. That is the strongest single control in this
        section: an install script runs arbitrary code on a developer machine
        and on every CI runner, and this estate names exactly which packages may
        do it.

        AND THE ACTION PINNING IS A REAL POLICY THAT NOBODY WROTE DOWN. Of 796
        workflow references across 106 workflows, 11 are pinned to a commit --
        and every one is a third party that either installs a toolchain or scans
        code: dtolnay/rust-toolchain, aquasecurity/trivy-action,
        trufflesecurity/trufflehog, snyk/actions/node. That is not an accident.
        It is somebody applying a sound rule, repeatedly, on exactly the
        references where a moved tag would be worst.

        The pattern is DERIVED from the references rather than asserted, because
        asserting "the policy is to pin the security tools" would be a claim
        about somebody's intent, and deriving it makes the exceptions findings
        rather than opinions.

        THE RULE EXISTS NOWHERE. So ten other security-sensitive references are
        named by something the owner can move: sigstore/cosign-installer@v3.7.0,
        which is the signing tool the entire chain rests on;
        anchore/sbom-action@v0, a MAJOR-version tag on the action that produces
        the SBOM; anchore/scan-action/download-grype@v4;
        google/osv-scanner-action; gitleaks-action@v2; and three zaproxy
        actions.

        AND THE CLEAREST PROOF IS ONE ACTION TREATED TWO WAYS.
        dtolnay/rust-toolchain is SHA-pinned in one workflow and @stable -- a
        BRANCH -- in another, in the same repository, and nothing notices. The
        run reports that pair by name and fails if it ever disappears without
        the finding being rewritten.

        A tag is a name the owner can move; a commit is not. The gap between the
        pinned and the unpinned is not a difference in judgement -- it is the
        absence of anything that reports one.

        The register keeps a-pattern-nobody-wrote-down as a state distinct from
        both a mechanism and nothing, because it is strictly better than nothing
        (somebody was applying a rule) and strictly worse than a rule (nothing
        can report a departure). Three controls are held by nothing: pinned
        images (no check reads a Dockerfile FROM line), generated artifacts
        (S11.18.a measured that every scan excludes them, so what a build emits
        is checked by nothing before it is signed), and typosquat.

        Measured: 13 controls -- 6 enforced by a mechanism, 2 declared, 2 held
        by a pattern nobody wrote down, 3 by nothing; 626 of 796 references on a
        major-version tag. 16 spec tests; scan PASSES with 10 negative controls,
        including that the same sensitive action pinned to a COMMIT is not
        reported, so the finding is about the pinning rather than about the
        owner. -->
  - [x] S11.19.f Normalize findings with scan/tool/db version, artifact/commit,
        severity/exploitability/scope, owner, fix/exception/expiry, retest, and
        raw evidence link. Evidence: evidence index rows.
        <!-- A LINK TO RAW EVIDENCE THAT EXPIRES IS A FINDING THAT BECOMES
        UNCHECKABLE ON A SCHEDULE.

        The cell requires a raw evidence link on every row, and the estate
        produces exactly that: every scanner uploads its report as a workflow
        artifact, and a finding can name the run it came from. Measured across
        the workflows: 94 uploads, 91 declaring a retention -- 4 kept for ONE
        day, 29 for seven, 36 for fourteen, 19 for thirty, 3 for ninety.

        So a finding raised on a Monday, with an exception granted for a
        quarter, points at a report that stopped existing before the first
        review. And the row STILL HAS A LINK IN IT, which is worse than an empty
        field, because an empty field is obviously empty.

        THAT IS NOT AN ARGUMENT FOR LONGER RETENTION. Storage costs money and
        old scan output ages badly. It is an argument that the retention period
        is part of the FINDING: a row whose exception outlives its evidence has
        to say so. exception-that-outlives-its-evidence names both numbers --
        "the exception runs 92 day(s) from 2026-08-01 and the evidence is kept
        for 14" -- so the repair is obvious and is a choice between two things
        somebody can actually change.

        AND A VERSION IS THREE VERSIONS. The cell asks for scan, tool and
        database version, and they move independently: the same tool, on the
        same commit, against a different advisory database produces a different
        answer, and only the third explains why a finding appeared with no code
        change. Recording one number called "version" loses exactly the case
        somebody will need to explain.

        EXPLOITABILITY IS SEPARATE FROM SEVERITY, because they answer different
        questions and a queue sorted by the wrong one wastes the week: severity
        is how bad it would be, exploitability is whether it can happen here.
        S11.19.a measured that nothing in the pipeline computes reachability, so
        that field arrives from a person -- and a critical with it unknown is a
        finding of its own, because it is the field that decides whether this
        week is spent on it.

        Both instants are ARGUMENTS rather than a clock read, so an expiry
        answer is reproducible: the same fix S11.18.f needed, for the same
        reason.

        Measured: 13 fields, 5 severities, 4 exploitability values, 3 remedies,
        12 fault kinds. 13 spec tests; scan PASSES with 11 negative controls,
        including that an exception INSIDE its evidence window is accepted and
        that the same unknown exploitability on a low severity is not a
        finding. -->
  - [x] S11.19.g Block critical policy findings, expired exceptions, missing/
        stale/incomplete scans, or artifact mismatch and prove with deliberate
        fixture. Evidence: release gate test.
        <!-- THE GATE ASKS WHETHER THE SCANNER SUCCEEDED. IT DOES NOT ASK WHAT
        THE SCANNER FOUND.

        release-gate asserts needs.<job>.result == "success" for each required
        scanner. That is a fact about a JOB. A job that ran, reported 156
        reviewed high and critical advisories, and exited zero because they are
        in a checked-in baseline is `success` -- and so is a job that found
        nothing. The gate cannot tell them apart, because the only thing it
        reads is the exit.

        THE BASELINE IS NOT THE DEFECT. non-v1-known-high-critical.json carries
        a schema version, the audit level it was generated at, an explicit
        scope, and a reviewedAt date of 2026-05-15; the audit tool splits
        findings by path so anything under a V1 prefix still blocks. That is a
        considered, documented, reviewable decision about which parts of a large
        monorepo gate a launch. The defect is that "the release gate passed" and
        "there are no high or critical advisories" are different statements, and
        the gate's output only supports the first.

        AND ONE THING THE GATE GETS RIGHT BY CONSTRUCTION, WHICH IS WORTH
        SAYING. Staleness cannot arise. The gate waits on jobs through needs:
        inside one workflow run, so every scan it depends on ran on the same
        commit, in the same run, minutes earlier. A gate that read a stored
        report would have to check how old it was; this one cannot be handed
        yesterday's answer. Recorded as impossible rather than as checked.

        THE CELL ASKS FOR PROOF BY DELIBERATE FIXTURE, and a gate asserted to
        block is a gate nobody has watched block -- the same argument S11.17.g
        made about mutation and S11.19.c made about canaries. So there are six
        fixtures, one per condition, each differing from a passing control in
        exactly ONE way, because a fixture that changes two things proves that
        one of them blocks and does not say which.

        The run drives the CONTROL first and requires it to pass. If the control
        blocks, every block below is free and proves nothing -- which is its own
        fault kind, passing-fixture-that-blocks. Then all six are driven, and
        each must block FOR ITS OWN CONDITION rather than merely block:
        fixture-that-blocks-for-another-reason is what catches a fixture that
        proves the gate says no without proving it says no for this. All six
        blocked, each for its own reason.

        Every reason is reported rather than returning at the first, because a
        release blocked for one reason and one blocked for four are different
        amounts of work, and a gate that stops early sends somebody back three
        times.

        Measured: 6 conditions -- 3 invisible to the estate gate (critical
        finding, expired exception, artifact mismatch), 1 impossible by
        construction (stale), 2 genuinely caught (missing, incomplete). 17 spec
        tests; scan PASSES with 11 negative controls, six of them one per
        fixture. -->
S11.19 parent rollup — closed by a–g (note only; the checkbox is the `S11.19` line above, §2.5.11).
      <!-- THE SECTION'S SHAPE: THE INSTRUMENTS ARE REAL AND THE GATE IS AN
      INTERSECTION.

      This estate has a substantial security posture and this section says so
      repeatedly: pnpm audit with a reviewed baseline, OSV recursively, gitleaks
      and trufflehog over full history, CodeQL on the WIDE query suite, syft in
      two SBOM formats, grype, cosign, build-provenance attestation, an install
      script allow-list, and integrity hashes the installer enforces. Most of
      this cell's work was measuring what those instruments reach.

      AND EVERY CELL FOUND THE SAME SHAPE: AN INSTRUMENT THAT IS RIGHT ABOUT
      WHAT IT SEES, AND A GAP NOBODY DECIDED ON. .a: the only scanner that reads
      Cargo, poetry and Go is the one that cannot fail the build, so 6 of 7
      present ecosystems are ungated. .b: an SBOM is a bill of SOFTWARE, and 545
      tracked files carry licence terms with no component to attach them to. .c:
      --only-verified means 4 of 6 secret kinds cannot be reported at all, and
      there is no canary to tell a working scanner from a silent one. .d: one
      language analysed, 1,017 places where a value leaves it. .e: 11 references
      pinned to a commit and every one security-sensitive -- a real policy that
      exists nowhere, so 10 more are on movable names and ONE ACTION IS PINNED
      BOTH WAYS. .f: the raw evidence a finding links to is kept for 14 days.
      .g: the gate reads a job result, not a finding count.

      THE DISCIPLINE. Every claim about the pipeline is PARSED from the workflow
      that makes it -- the required jobs, the query suite, the scanner flags, the
      SBOM formats, the retention periods -- because a register that restates a
      YAML file is a second copy that drifts. And .e derives the pinning policy
      from the references rather than asserting it, which turns an opinion about
      intent into a measurement with exceptions.

      Seven cells, seven scans, all passing with negative controls; the section
      closes with the kit at 311 entry points and 0 violations. -->
- [x] S11.20 Add a release-readiness generator. Evidence: it refuses to produce
      a green packet when required evidence is missing or stale.
      <!-- THE PARENT ASKS FOR AN "IT". Seven sub-cells built the manifest (.a),
      the resolver (.b), the validator (.c), the freshness recomputation (.d),
      the truth tables (.e), the emission (.f) and the packet (.g). NONE OF THEM
      IS THE "IT" -- so the parent was not marked on the strength of its parts.
      `readiness-generator.ts` composes them, and composing them is where the
      pieces stop agreeing.

      THE ALPHABETS DO NOT MEET. .d produces four freshness verdicts. .f names
      five zero-conditions. `unknowable-no-kind-for-a-dependency` IS NOT ANY OF
      THE FIVE. Calling it fresh reports a check nothing performed; calling it
      stale claims a change nobody observed. It is filed as `blocking`, with the
      reason in a named register (UNMAPPABLE_VERDICTS) rather than picked
      quietly in a switch arm -- a mapping that chooses a bucket without saying
      so is how two honest subsystems produce one dishonest answer.

      A RESOLUTION FAILURE IS NOT ONE CONDITION EITHER. Five of the resolver's
      six outcomes are not `resolved`, and they map to THREE conditions because
      they need different people: resolves-to-nothing is MISSING and the
      pipeline produces it; digest-does-not-match is FAILED and somebody owns
      it; a mutable name, a cross-class substitution, and an id two requirements
      both claim are decisions, so BLOCKING.

      THE ORDER IS RESOLVE, THEN AGE, THEN APPROVE, stopping at the first
      answer. A requirement whose evidence resolves to nothing has no capture to
      be stale, and asking a person to approve something that does not exist
      spends the one input no pipeline can produce.

      THE SCAN RUNS THE THING against the parent's own sentence. A baseline that
      IS GREEN FIRST -- a generator that refuses everything satisfies the
      sentence and is useless -- then NINE WITHDRAWALS ONE AT A TIME, each
      required to refuse green FOR ITS OWN REASON, covering 5 of 5 conditions,
      with the human gates compared before and after every one. Plus a
      deterministic seal that is identical across two runs and MOVES when only
      the generator version moves while the packet contents stay byte-identical.
      And all seven sub-cell scans are INVOKED, not trusted: 7/7 green at the
      moment this claim is made.

      AND THE SCAN CAUGHT A DEFECT IN THE GENERATOR ITSELF. `failed` was
      unreachable, because the expected digest was read off the ref being
      checked -- both sides of the comparison out of the same box. It showed up
      as one of the five conditions never being raised by any withdrawal. The
      manifest-side digest is now a separate input, and the withdrawal battery
      covers all five.

      26 spec tests; scan PASSES with 12 negative controls, three of them
      silence controls (a change after the instant being judged; a change the
      estate says does not invalidate; the green baseline itself). -->
  - [x] S11.20.a Define versioned readiness manifest for
        release/commit/artifact/ environment/target, scope/requirements,
        dependency graph, required evidence types, owners/approvals, freshness,
        waivers, and stop policy. Evidence: readiness schema.
        <!-- A REQUIREMENT SET RESOLVED FOR A RELEASE ID IS A SET RESOLVED FOR A
        NAME.

        CREDIT, AND THE SUBSTRATE IS MOSTLY THERE. gate-definitions.ts has gate
        types, audiences, per-stage severities, ownership, and a
        RequiredEvidence carrying BOTH maxAgeSeconds and invalidatedBy --
        freshness by age and by dependency change, which is the pair most
        schemes get half of. UNAVAILABLE_BEHAVIOURS has no `pass`, absent as a
        TYPE rather than as a rule, with the reason written down: a gate that
        passes when its evidence is missing passes when the thing it checks is
        broken in exactly the way that hides the evidence. gate-runs.ts gives
        every result a provenance with the target revision, target hash, gate
        version, evaluator and policy. release-locks.ts freezes ten inputs, two
        of them `gates` and `approvals`.

        AND THE PER-RELEASE SET ALREADY EXISTS. publish-eligibility.ts has
        RequirementResolution: a releaseId, the destination it was resolved FOR,
        the bindings it was resolved against, the requirements themselves, and
        best of all an `excused` map where every evidence kind deliberately NOT
        required carries the reason why. It even has a fault for a set that was
        resolved for the release and pointed at a destination afterwards.

        THIS CELL WAS WRITTEN ON THE ASSUMPTION THAT NO SUCH SET EXISTED, AND
        ITS OWN SCAN REFUTED THAT ON THE FIRST RUN -- the check "is there a
        module holding both a releaseId and a requirement list" returned
        publish-eligibility.ts and failed the run. The claim below is what
        survived, and it is sharper than the one it replaced.

        WHAT THE SET IS BOUND TO IS A NAME. RequirementResolution carries a
        releaseId and bindings, and NO commit and NO artifact digest -- checked
        structurally by reading its fields, so the day one is added this cell
        fails rather than going quietly stale. Two builds of the same release id
        resolve the same requirements, and nothing in the set says which bytes
        it was about. That is S11.19.g's artifact-mismatch one level down:
        there, the gate could not tell whether the artifact scanned was the
        artifact released; here, the requirement set cannot say either.

        AND resolvedAt IS A TIME, NOT A VERSION. Two resolutions taken a week
        apart differ by an instant, and nothing says whether the REQUIREMENTS
        changed between them. A packet assembled against a shorter list looks
        exactly as complete as one assembled against a longer one -- and the
        older one looks better, because it satisfied less.

        SO THIS MANIFEST ADDS FOUR THINGS TO A SHAPE THAT MOSTLY EXISTS: the
        commit and the artifact digest, a VERSION of the requirement set, a
        dependency graph between requirements (topologically ordered, returning
        null on a cycle rather than looping), and a stop policy. Everything else
        is the estate's vocabulary, arranged.

        requirement-class-with-no-requirement is a finding on purpose: a release
        may legitimately need no screenshots, and the manifest should have to say
        so out loud, because an absent class and a class nobody thought of look
        identical.

        Measured: 9 requirement classes, 4 targets, 3 stop policies, 11 fault
        kinds. 16 spec tests; scan PASSES with 10 negative controls, including
        that a dependency cycle terminates and that a missing class is reported
        BY NAME. -->
  - [x] S11.20.b Resolve requirement/ledger IDs to code, tests/scans, migration,
        screenshots/traces, performance/SLO, runbooks, approvals, and immutable
        evidence hashes. Evidence: traceability resolver tests.
        <!-- RESOLVING TO A NAME RESOLVES TO WHATEVER IS THERE NOW.

        A requirement that resolves to evidence/scans/dependency.json is
        satisfied by whatever that path holds at the moment somebody looks. A
        requirement that resolves to a digest is satisfied by one sequence of
        bytes and by nothing else. Both look like a resolved requirement in a
        packet; only the second is still true tomorrow.

        This is the same rule S11.19.e found in the workflows -- a tag is a name
        the owner can move -- arriving one level up, which is why the resolution
        targets are distinguished by TYPE rather than by a convention about
        where evidence lives.

        MEASURED AGAINST THE ESTATE: 1,238 tracked files under evidence/, and
        ZERO of them named by a digest. Every one would be reached by a path.

        A RESOLVER THAT CANNOT FAIL IS A LOOKUP TABLE, so the failures are the
        content: an id that resolves to nothing, an id that resolves to evidence
        of the wrong CLASS, an id two requirements both claim, an id that
        resolves to a mutable name, and an id whose digest does not match what
        the manifest recorded. Each is a different repair and only the first is
        obvious, so the resolver reports the KIND rather than returning null.

        AND A CLASS MISMATCH IS THE ONE THAT LOOKS FINE. A requirement for a
        runbook, resolved to a passing test, IS resolved: there is an id, there
        is an artifact, there is a digest, and a packet counting resolved
        requirements counts it. What is missing is that a test does not tell
        anybody what to do at three in the morning. S11.20.e is where one result
        substituting for another becomes a rule; this is where it becomes
        visible.

        The claim map is passed IN rather than derived, because whether an id is
        claimed twice is a fact about the WHOLE set and resolving one
        requirement in isolation cannot see it.

        Measured: 3 resolution targets, 6 outcomes all exercised by one set, 9
        requirement classes all resolvable. 16 spec tests; scan PASSES with 10
        negative controls. -->
  - [x] S11.20.c Validate artifact/commit/version/target/scope match, file/link
        existence, hash/signature, schema, command/result, complete pages/
        matrices, owner, timestamp, expiry, and source provenance. Evidence:
        validator tests.
        <!-- A VALIDATOR THAT CHECKS THE RECORD CANNOT CHECK THE WORLD.

        The cell lists twelve checks in one sentence and they are not one kind
        of thing. Some are about the record on its own: does it have an owner,
        does it parse, does the artifact it names match the artifact the
        manifest names. Some are about correspondence with something that has to
        be PRESENT: does the file exist, does the hash match the bytes, does the
        signature verify. And one is about correspondence with something that
        only exists while it happens -- did the recorded command produce the
        recorded result.

        Those three fail differently and run in different places.

        An INTERNAL check runs anywhere, needs nothing, and is the cheapest
        thing in a release pipeline, so there is no excuse for skipping it. A
        CORRESPONDENCE check needs the artifact on the machine, which is exactly
        what S11.19.c measured is absent when the secret scan runs. A RE-RUN
        check is not a validation at all: it is another execution, and "the
        recorded command produced the recorded result" is only knowable by doing
        it again, at the cost the original run cost. It is reported as unchecked
        even when the validator is permitted to re-run, because the comparison
        belongs to whoever pays for it.

        THE RUN DIGESTS A REAL EVIDENCE FILE AND VALIDATES ONE ENTRY TWICE.
        With the world present: 14 of 15 passed, nothing failed, and it may be
        called validated. With the world absent: ALL ELEVEN internal checks
        still passed, one failed, three were unchecked, and it may not.

        A packet full of internal passes is a packet that is internally
        consistent. That is worth something, and it is not what "validated"
        sounds like: a record naming a file that was deleted, with an owner and
        a timestamp and a schema that parses, passes every internal check there
        is.

        EVERY CHECK RETURNS A RESULT, INCLUDING `unchecked`. A validator that
        omits the checks it could not run produces a report where absence looks
        like success -- the failure this initiative keeps finding, and the same
        distinction S11.18.g had to make about a scan that did not run. A hash
        nobody recorded, a signature nobody attempted, and bytes nobody digested
        are each unchecked rather than passed.

        Measured: 15 checks over 3 natures (11 internal, 3 correspondence, 1
        re-run), partitioning exactly. 16 spec tests; scan PASSES with 12
        negative controls, including a real digest mismatch against altered
        bytes. -->
  - [x] S11.20.d Recompute freshness from code/contract/schema/dependency/
        policy/model/corpus/fixture/environment/standard changes rather than
        trusting a green label. Evidence: staleness mutation tests.
        <!-- A LABEL SAYS WHEN SOMEBODY LOOKED. FRESHNESS IS WHETHER ANYTHING
        MOVED SINCE.

        CREDIT, AND staleness.ts IS ONE OF THE BEST THINGS IN THE KIT. An
        INVALIDATION_REGISTRY of 27 named mutations across 8 dependency kinds,
        each carrying `invalidates` and a REQUIRED `why` on BOTH answers -- 10
        of them saying false. The comment explains that the negative answer
        matters more: "a title correction does not invalidate a rights check" is
        a judgement somebody made, and an unexplained `invalidates: false` is a
        judgement somebody made to stop the queue filling up, and afterwards
        there is no way to tell them apart.

        Its doc even names the case that justifies the whole subsystem: a
        provider repoints a stable model name at new weights, nothing in this
        system changed, no revision moved, no editor did anything -- and every
        gate answered by that model is now answering a different question, which
        cannot be noticed from inside a single result.

        THE ALPHABET IS SHORTER THAN THE ESTATE. DEPENDENCY_KINDS has eight
        members and the cell names ten input classes. Five map: code→source,
        model→model, policy→policy, environment→target-configuration, and
        dependency is what the registry is about. FIVE HAVE NO KIND AT ALL:
        contract, schema, corpus, fixture, standard.

        Each of those five is load-bearing in this repository. A contract is
        GraphViewContract and TimelineContract. A schema is what the validator
        parses against. A corpus is what an evaluator scores against -- and
        ScoreProvenance already carries a corpusId AND a corpusVersion, so the
        estate VERSIONS the thing it cannot register a mutation for. Fixtures
        are the 25 modules S11.18.d walked. And a standard is WCAG 2.2,
        BS.1770-4, C2PA: all three leaned on in this section alone.

        A KIND THAT DOES NOT EXIST CANNOT HAVE A MUTATION REGISTERED AGAINST IT,
        so no evidence can be invalidated by one. When WCAG 2.2 becomes 2.3,
        every accessibility gate is answering a question nobody asks any more,
        and the mechanism that exists to notice exactly this cannot express it.

        The verdict unknowable-no-kind-for-a-dependency is what keeps the
        recomputation honest. It outranks `fresh` and is outranked by both
        staleness verdicts, because knowing something moved beats not knowing
        whether anything did -- and "fresh" for an unwatchable dependency is a
        report that nothing was watching.

        AND A MUTATION TEST IS THE ONLY WAY TO KNOW A RECOMPUTATION WORKS. One
        mutation per input class, each required to flip the verdict for its OWN
        class, over a baseline checked FIRST and required to be fresh -- the
        same guard S11.17.g needed.

        (One fault kind, mutant-that-changes-it-for-another-input, was
        UNREACHABLE as first written, because it was inferred from the verdict
        and no input could produce it. That is the defect this initiative keeps
        finding in other people's registers, so it was made meaningful instead
        of deleted: it now compares the mutant's filed input to the input its
        change is about, which catches a badly-built battery and is reachable.)

        Measured: 10 input classes, 5 with no kind, 4 verdicts, 27 registered
        mutations. 20 spec tests; scan PASSES with 13 negative controls. -->
  - [x] S11.20.e Evaluate required pass/known limitation/waiver/N/A/human
        approval states and stop-the-line conditions without allowing one result
        to substitute for another evidence class. Evidence: truth tables.
        <!-- A WAIVER IS NOT A PASS. AN N/A IS NOT A PASS. A PERSON SAYING YES
        IS NOT EVIDENCE. AND EACH OF THEM IS EASIER TO OBTAIN THAN THE THING IT
        REPLACES -- which is why this cell asks for a TABLE and not a rule. A
        rule saying "do not let one result substitute for another" is advice. A
        table says, for every state a requirement can be in and every policy the
        release runs under, exactly what the packet may claim.

        CREDIT, BECAUSE THE ESTATE IS GOOD AT THE STATES. gate-panel.ts counts
        blocks, waived, satisfied and unknown as four separate figures and
        carries a fault named unknown-counted-as-satisfied for exactly the
        collapse this cell fears. gate-runs.ts returns 'waived' rather than
        folding it into a pass. comparisons.ts requires a waiverId before a
        verdict may become waived.

        THE GAP IS NOT IN THE STATES. IT IS IN THE CLASSES. Measured off the
        kit: 18 alphabets name evidence, 89 distinct members between them, and 3
        of 153 pairs share anything at all. There is no single evidence
        vocabulary -- there are many. And `RequiredEvidence.evidenceType`, the
        field a gate definition uses to say what it requires, is typed `string`.

        Free text, belonging to no alphabet. Any two equal strings match and
        nothing constrains what the strings are. So the rule this cell asks for
        CANNOT BE STATED at the place where requirements are defined; it can
        only be checked afterwards by somebody who already knows which
        vocabulary was meant.

        Alongside it: 14 distinct ways to say "we did not check", spread across
        122 of 2,499 exported alphabets, 32 of which put one of them beside a
        passing member in the SAME alphabet. That is where a substitution stops
        being a decision and becomes a matter of which comparison somebody
        happened to write.

        THE ANSWER IS TWO TABLES. Six states by three policies, printed by the
        scan: blocking changes the answer for exactly one state (unmet), and
        awaiting-human is the only state no evidence can resolve, because it is
        the absence of an act by a person -- a generator that resolves it has
        forged a signature, and that is a fault kind with a before/after check
        behind it rather than a value check.

        And a substitution matrix false off the diagonal except for two
        exceptions written down with their reasons, out of 72 off-diagonal
        pairs, asymmetric (a screenshot may stand in for a trace; a trace may
        not stand in for a screenshot), with NOTHING AT ALL permitted to satisfy
        an approval. An exception nobody stated is indistinguishable from a
        mistake.

        Also: advisory-only genuinely disarms a stop-the-line condition, and
        that is reported (stop-the-line-disarmed-by-the-policy) rather than
        silently allowed -- the policy is doing what it says, which is precisely
        why it belongs in the packet.

        37 spec tests; scan PASSES with 15 negative controls, four of them
        silence controls. -->
  - [x] S11.20.f Emit green only with zero missing/stale/failed/blocking/
        unapproved items; otherwise emit exact blockers and remediation while
        preserving unchecked human gates. Evidence: negative fixtures.
        <!-- NAMING WHAT IS WRONG AND SAYING WHAT TO DO ABOUT IT ARE DIFFERENT
        ACTS, AND ONLY ONE OF THEM HAS BEEN DONE AT SCALE.

        Counted off the kit's own source: 754 exported fault and blocker
        alphabets carrying 6,592 distinct kinds between them. This estate is
        extremely good at naming precisely what is wrong. Maps joining a fault
        kind to what somebody should do about it: ONE, with 11 rows.
        capacity-plugins.RESERVATION_FAULT_REMEDIATION. 0.17%.

        Widening the key side to anything at all finds a second map --
        provenance-inspector.ACTION_REQUIREMENTS, 80 rows -- and it says what a
        subject REQUIRES rather than what fixes it. So the figure is a fact
        about the estate and not about the regex that found it. (My own
        registers are part of that 6,592 and join none of it. The count includes
        my contribution to the problem.)

        CREDIT, AND THE TWO PLACES THAT DO IT ARE THE MODEL. capacity-plugins
        names its remedies change-the-request, wait-or-retarget,
        ask-for-more-quota, ask-somebody-else and nothing-the-requester-can-do
        -- every one says WHO acts, and the last is the honest answer nobody
        writes. authorization maps every disposition to a remediation and
        WITHHOLDS one for a concealed object, because a remediation for a
        concealed object confirms the object.

        THE FIVE ZERO-CONDITIONS ARE NOT ONE LIST. The cell names missing,
        stale, failed, blocking and unapproved and treats them as one set of
        counters. Only TWO of them are cleared by running something. Re-running
        a failure reproduces the failure; re-running past a block produces the
        same block; and no pipeline produces an approval.

        So "re-run the release pipeline" is right twice out of five and wrong
        specifically on the condition that is actually holding the release --
        which is the sentence somebody reads at three in the morning before
        running the pipeline again.

        A remedy therefore carries an ACTOR and an INSTRUCTION. A blocker with
        no remedy is a fault; a remedy addressed to the pipeline for a condition
        the pipeline cannot clear is a fault; and "fix it" / "investigate" /
        "TBD" are instructions that name no act by anybody.

        SIX NEGATIVE FIXTURES, one per condition plus a clean control -- because
        five detectors that fire on everything would pass all five of the
        others. All 6 correct, each raising its own condition and no other.

        AND THE HUMAN GATES COME OUT AS THEY WENT IN. Preservation is a claim
        about what the emitter did NOT do, so the check holds the BEFORE and
        compares field by field; an unchecked gate becomes an `unapproved`
        blocker rather than being resolved by something that runs.

        29 spec tests; scan PASSES with 16 negative controls, four of them
        silence controls. -->
  - [x] S11.20.g Generate deterministic signed/checksummed packet and machine/
        human summaries from the same evidence, and detect generator drift.
        Evidence: reproducibility test.
        <!-- A DIGEST ANSWERS "DID THE BYTES CHANGE". GENERATOR DRIFT IS THE
        CASE WHERE THE BYTES CHANGED AND THE EVIDENCE DID NOT -- WHICH ONE
        DIGEST CANNOT SAY.

        Two runs produce different packets. Three completely different things
        could have happened: the evidence moved, the generator was upgraded, or
        the generator is not deterministic. Only the third is a bug; the second
        is a fact somebody needs BEFORE comparing anything; the first is
        ordinary. A single checksum over the packet reports all three
        identically.

        So: two digests plus a generator identity, and compareRuns returns
        reproduced / generator-drift / nondeterministic-generator /
        evidence-changed. An evidence change is answered FIRST, because it
        explains any packet difference and nothing about the generator may be
        inferred from it.

        MEASURED, AND THE ESTATE IS EXCELLENT AT THE HARD HALF. ZERO clock
        reads, argless date constructions or random calls in executable code
        across 309 kit modules -- measured with comments stripped and a POSITIVE
        CONTROL on the stripper, because a grep that finds nothing and a grep
        that cannot see produce the same output. The 5 files the raw patterns
        match are all doc comments describing the defect. 8 modules canonicalise
        before hashing, and build-golden NAMES its per-run provenance fields
        rather than dropping them quietly, because "the golden does not cover
        this" and "the golden covers everything" are different claims.
        Determinism is not the gap.

        THE GAP IS THAT ALMOST NOTHING RECORDS WHAT PRODUCED IT. 69 modules
        carry a digest. 9 name a producer. 5 of 68 do both, once this register
        excludes ITSELF from its own count -- it is one of the six, and a
        register that counts its own family is measuring itself. So a checksum
        in this estate is a fact about bytes with no fact about the code that
        wrote them, and "did the generator drift" has no field to read.

        AND THE EXCLUSION LIST IS WHERE A GENERATOR HIDES ITS OWN INSTABILITY,
        so an excluded field that is not actually per-run is a fault, and the
        list actually used is passed in rather than read from the constant.

        THE CHECKSUM IS FNV-1a 64-BIT AND IT IS NOT A SIGNATURE. A real, named
        algorithm, cross-checked against sha256 over 84 comparisons of the same
        canonical bytes with 0 disagreements -- so the verdicts are a fact about
        the bytes and not about the checksum. It meets 1 of the 4 things a
        signature is asked for, and NO digest computed by the same process that
        produced the bytes can meet the other three, whatever the algorithm.

        That is why "stored under a name that claims a signature" is a fault
        about the field NAME rather than a keyword grep over prose: the one
        description doing the right thing is the one that says "this is not a
        signature", and a prose grep flags exactly that one.

        (The seal check is a PROBE, not an inspection: change only the generator
        version and require the value to move. Written as an inspection first,
        it was unreachable -- the same defect this initiative keeps finding in
        other people's registers.)

        36 spec tests; scan PASSES with 18 negative controls, five of them
        silence controls. -->
- [ ] S11.21 Migrate Tara to the shared kit incrementally. Evidence: each slice
      preserves public API and behavior.
      <!-- DELIBERATELY LEFT UNCHECKED, with .a-.g all closed.

      The parent's evidence is "each slice preserves public API and behavior".
      That is a claim about slices that HAVE BEEN PERFORMED. None has. .a-.g
      built the preconditions -- the freeze, the ordering, the compatibility
      model, the dual-write model, the lane matrix, the rollback model and the
      deletion gate -- and every one of them is a statement about what a
      migration would have to satisfy, not a record of one that happened.
      Marking this would claim that slices preserved behaviour when no slice
      ran.

      AND THE SUB-CELLS' OWN MEASUREMENTS SAY THE MIGRATION CANNOT PROCEED AS
      SPECIFIED. Four independent readings, each with a positive control:

        S11.21.a  3 of 8 aspects cannot be frozen detectably: performance has an
                  instrument and no recorded number, accessibility has features
                  and no baseline, and no fixtures policy exists at all.
        S11.21.b  1 of 10 slices (branded IDs/envelopes) has NOTHING in Tara to
                  migrate and 3 more are thin; the same detectors find all four
                  in the kit, so the slice list describes the kit.
        S11.21.d  All 16 hand-written prisma mutations are in two seed scripts
                  and there are 0 mutating route handlers, so there is no
                  application call path to migrate one at a time.
        S11.21.e  4 of 7 lanes run on no Tara project, and only 2 both run here
                  and produce something a before and an after can be held
                  against.

      WHAT WOULD CLOSE IT. Slices performed against the frozen manifest, with
      each one's before/after comparison recorded on the two lanes that can
      carry one. That is an act on the estate, not an analysis of it, and it is
      the same class of thing S11.2.g was left open for.

      This follows the S11.20 precedent in the other direction: there the parent
      asked for an "it" that could be built and verified, so it was built. Here
      the parent asks for work to have been done. -->
  - [x] S11.21.a Freeze Tara public package exports, contracts, routes, BFF/UI
        behavior, persistence/events/jobs, fixtures policy, performance, and
        accessibility baselines. Evidence: pre-migration manifest.
        <!-- A FREEZE IS A PROMISE THAT A VALUE WILL NOT CHANGE. YOU CANNOT MAKE
        THAT PROMISE ABOUT A VALUE NOBODY HAS.

        The cell names eight aspect classes and treats them as one list. They
        are FOUR KINDS, and only one of the four can show that a migration slice
        broke something:

          an-artifact-a-program-reads       record, migrate, re-read, compare
          an-instrument-that-records-nothing  measuring is not recording
          a-description-somebody-wrote      cannot fail; a person re-reads it
          nothing-exists

        READ OFF THE REAL TARA ESTATE, five of eight are readable artifacts: 31
        package exports across 9 libs, 30 route files, 24 prisma models, 106
        spec files, and exactly ONE file declaring a schema. (One. For 31
        exports. That is the contracts baseline.)

        THREE ARE FROZEN IN NAME ONLY.

        PERFORMANCE: 4 files measure -- a real WebVitalName union covering FCP,
        LCP, FID, CLS, TTFB, INP -- and no recorded number exists outside a spec
        file. "We can measure it" and "we know what it was" are different
        claims, and only the second survives a migration.

        ACCESSIBILITY: 5 files, 15,226 bytes, in the two accessibility
        directories. What is in them is a WebVTT caption validator and a
        SkipLink component. Those are accessibility FEATURES. A feature is not a
        baseline, and the difference is whether anything records how many
        violations there are NOW.

        FIXTURES POLICY: the word "fixture" appears in 25 files, 6 of them the
        Playwright test-fixture sense -- NextFixture, WorkerFixtureValue,
        TestFixtureValue. Nothing anywhere states a policy about serving
        substitute data. SAME SPELLING, DIFFERENT WORD. A grep that finds 25
        files, none of which is about the thing being asked for, is the most
        expensive kind of confirmation.

        EVERY ABSENCE CLAIM CARRIES A POSITIVE CONTROL. Before reporting that a
        directory holds no accessibility baseline, the run shows the directory
        exists, holds bytes, and that the SAME READER finds the 10 exports that
        ARE there. Otherwise "no baseline" and "bad path" produce one output.

        AND compareAspect HAS THREE ANSWERS, not two: unchanged, changed, and
        cannot-tell. A migration reporting "no differences" for an aspect with
        no baseline has reported that nothing was watching.

        Two controls run the comparison against the REAL baselines: dropping one
        export and dropping one prisma model each show up as `changed`.

        23 spec tests; scan PASSES with 13 negative controls. -->
  - [x] S11.21.b Order independently reversible slices for branded IDs/
        envelopes, capability/route registry, lifecycle/revisions/gates,
        persistence/jobs, API/security, provider/trust, bundle/publish,
        shell/UI, collaboration/taxonomy, and observability/testing. Evidence:
        migration plan.
        <!-- AN ORDER IS A LINE. REVERSIBILITY IS A GRAPH. AND THE GRAPH PEOPLE
        LOOK AT IS THE WRONG ONE.

        "Independently reversible" says that after slice B has landed, A can be
        undone without undoing B. That is false whenever A and B share anything
        -- and an import edge is only ONE way to share. Two slices that never
        import each other but both edit the same file are entangled exactly as
        thoroughly, and that entanglement is invisible to a dependency graph,
        which is the graph a migration plan is normally drawn from.

        So a pair is in one of THREE relations: independent, ordered-by-a-
        dependency, entangled-by-a-shared-file.

        MEASURED ON THE REAL ESTATE, GOOD NEWS TWICE OVER:
          12 Tara projects, 5 import edges, 5 of 132 ordered project pairs
          => 0 of 45 SLICE pairs ordered by a dependency
          12 of 45 slice pairs share at least one file (33 placements)
          25 of 566 files touched by more than one slice

        An import graph would call all 12 of those pairs independent. Worst:
        api-security x shell-ui share 12 files; api-security x bundle-publish 7.
        3 of 10 slices survive as reversible-alone in the cell's own order.

        AND THE BAD NEWS: THE SLICE LIST DESCRIBES THE KIT, NOT TARA. The same
        detectors over both estates:

          branded-ids-envelopes         0 in Tara,  7 in the kit
          capability-route-registry     1 in Tara, 91 in the kit
          lifecycle-revisions-gates     3 in Tara, 89 in the kit
          provider-trust                2 in Tara, 13 in the kit

        One absent, three thin. THE KIT COLUMN IS THE POSITIVE CONTROL: an
        absence measured with a detector that finds nothing anywhere is not an
        absence, it is a broken detector. And shell-ui reading 159 in Tara
        against 2 in the kit shows the reference is not simply the bigger
        estate -- the kit is a contract library with no React in it.

        YOU CANNOT ORDER A SLICE THAT HAS NOTHING TO MIGRATE. A plan whose first
        slice is "branded IDs and envelopes" is a plan for INTRODUCING them,
        which is a different job with a different rollback: there is nothing to
        roll back TO. Substance is a state (substantial / thin /
        absent-in-the-subject), not a footnote.

        28 spec tests; scan PASSES with 14 negative controls, three of them
        silence controls. -->
  - [x] S11.21.c Add shared implementation behind existing Tara public adapters
        without changing consumer imports/contracts. Evidence: compatibility
        tests per slice.
        <!-- AN ADAPTER PRESERVES A SHAPE FOR SOMEBODY, AND A TEST THAT IMPORTS
        THE IMPLEMENTATION HAS WALKED PAST THE ADAPTER TO CHECK IT.

        Both halves of this cell turn on a question nobody asks first: WHICH
        imports, BY WHOM.

        FIRST, WHO CONSUMES. Measured over the WHOLE repository, because
        "nothing imports this" is false the moment one file anywhere does: 31
        published specifiers against 81,743 source files.

          outside-the-subject        3   (13 import sites)
          inside-the-subject-only   11
          imported-by-nothing       17

        @tara/features (9 outside), @tara/live-class-booking (3),
        @tara/monitoring (1). That is the entire real subject of "without
        changing consumer imports". For the other 17 -- the whole of
        @tara/analytics, all of @tara/api-client, @tara/content/api and the rest
        -- a compatibility test asserts a shape against itself, and passes for
        the same reason a deleted export would.

        SECOND, HOW THE TESTS REACH IT. A compatibility test has to travel the
        road the consumer travels.

          106 Tara spec files
          2 imports through a public @tara/ specifier
          135 through a relative path into the implementation
          => 1.5%

        Only 2 of 106 spec files could fail if an adapter stopped re-exporting
        something. The rest test the implementation; the public specifier could
        drop an export entirely and every one of them would still pass, for the
        same reason it passed before.

        THAT IS A COMPARISON WITH BOTH SIDES OUT OF THE SAME BOX, wearing
        different clothes. The point of an adapter is that the public name and
        the implementation MAY DIFFER. A test that reads the implementation has
        removed the only difference it was there to detect.

        14 of 31 exports need a compatibility test. 1 is backed by one that
        could fail.

        The claims are DERIVED from what the specs actually import, never
        declared -- a declared claim is a promise, and this asks what is true.

        23 spec tests; scan PASSES with 15 negative controls, two of them
        silence controls. -->
  - [x] S11.21.d Migrate one call path/state/data flow at a time with expected
        revision/idempotency/events/audit and no dual-write divergence.
        Evidence: slice integration tests.
        <!-- A DUAL WRITE IS ONE ACT WRITTEN TWICE, AND FOUR OF THE FIVE THINGS
        THIS CELL ASKS TO PRESERVE COUNT ACTS.

        The state is the easy one: write the same value to both stores and they
        agree. Revision, idempotency, events and audit are LEDGERS, and a ledger
        records that something HAPPENED. Write once through two paths and:

          revision      each path allocates its own, so the revision stops being
                        a fact about the edit and becomes a fact about which
                        path wrote
          idempotency   the key is consumed on each side, so a retry the old
                        path correctly refuses is accepted by the new one and
                        the act happens twice
          events        every consumer receives it twice, and a consumer that is
                        itself idempotent hides this until one that is not
                        is added
          audit         the trail says the person did it twice -- the one record
                        that exists to be read by somebody reconstructing what
                        happened

        SO THE STATE CAN CONVERGE PERFECTLY WHILE EVERY LEDGER DIVERGES. A
        dual-write check that compares the two stores row by row and reports no
        differences is telling the truth about 1 of 5 facts, and it is the one
        that was never in danger.

        AND DIVERGENCE IS FOUND BY READING BOTH, WHICH IS THE ONE THING A
        CUTOVER NEVER DOES. Serve from the old side, then the new side; neither
        configuration reads both, so neither can see a difference. Detecting it
        needs a third act -- a compare read -- that serves nobody.

        (The per-act ledgers are different and worse: a doubled event is visible
        from a SINGLE side, because the duplicate lands on the side you are
        already reading. The reason nobody sees it is that nobody counts.)

        CREDIT, BECAUSE THE ESTATE ALREADY KNOWS THIS.
        protection.MIGRATION_PHASES is expand → dual-write → backfill → verify →
        read-switch → contract, with `verify` sitting exactly between the
        backfill and the read switch, and a comment explaining that separating
        the read switch from the backfill keeps the old value available while
        the new path takes production traffic. That `verify` IS the compare
        read. It also carries `backfill-without-dual-write`, the silent one.

        The phase list is PARSED out of protection.ts, never restated -- a
        register carrying its own copy of the estate's migration model cannot
        notice the estate changing it.

        MEASURED ON TARA: all 16 hand-written prisma mutations are in TWO SEED
        SCRIPTS (prisma/seed.ts 10, prisma/seed-test.ts 6), and there are ZERO
        mutating Next route handlers. So in the subject as it exists there is no
        application call path for this cell to migrate and nothing to
        dual-write. Reported with a POSITIVE CONTROL, because the reader DID
        find writes -- it found them all in the seeds.

        (An earlier shell grep of mine said 550 write sites. Those were
        `libs/tara/database/src/generated/client/models` and
        `node_modules/.prisma`: generated type declarations, not call sites. The
        scan excludes both, which is why it says 16.)

        24 spec tests; scan PASSES with 17 negative controls, three of them
        silence controls. -->
  - [x] S11.21.e Run targeted unit/contract/integration/browser/performance/
        accessibility/stub scans and compare behavior after every slice.
        Evidence: per-slice report.
        <!-- A SUITE GIVES YOU A VERDICT. THE CELL ASKS YOU TO COMPARE
        BEHAVIOUR. THOSE ARE NOT THE SAME THING.

        A suite that was green before and is green after has told you the same
        assertions still hold -- which is compatible with every behaviour the
        assertions do not mention having changed. Comparing behaviour needs a
        RECORDED OUTPUT: a golden, a report file, a measured number. 2 of the 7
        lanes (unit, integration) produce only a verdict.

        AND A LANE IS A TARGET ON A PROJECT, WHILE A SLICE CUTS ACROSS PROJECTS.
        7 lanes x 12 Tara projects = 84 cells. Measured off every project.json
        and the root package.json:

          runs on the project in question      17
          runs but on another subject          33
          right name, unreadable target        12
          does not exist                       22

          unit           12 of 12
          contract        0 of 12
          integration     0 of 12
          browser         2 of 12
          performance     3 of 12
          accessibility   0 of 12
          stub-scan       0 of 12

        FOUR OF SEVEN LANES RUN ON NO TARA PROJECT: contract, integration,
        accessibility, stub-scan.

        A LANE THAT EXISTS SOMEWHERE ELSE IS NOT A LANE FOR THIS SUBJECT -- the
        same shape as the fixtures trap in S11.21.a. The repository HAS
        `verify:operations:v1-a11y-findings`, `…-a11y-waivers` and
        `…-axe-policy`. Three accessibility scripts, all real, all green, and
        every one points at `libs/oshun/workbench-kit/tools`. They scan the kit.
        Running them after a Tara slice reports on a codebase the slice did not
        touch.

        SO COVERAGE HAS FOUR ANSWERS, and the middle two are the point.
        `runs-but-on-another-subject` is what would otherwise be counted as
        coverage. `unknown-what-it-runs-against` is for a script with the right
        name whose target could not be read from its command line --
        `contracts:check` is one. Folding that into "runs elsewhere" would
        assert something nobody measured, which is the defect this register
        exists to catch, one level up. (I wrote it the wrong way first and the
        scan's own output -- "points at null" -- is what showed it.)

        THE OVERLAP THAT DECIDES THE CELL: only 2 of 7 lanes both RUN on a Tara
        project AND produce something a before and an after can be held against
        -- browser and performance.

        25 spec tests; scan PASSES with 16 negative controls, three of them
        silence controls. -->
  - [x] S11.21.f Define per-slice enablement/rollback, data compatibility/
        reconciliation, old/new mixed deployment, and removal criteria.
        Evidence: rollback tests.
        <!-- A ROLLBACK GOES BACKWARDS IN CODE AND FORWARDS IN DATA.

        Deploying the previous build returns the code to what it was. It does
        NOT un-write the rows the new build wrote while it was live, and it does
        not un-tell the consumers that adopted the new shape. "Rollback" names
        four different acts and only two of them are an undo:

          code            returns-to-the-previous-value
          configuration   returns-to-the-previous-value
          data            MOVES FORWARD ONLY
          consumers       depends-on-somebody-else

        SO THE ROLLBACK TEST FOR DATA IS NOT "UNDO IT". It is: can the PREVIOUS
        code read what the new code wrote, without error and without loss. A
        compatibility property, tested by running the old reader over new rows.

        And consumers are outside this repository: their deploy is their
        decision and their timetable, so nothing here reverts them.

        THE ESTATE MAKES THIS CONCRETE. Tara's migration history is ONE
        directory -- 20260506000000_initial, 803 lines, 24 CREATE TABLE, 28,081
        bytes. Repository-wide: 189 forward .sql steps and ZERO reverse ones.
        Prisma migrate is forward-only. There is no previous schema to return to
        and no tool step that would return to it.

        (Reported with a POSITIVE CONTROL: the same reader found the 189 forward
        steps, so "no reverse step" is a statement about the tool and not about
        a reader that cannot see SQL.)

        A MIXED DEPLOYMENT IS TWO CLAIMS, NOT ONE. While both versions serve:
        old code reads rows the new code wrote, AND new code reads rows the old
        code wrote. A plan that tests one direction has tested the half that
        gets tested BY ACCIDENT -- every row that existed before the deploy was
        written by the old code. The other half is the one a rollback depends on
        and the one nobody sets up, because writing with the new code is what
        you were doing anyway.

        REMOVAL IS A CLAIM ABOUT TIME. The old path may go when nothing can roll
        back to it, which needs a window that has ELAPSED, not a window that
        exists -- the same distinction S10.9 needed when a grace period was
        anchored to the event it was supposed to outlive.

        24 spec tests; scan PASSES with 15 negative controls, three of them
        silence controls. -->
  - [x] S11.21.g Migrate external consumers and delete legacy implementation/
        adapters/flags only after import/usage graph and full suite prove no
        dependency. Evidence: deletion gate.
        <!-- AN IMPORT GRAPH ANSWERS "WHO IMPORTS THIS BY NAME". A DELETION ASKS
        "WHAT BREAKS". THOSE DIFFER BY EVERY REFERENCE THAT IS NOT A STATIC
        IMPORT.

        Seven reference kinds; four break, two go stale, one is unknowable:

          a-static-import                            breaks-at-build
          a-manifest-or-config-entry                 breaks-at-build
          a-path-mapping                             breaks-at-build
          a-dynamic-import-with-a-literal            breaks-at-runtime
          a-doc-or-a-string                          goes-stale
          a-build-output                             goes-stale
          a-dynamic-import-with-a-computed-specifier UNKNOWABLE

        A DOC TAG GOES STALE AND A MANIFEST ENTRY BREAKS THE BUILD, and both
        look identical to a grep over text. Every mention of @tara/content/api
        in this repository is an `@module` JSDoc tag; @tara/api-client appears
        inside a `describe()` label. A graph that counted text would call both
        load-bearing; one that counts `from` clauses calls both free. Neither is
        wrong and only one is answering the deletion question. (That also
        CONFIRMS S11.21.c: nothing imports either.)

        AND WHEN A SPECIFIER IS ASSEMBLED AT RUNTIME, NO STATIC GRAPH CAN ANSWER
        AT ALL. 243 such sites beside 2,671 literal ones across 81,755 files.
        Real examples: `import(`./messages/${locale}/tara.json`)` -- a Tara
        resource loaded by a name that does not exist until the locale does --
        and a verified `return import(moduleName)`.

        BUT ONLY THE ONES THAT COULD REACH THE SUBJECT COUNT: 225 of the 243. A
        messages path cannot resolve to a bare package specifier, and a gate
        blocking on it would block every deletion in the repository forever,
        which is a gate that has STOPPED ANSWERING rather than one being
        careful. The run compares each site's STATIC PREFIX to the subject; a
        bare identifier has no prefix and always counts.

        AND A GATE IS ONLY AS WIDE AS WHAT IT SEARCHED. "No dependency" is a
        claim about a repository. A claim over 5 directories from a search over
        1 is refused BEFORE anything else -- it is a different question, not a
        weaker answer.

        Two of the four verdicts are unknowable and both outrank `safe`: a gate
        blind to a class of reference that says safe has reported that nothing
        was looking.

        (The reader strips comments and requires expression position. Its first
        version counted a function signature and a doc comment as dynamic
        imports, which is what made the figure move from 476 to 243.)

        23 spec tests; scan PASSES with 20 negative controls, four of them
        silence controls. -->
- [ ] S11.22 Run Tara's complete relevant unit, integration, contract, build,
      accessibility, and Playwright suite after migration. Evidence: reports are
      linked and green.
      <!-- DELIBERATELY LEFT UNCHECKED, with .a-.h all closed. Two independent
      reasons, either of which is sufficient.

      FIRST, "AFTER MIGRATION". No migration happened -- see the S11.21 parent,
      which is left open for the same reason. A suite run before the thing it is
      meant to verify has not verified it.

      SECOND, "REPORTS ARE LINKED AND GREEN" IS FALSIFIED BY .a-.h THEMSELVES.
      Each measured what the corresponding report could honestly say, and the
      answers were:

        .a  10 of 12 verification inputs can be pinned. The clock cannot: 491
            direct platform reads across 113 of 566 files against 5 files with
            a seam. Fixtures cannot: no policy switch exists.
        .b  0 of 106 spec files use a property-based generator; 72 are
            example-only. Credit: 2,866 cases, 4,270 assertions, 0 asserting
            nothing.
        .c  0 of 11 integration subjects are covered by a test that would FAIL
            if the dependency were absent. 0 of 106 spec files carry
            live-connection machinery.
        .d  0 of 8 contract artefacts bind both parties, so 0 of 10 suites have
            one. The generated client declares 14 paths; the provider carries
            16 route modules.
        .e  3 of 9 build gates can stop a change. 4 run on another subject.
        .f  4 of 8 accessibility rows are covered; 2 need an instrument that is
            not present on this machine at all.
        .g  1 of 3 browser artefacts describes the failing execution, in either
            environment. Fixture mode is declared nowhere.
        .h  a rerun that passes is the same observation for a fix and a flake,
            and 2 of 20 combinations are established by one.

      A report that is linked and green is producible from this estate today.
      It would not be false in any single line and it would not mean what the
      cell is asking it to mean, which is the failure every one of .a-.h is
      written against.

      WHAT WOULD CLOSE IT. The migration performed (S11.21), then the suites run
      against it with the gaps above closed or explicitly excepted -- an
      integration lane that fails without its dependency, a contract artefact
      both sides read, and gates that can stop a change. That is work on the
      estate, not analysis of it. -->
  - [x] S11.22.a Pin migrated commit, dependencies, environment, database/
        storage/queue/provider modes, fixtures, accounts/tenants, browser,
        clock, and exact commands. Evidence: verification manifest.
        <!-- A PIN IS A PROMISE THAT A VALUE WILL BE THE SAME NEXT TIME, AND
        THAT PROMISE NEEDS SOMEWHERE TO STAND.

        A commit pins itself -- git records it and nothing downstream can
        disagree. Dependencies pin through a lockfile the installer reads. A
        browser pins through a version a tool honours. Those are values somebody
        writes down and a tool then obeys.

        A CLOCK IS NOT LIKE THAT. Writing "clock: 2026-08-16T00:00:00Z" pins
        nothing unless every read of the current time goes through something
        that can be GIVEN that value. Code calling the platform directly reaches
        past any injection point, and the manifest line becomes a note about
        what somebody intended.

        6 of the 12 inputs need a seam in the code: clock, fixtures, and the
        database/storage/queue/provider modes.

        MEASURED WITH THE SAME DETECTOR ON TWO ESTATES, comments and quoted
        strings stripped:

                              Tara     kit
          Date.now()           103       0
          new Date()           358       0
          Math.random()         27       0
          performance.now()      3       0
          TOTAL                491       0
          files with >=1    113/566   0/331

        Five Tara files carry anything resembling an injectable clock seam,
        against 113 that go straight to the platform. One injection point beside
        hundreds of direct calls is not a seam with exceptions -- it is hundreds
        of code paths and a decoration. A fixtures policy switch: 0 files, which
        S11.21.a found by a different route.

        SO THE VERDICT ALPHABET HAS THREE MEMBERS. A manifest for this subject
        can honestly call 10 of 12 pinned; clock is recorded-but-not-enforced
        and fixtures is unpinnable-no-seam-exists. A manifest claiming all
        twelve produces three findings.

        (THE DETECTOR STRIPS QUOTED STRINGS AS WELL AS COMMENTS. Its first
        version counted one hit in the kit: a register whose prose reads "a
        constant, never `new Date()`" -- a register of source text tripping a
        detector that reads source text, which is a trap this initiative has hit
        before. Template literals are deliberately left alone, because an
        interpolated clock read executes; both directions are negative
        controls.)

        22 spec tests; scan PASSES with 16 negative controls, two of them
        silence controls. -->
  - [x] S11.22.b Run all affected Tara/shared unit/property/known-value/
        component tests with coverage mapping. Evidence: unit report.
        <!-- COVERAGE IS A CLAIM ABOUT WHICH LINES RAN. The cell names four
        kinds because each finds a class of defect the others cannot, and NO
        LINE NUMBER DISTINGUISHES THEM.

          example      the case somebody thought of, exactly as thought of
          property     a case nobody thought of, within reach of its generator,
                       shrunk to say which part mattered
          known-value  a disagreement with a truth from OUTSIDE this repository
                       -- the only kind that catches the whole system being
                       CONSISTENTLY wrong
          component    what renders; cannot tell a right value from a wrong one

        Run one example over a function and every line is covered. Run a
        thousand and the number does not move.

        CREDIT WHERE THE MEASUREMENT FINDS IT. Tara has 2,866 test cases and
        4,270 assertions across 106 spec files, at 1.49 assertions per case, and
        ZERO files declare a case and assert nothing. The classic way a coverage
        number rises without anybody checking anything is absent here.

        AND THE KINDS ARE NOT EVENLY REPRESENTED:

                        Tara     kit
          example         72     319
          property         0       1
          known-value      1       1
          component       33       3
          spec files     106     324

        NONE of Tara's 106 spec files uses a property-based generator. The kit
        column is the control: the same detector finds one there, so the zero is
        a fact about the subject rather than about a detector that finds nothing
        anywhere.

        A suite made entirely of examples has line coverage and NO input-space
        coverage, and that is invisible in every report this cell asks for.
        Coverage is blind to four things it is routinely read as answering,
        including which of the four kinds ran -- all four move the same number.

        17 spec tests; scan PASSES with 12 negative controls, three of them
        silence controls. -->
  - [x] S11.22.c Run real database/storage/queue/provider/publisher/host
        integration, migration, tenant, fault, replay, and restore tests.
        Evidence: integration report.
        <!-- A TEST IS A REAL INTEGRATION TEST IF IT FAILS WHEN THE DEPENDENCY
        IS ABSENT. That is the whole definition, and it is the one nobody
        checks.

        "Real" is doing all the work in this cell's sentence. A suite that
        passes with nothing running has told you about the code and nothing
        about the integration, and the only way to know which one you have is to
        take the dependency away.

        FOUR RELATIONS, NOT TWO:

          connects-to-a-real-one  fails without it. This is integration.
          uses-a-double           passes either way -- CORRECT for a unit test,
                                  and not integration.
          skips-when-absent       REPORTS GREEN. Nothing ran.
          never-touches-it        the code does not reach it at all.

        `skips-when-absent` is the one that turns an honest arrangement into a
        false report. Skipping when the database is down is sensible on a
        developer's machine and catastrophic for a release gate to count,
        because a skipped test and a passing test are the same colour in every
        summary -- the same defect as an aggregate that reports success when a
        status is merely not a failure.

        MEASURED:

          Tara spec files carrying live-connection machinery:   0 of 106
          kit  spec files carrying it, same detector:           2 of 325

          subject      specs  live   relation
          database         2     0   uses-a-double
          storage          5     0   uses-a-double
          queue            1     0   uses-a-double
          provider        11     0   uses-a-double
          publisher        1     0   uses-a-double
          host             0     0   never-touches-it
          migration        1     0   uses-a-double
          tenant           0     0   never-touches-it
          fault           21     0   uses-a-double
          replay           0     0   never-touches-it
          restore          1     0   uses-a-double

        ZERO of 11 subjects are covered by a test that would fail if the
        dependency were absent. Three -- host, tenant, replay -- are not
        mentioned by any spec at all. A report claiming all eleven produces 14
        findings.

        The kit column is the control: the same harness detector finds two files
        there, so the Tara zero is a fact about Tara.

        provesIntegration and failsWhenAbsent are deliberately the SAME
        predicate. The property that makes a test an integration test IS failing
        without the thing, and two functions that happen to agree would invite
        one of them to drift.

        19 spec tests; scan PASSES with 13 negative controls, one of them a
        silence control. -->
  - [x] S11.22.d Run provider/consumer/schema/generated/API/event/job/bundle/
        compatibility/drift contract suites. Evidence: contract report.
        <!-- A CONTRACT IS BETWEEN TWO PARTIES. A CONTRACT TEST THAT ONLY ONE
        PARTY RUNS IS A UNIT TEST WITH A LONGER NAME.

        A provider suite asserting the server's own schema passes whatever the
        client expects. A consumer suite asserting a generated client passes
        whatever the server sends. Both are green, both are honest about what
        they check, and between them they establish nothing about whether the
        two agree.

        FOUR STANDINGS, AND THE FOURTH IS THE ONE THIS ESTATE IS IN:

          shared-by-both-parties                  a real contract
          read-by-one-party                       a unit test, longer name
          read-by-nobody                          it can say anything
          generated-from-a-source-that-is-absent

        THAT LAST ONE IS THE INTERESTING FAILURE. A generated client is a
        snapshot of a specification at the moment somebody ran the generator. If
        the specification is not in the repository, the snapshot CANNOT BE
        REGENERATED -- so the difference between it and the current server
        cannot be computed, and "drift" is the tenth suite this cell names. The
        artefact that exists to detect drift is the one drift is invisible in.

        MEASURED:

          openapi specification documents in the repository:  21
          belonging to the subject:                            0

        Stated beside the reader that found the 21, because an absence measured
        by a reader that finds nothing anywhere is not an absence.

        THE CHEAPEST DEMONSTRATION: the generated client declares 14 paths and
        the provider carries 16 route modules. THEY DISAGREE. Two counts of one
        surface differing is the least you can do to show that no shared
        document is being enforced -- a count, deliberately, not a shape check.

        4 of 5 JSON schema documents (collection, course, sound, teacher) are
        mentioned by no source file anywhere in the subject, its tools or its
        scripts.

        0 of 8 artefacts bind both parties, so 0 of 10 suites have one. A report
        claiming all ten produces 16 findings.

        18 spec tests; scan PASSES with 13 negative controls, two of them
        silence controls. -->
  - [x] S11.22.e Run lint/typecheck/package/bundle/production build and
        dependency boundary/test-fixture/stub/secret scans sequentially.
        Evidence: build/static reports.
        <!-- A GATE IS A THING THAT CAN STOP YOU. THERE ARE THREE WAYS IT
        CANNOT, AND ONLY ONE OF THEM LOOKS LIKE AN ABSENCE.

          does-not-exist            nothing to run
          runs-on-another-subject   green every time, about a codebase this
                                    change did not touch
          has-a-documented-bypass   it stops you unless the variable is set,
                                    and the failure message names the variable

        MEASURED ON THIS REPOSITORY (323 root scripts, 3 husky hooks):

          lint                  can-stop-you             lint
          typecheck             has-a-documented-bypass  SKIP_TYPECHECK=1, 52x
          package               does-not-exist           (nothing)
          bundle                runs-on-another-subject  -> scripts/v3/...
          production-build      can-stop-you             build
          dependency-boundary   runs-on-another-subject  -> workbench-kit/tools
          test-fixture          runs-on-another-subject  -> workbench-kit/tools
          stub-scan             can-stop-you             .husky/pre-commit
          secret-scan           runs-on-another-subject  -> workbench-kit/tools

        3 OF 9 CAN STOP A CHANGE. Three of the four that run elsewhere resolve
        to workbench-kit registers -- my own -- rather than to the thing being
        verified. The same trap as S11.21.e's accessibility scripts.

        AND THE BYPASS IS NOT HYPOTHETICAL. SKIP_TYPECHECK is named in 52 lines
        of the last 200 commit messages. This session took it on every commit,
        with the reason stated in each body. A gate that prints its own escape
        hatch when it fails has told you it is advisory; that is the honest kind
        of hole and it is still a hole.

        "SEQUENTIALLY" IS THE OTHER HALF OF THE CELL. Nine gates in order means
        the first failure stops the rest. A run that stops at gate two has said
        NOTHING about gates three to nine, and listing them as "not failed"
        counts seven absences as seven passes -- the skip-as-a-pass defect
        again. Three outcomes, exactly one green:
        never-ran-because-an-earlier-gate-stopped is the answer to a question
        nobody asked yet.

        (THE BYPASS IS ATTRIBUTED BY PROXIMITY, not co-residence. The scan's
        first run blamed SKIP_TYPECHECK for the stub scan: both live in
        .husky/pre-commit and only the typecheck is guarded. The stub scan has
        no bypass at all -- it blocked two commits this session and both were
        fixed by rewording, not by setting a variable.)

        19 spec tests; scan PASSES with 14 negative controls, three of them
        silence controls. -->
  - [x] S11.22.f Run automated/manual/keyboard/screen-reader/semantic parity/
        visual/zoom/locale accessibility matrix. Evidence: accessibility report.
        <!-- AN ACCESSIBILITY MATRIX HAS TWO KINDS OF ROW. A MACHINE CAN FILL
        ONE KIND, AND THE OTHER KIND IS WHERE THE THINGS AUTOMATION CANNOT SEE
        LIVE.

        5 of 8 are a machine's job. manual, screen-reader and semantic-parity
        need a person operating an assistive technology, and they are NOT the
        easy rows left over -- a control with a perfect accessible name that
        announces at the wrong moment passes every automated check ever written,
        and semantic parity compares what a person HEARS to what a person SEES,
        which is not a property of a tree.

        AND A ROW HAS A FURTHER STANDING: whether the instrument it needs is
        present WHERE THE RUN HAPPENS.

          INSTRUMENTS on this machine
            a screen reader:   ABSENT (orca/nvda/voiceover)
            at-spi2 bridge:    present
            an axe runner:     @axe-core/playwright
            a browser driver:  @playwright/test

          row              filler                   files   standing
          automated        a-machine                    0   fillable-and-not-filled
          manual           a-person...                  0   needs-a-person
          keyboard         a-machine                   39   filled
          screen-reader    a-person...                 17   instrument-absent-on-this-machine
          semantic-parity  a-person...                 90   instrument-absent-on-this-machine
          visual           a-machine                   34   filled
          zoom             a-machine                    5   filled
          locale           a-machine                   76   filled

        TWO ABSENCES, TWO COMPLETELY DIFFERENT MEANINGS. @axe-core/playwright is
        installed and 38 files in this repository use it, ZERO of them in the
        subject -- so an unfilled automated row is a statement about the
        SUBJECT. No screen reader is installed anywhere here -- so an unfilled
        screen-reader row is a statement about THIS MACHINE. A matrix that
        reports them the same way has told a reader nothing. (This is also why
        earlier sessions refused S11.5.f.)

        4 of 8 rows are covered. A matrix claiming all eight and calling itself
        complete produces 5 findings.

        (The automated row counts IMPORTS of the runner, not occurrences of the
        word. The first run called two files that merely mention "axe" filled;
        mentioning a tool and running it are different.)

        21 spec tests; scan PASSES with 14 negative controls, two of them
        silence controls. -->
  - [x] S11.22.g Run all critical live-data and fault-state Playwright journeys
        with fixture mode explicit. Evidence: browser report/traces/screenshots.
        <!-- AN ARTEFACT OF THE RETRY IS NOT AN ARTEFACT OF THE FAILURE.

        Whether a failing browser run can be investigated depends on TWO
        settings that are read separately and mean something only together.
        PARSED from apps/tara/web/playwright.config.ts:

          retries: process.env.CI ? 2 : 0
          screenshot: only-on-failure
          trace: on-first-retry
          video: on-first-retry

        LOCALLY, retries is 0. There is never a first retry, so NO TRACE AND NO
        VIDEO ARE PRODUCED AT ALL. Both lines are individually sensible and the
        combination is a setting that does nothing. 1 of 3 artefacts describes
        the failure.

        IN CI, retries is 2, so both ARE produced -- from a DIFFERENT EXECUTION
        than the screenshot. The screenshot is of the run that failed; the trace
        is of a later run that may have passed. If the failure was a race, the
        trace is of the case that WORKED. One bundle, two events, presented as
        one. Still 1 of 3.

        So the answer has THREE members, and `a-different-execution` is the
        expensive one, because it is the one that LOOKS like evidence.

        AND "FIXTURE MODE EXPLICIT" IS A CLAIM ABOUT WHERE THE DECLARATION IS. A
        journey whose title says "live data" has declared it to a READER; the
        substitute is installed on the other side of the glass -- a server, a
        route handler, an environment value -- and a title cannot reach it. Only
        a declaration made where the fixture is INSTALLED can be wrong in a way
        that fails.

        Of the subject's 11 e2e files: 0 declare it where it is installed, 0
        where it is read. `not-declared`.

        This reproduces S11.2.f's finding independently, in a different
        configuration file and a different app.

        19 spec tests; scan PASSES with 15 negative controls, one of them a
        silence control. -->
  - [x] S11.22.h Triage every failure, rerun affected/full gates, and link final
        current green reports with no stale result. Evidence: zero open
        failures.
        <!-- A RERUN THAT PASSES IS THE SAME OBSERVATION FOR A FIX AND FOR A
        FLAKE. WHAT TELLS THEM APART IS WHETHER ANYTHING CHANGED IN BETWEEN.

        A test fails, somebody looks at it, does something, runs it again, and
        it passes. Two completely different stories fit that: the change fixed
        it, or it was intermittent and this time went the other way. The passing
        run is identical in both.

          disposition             changed  passed  answer
          fixed                   true     true    established
          fixed                   false    true    indistinguishable-from-a-flake
          a-flake                 true     true    indistinguishable-from-a-flake
          a-flake                 false    true    established
          an-environment-problem   any     true    a-claim-a-rerun-cannot-support
          a-known-limitation       any     true    a-claim-a-rerun-cannot-support

        2 of 20 combinations are established by a rerun. An environment problem
        and a known limitation are claims about the world OUTSIDE the test and
        are settled by running it again in no combination at all.

        The three-member answer is what stops the overreach in both directions:
        "fixed" and "it was a flake" are equally unearned when nothing changed.

        AND CALLING SOMETHING A FLAKE IS A DECISION, NOT AN OBSERVATION. It says
        the failure was not about the code -- a claim about every future run,
        made from one. So it owes a count of how often it has been seen and
        something a person can look at. A flake with neither is a way of closing
        a ticket.

        "NO STALE RESULT" IS S11.20.d WEARING DIFFERENT CLOTHES. The scan drives
        it with the REAL HEAD and a REAL earlier commit rather than two invented
        strings:

          unit          green  from HEAD          current
          integration   green  from HEAD~5        stale-from-another-commit
          browser       green  from (none)        no-commit-recorded

        ALL THREE ARE GREEN and one describes the tree being released. A URL to
        a green report looks the same whichever commit produced it -- and a
        green report is the one nobody re-reads.

        A report claiming ZERO OPEN FAILURES over a realistic set produces 7
        findings.

        21 spec tests; scan PASSES with 13 negative controls, three of them
        silence controls. -->
- [ ] S11.23 Compare Tara pre/post performance and accessibility baselines.
      Evidence: regressions are fixed or explicitly approved.
      <!-- DELIBERATELY LEFT UNCHECKED, with .a-.g all closed. Same shape as the
      S11.21 and S11.22 parents, and for a reason .a and .c each measured.

      THERE IS NO "PRE". A comparison needs two sides.

        S11.21.a  the accessibility baseline does not exist -- what the subject
                  holds is accessibility FEATURES (a caption validator, a
                  SkipLink) and nothing recording how many violations there are
                  now. The performance baseline is an INSTRUMENT with no
                  recorded number outside a spec file.
        S11.23.c  2 of 566 subject files record a violation count or a WCAG
                  level; 2 of the 9 diff subjects need a screen reader that is
                  not present on this machine at all.

      And there is no "post", because no migration ran (S11.21).

      THE PARENT'S OWN EVIDENCE PRESUPPOSES THE COMPARISON. "Regressions are
      fixed or explicitly approved" is a statement about regressions that were
      FOUND, which needs a comparison that RAN, which needs two baselines. Each
      link in that chain is missing and .a-.g say which.

      WHAT .a-.g DID ESTABLISH is what the comparison would have to satisfy, and
      several of those are findings in their own right: 2 of 10 equivalence
      inputs can never be equal and need a bound rather than a checkmark; at the
      3 runs this estate collects the smallest detectable difference is 4.303
      standard errors, so most differences are unreadable; a finding count going
      down is ambiguous without a denominator; only 1 of 6 delta classes is
      settled by the measurement; three named approvals are not one; and a
      published baseline moves past any unfixed regression it contains.

      WHAT WOULD CLOSE IT. A recorded accessibility baseline and a recorded
      performance number, taken before a migration that has happened, compared
      after it with the machinery .a-.g specifies. Two of those are acts on the
      estate and one is an act on this machine. -->
  - [x] S11.23.a Verify pre/post builds use equivalent
        environment/data/accounts/
        browser/device/network/clock/providers/routes/states and record
        unavoidable differences. Evidence: comparison manifest.
        <!-- "EQUIVALENT" IS A CLAIM ABOUT TWO RUNS. SOME OF THE TEN THINGS THIS
        CELL NAMES CANNOT BE EQUAL BETWEEN TWO RUNS AT ALL, AND FOR THOSE THE
        MANIFEST HAS TO RECORD A BOUND RATHER THAN AN ASSERTION.

          equal-by-construction        routes, states -- one tree, one value
          equal-if-pinned              environment, data, accounts, browser,
                                       device, providers
          never-equal-only-boundable   network, clock

        CALLING THE CLOCK AN "UNAVOIDABLE DIFFERENCE" AND RECORDING IT IS THE
        RIGHT SHAPE AND THE WRONG EMPHASIS. Two runs happen at two different
        instants BY DEFINITION. It is not an exception that arose, it is the
        guaranteed case, and a manifest listing it beside a genuinely unexpected
        difference has flattened the two.

        A manifest asserting all ten equal produces exactly 2 findings. An
        honest one, with bounds where a bound is the only honest record,
        produces 0.

        AND A BOUND WITHOUT A MEASUREMENT IS A WISH. A number, an observation
        and a unit are all required: a bound with no observation is a rule
        nobody checked, an observation with no bound is a number nobody can
        judge, and "network: comparable" is a checkmark wearing a bound's
        clothes.

        THE CLOCK IS RE-MEASURED HERE, not cited from S11.22.a: 461 direct
        platform reads across 110 of 566 subject files, against 0 across 339 kit
        modules with the same detector. So the clock is BOTH unpinnable in this
        subject AND guaranteed to differ. A manifest asserting clock equivalence
        would be wrong twice.

        20 spec tests; scan PASSES with 14 negative controls, three of them
        silence controls. (One control was mis-written first: passing a single
        entry also reports the nine that are missing, which is correct and is
        not what the control is about.) -->
  - [x] S11.23.b Compare route load/transfer/request/main-thread/memory/layout,
        interaction/save/search/compare/virtualization, API/job/queue/provider,
        and cost/resource metrics statistically. Evidence: performance diff.
        <!-- TWO NUMBERS ARE NOT A DISTRIBUTION. One before and one after is a
        SUBTRACTION, and the cell asks for a statistical comparison.

        The difference between two runs is the change PLUS the noise, and
        nothing in the two numbers separates them. What separates them is the
        SPREAD. A difference smaller than the spread is not a small regression
        -- it is a measurement of the noise.

        So this computes the smallest difference the samples could distinguish:
        Welch's standard error sqrt(sb²/nb + sa²/na) times a two-sided 95%
        Student's t critical value, and refuses to call anything below it a
        regression.

        THE t VALUES MATTER AT THE n THIS ESTATE USES. apps/tara/web/
        lighthouserc.js collects `numberOfRuns: 3`. Welch's df are never below
        min(n)-1, so this takes 2, where t is 4.303 against a normal 1.96 -- AN
        INTERVAL MORE THAN TWICE AS WIDE. Using the normal value would call
        noise significant, which is the error this cell exists to prevent.

          n per side   df   t        smallest detectable (sd=10ms)
          1            -    -        nothing -- too few samples
          2            1    12.706   179.69ms
          3            2    4.303     40.57ms
          5            4    2.776     19.23ms
          10           9    2.262     10.66ms
          30          29    2.060      5.41ms
          100         99    1.960      2.79ms

        TWO COMPARISONS AT n=3, BOTH MOVING THEIR MEANS BY EXACTLY 70ms:

          noisy       sd 60/60   smallest detectable 210.8ms  =>
                                 indistinguishable-from-noise
          repeatable  sd  2/2    smallest detectable   7.0ms  =>  regression

        The same difference is a regression in one and unreadable in the other.
        The number that decides is the spread, which a two-number comparison
        does not have.

        FOUR VERDICTS, AND TWO ARE NOT "NO CHANGE".
        `indistinguishable-from-noise` says the difference was smaller than this
        experiment could see; `too-few-samples-to-say` says the experiment could
        see nothing. A diff reporting both as "no regression" has told a reader
        a check happened.

        A DOCUMENTED APPROXIMATION, NAMED: the interval assumes the sample means
        are approximately normal, which at n=3 is an assumption rather than a
        fact. It is computable from what the estate collects; a bootstrap would
        need samples nobody gathers.

        (THE SCAN CAUGHT MY OWN FIXTURE ERROR: the first pair moved 35ms and
        70ms, so the demonstration did not demonstrate what it claimed.)

        27 spec tests; scan PASSES with 15 negative controls. -->
  - [x] S11.23.c Compare automated WCAG findings, keyboard journeys, screen-
        reader announcements, focus, semantic alternatives, zoom/reflow,
        themes/densities, RTL/locales, and visual baselines. Evidence:
        accessibility diff.
        <!-- A FINDING COUNT IS A NUMERATOR. WITHOUT THE DENOMINATOR, A COUNT
        GOING DOWN IS AMBIGUOUS BETWEEN "FEWER PROBLEMS" AND "LESS WAS
        EXAMINED".

        Before: 40. After: 12. That looks like a very good change, and it is
        equally consistent with a page that now fails to load, a route removed
        from the crawl, a component that renders nothing, or a scan that timed
        out. Every one of those reduces the count and none of them fixes
        anything.

          40 -> 12, same examination   =>  fewer-problems  (an improvement)
          40 -> 12, more examined      =>  fewer-problems  (an improvement)
          40 -> 12, less examined      =>  less-was-examined
          40 -> 12, no denominator     =>  ambiguous-no-denominator

        2 of 4 readings of the SAME drop is an improvement. The denominator is
        checked BEFORE the numerator, so even MORE findings over LESS
        examination reads as less-was-examined -- the conservative and correct
        choice, since an improvement that only appears when less is looked at is
        not one. Same shape as S10.3.

        AND ONLY 2 OF THE 9 SUBJECTS ARE COUNTS AT ALL:

          a-count-with-a-denominator  2   automated-wcag, zoom-and-reflow
          an-ordered-sequence         2   keyboard-journeys, focus
          a-text                      2   announcements, semantic-alternatives
          a-set-of-states             2   themes/densities, rtl/locales
          an-image                    1   visual-baselines

        A screen-reader announcement is TEXT. Focus is an ORDER -- the same
        focusables in a different order is a different experience with an
        IDENTICAL COUNT. Subtracting two numbers is the wrong operation for 7 of
        9, and a diff reporting every subject as a delta has quietly turned
        those comparisons into counts.

        AND A DIFF NEEDS A BEFORE. 2 of 566 subject files record a violation
        count or a WCAG level. 2 rows need a screen reader that is not present
        on this machine. Those are two different reasons a row cannot be diffed
        and they are kept apart rather than sharing a footnote.

        23 spec tests; scan PASSES with 14 negative controls, two of them
        silence controls. -->
  - [x] S11.23.d Distinguish regression/improvement/noise/tool/environment/
        intentional semantic change with raw evidence and significance/budget.
        Evidence: triaged delta register.
        <!-- A SIGNIFICANCE TEST SAYS THE DIFFERENCE IS REAL. IT SAYS NOTHING
        ABOUT WHAT CAUSED IT.

        S11.23.b answers one question -- is this larger than the spread -- which
        separates `noise` from everything else and stops. The other five classes
        are ALL "the difference is real"; they differ in their CAUSE, and the
        samples record only the effect.

          noise                        the samples themselves
          tool                         a change in the toolchain
          environment                  a change in the machine or image
          intentional-semantic-change  a record written BEFORE the run
          regression / improvement     nothing in the run -- the RESIDUE

        1 of 6 is settled by the measurement. 2 need evidence the run does not
        contain. 1 needs a prior record -- a semantic change explained AFTER a
        number moves is indistinguishable from a regression with a good story,
        and the difference is entirely in WHEN it was written down.

        SO A REGRESSION IS WHAT IS LEFT. That is not a weakness of the method,
        it is what attribution IS. The register's job is to rule things out, in
        order, and one that jumps straight to "regression" has skipped the
        ruling-out and happened to be right some of the time.

        THE WHOLE SPACE, 96 combinations of the four pieces of evidence against
        the six classes:

          class                          supported  ruled out  unsupported
          regression                             2         14            0
          improvement                            2         14            0
          noise                                  8          0            8
          tool                                   4          8            4
          environment                            2         12            2
          intentional-semantic-change            1         14            1

        19 of 96 support the attribution made. Every class is supported by SOME
        combination and none by all of them -- which is what stops the table
        being a decoration. With a toolchain change present, only the tool
        attribution stands.

        23 spec tests; scan PASSES with 13 negative controls, two of them
        silence controls. -->
  - [x] S11.23.e Fix each unapproved regression and rerun focused then complete
        affected comparison. Evidence: closed fix results.
        <!-- A FOCUSED RERUN IS A CHECK ON THE FIX. A COMPLETE RERUN IS A CHECK
        ON THE FIXER.

        The cell asks for focused THEN complete, and the order is the whole
        instruction:

          focused   did this number come back
          complete  did the change that moved this number move anything else

        The focused rerun is fast and is the right thing to run first. It cannot
        answer the second question -- which is the one a person under time
        pressure stops before reaching.

        THREE ANSWERS, NOT TWO:

          closed
          the-fix-worked-and-nothing-else-was-checked
          not-closed

        The middle one is a TRUE statement about a REAL result and it is not
        closure. Reporting it as closure is how a fix for one regression ships
        another.

        AND THE COMPLETE RERUN HAS TO BE THE ONE AFTER THE FIX. A green complete
        comparison from before it describes a tree that no longer exists -- the
        same staleness S11.22.h found in a linked report, through a different
        door. The scan drives that with REAL commits, not two invented strings.

        AND A FIX IS NOT A FIX UNTIL SOMETHING CHANGED. A regression that
        disappears with no change was not fixed, and calling it fixed removes
        the record that would have said the metric is unstable.

          closed-properly                 closed
          focused-only                    the-fix-worked-and-nothing-else...
          complete-from-before-the-fix    the-fix-worked-and-nothing-else...
          nothing-changed                 not-closed

        1 of 4 closed. A report claiming all four produces 3 findings.

        21 spec tests; scan PASSES with 13 negative controls, two of them
        silence controls. -->
  - [x] S11.23.f For an allowed regression, record affected users/SLO/criterion,
        rationale, alternatives, compensating control, owner, named product/
        accessibility/performance approval, expiry, and follow-up. Evidence:
        approved exception.
        <!-- AN EXCEPTION IS A DECISION TO SHIP SOMETHING WORSE. THE FIELDS MAKE
        IT REVIEWABLE AND THE APPROVALS MAKE IT SOMEBODY'S.

        The cell names ten things and the tenth IS NOT ONE FIELD. "Named
        product/accessibility/performance approval" is THREE approvals by three
        different people with three different jobs, and an exception carrying
        one signature has one person's judgement standing in for all three.

        That is not pedantry about headcount. A performance approver can say a
        200ms regression is acceptable for this feature. They cannot say it is
        acceptable for a person using a screen reader -- a different question
        about different users with a different answer.

        AND AN EXCEPTION WITHOUT AN EXPIRY OUTLIVES ITS REASONS. Every field
        describes a MOMENT: these users, this SLO, this alternative that was not
        available. One never revisited becomes a permanent property of the
        system that nobody chose. The instant is an ARGUMENT, never a clock
        read, so the same exception judged at two moments gives two reproducible
        answers.

        AND A COMPENSATING CONTROL IS A THING THAT EXISTS. "We will monitor it"
        is a sentence; a control is something that ACTS -- the same distinction
        S11.20.f drew about remediation. An approval naming a role rather than a
        person is a fault for a related reason: a role cannot be asked about it
        afterwards.

        9 withdrawals from a COMPLETE exception, one thing removed at a time,
        cover all 7 fault kinds -- so every finding is attributable to what was
        taken away rather than to a fixture that was never valid. The complete
        exception produces 0.

        20 spec tests; scan PASSES with 14 negative controls. -->
  - [x] S11.23.g Publish final comparison with zero unexplained or expired
        regressions. Evidence: signed baseline report.
        <!-- "ZERO UNEXPLAINED OR EXPIRED" IS TWO CONDITIONS, AND THE SECOND
        ARRIVES WITHOUT ANYBODY DOING ANYTHING.

        A regression is unexplained until somebody explains it -- a state a
        person changes. An EXPIRED one is different in kind: an explained
        regression carries an exception, the exception carries an expiry, and
        the expiry passes ON ITS OWN. The report was TRUE when published and
        becomes FALSE while nobody edits it, which is the only way a document
        can go wrong with no author.

        THE SAME REPORT, PUBLISHED 2026-09-01, READ AT FOUR INSTANTS:

          2026-09-01  clean                   0 of 3 blocking
          2026-10-01  has-expired-exceptions  1 of 3 blocking
          2026-11-15  has-expired-exceptions  1 of 3 blocking
          2027-01-01  has-expired-exceptions  2 of 3 blocking

        NOBODY EDITED IT. So a publication check that runs once, at publication,
        has verified the only moment at which the property was guaranteed to
        hold. Findings at publication: 3. In November: 4.

        AND "SIGNED" IS NOT "CHECKSUMMED". 1 of 3 attestations is a signature.
        S11.20.g measured what a digest computed by the producing process can
        establish -- one of the four things a signature is asked for -- and this
        cell asks for a SIGNED report, so the register keeps the distinction
        rather than accepting a digest under a signature's name. A reader takes
        the word for the claim.

        AND A BASELINE REPORT IS WHAT FUTURE COMPARISONS ARE MEASURED AGAINST.
        Publishing one with an outstanding regression does not merely record it
        -- it MOVES THE BASELINE, and the next comparison finds nothing wrong,
        correctly, against a baseline that already contains it. An unfixed
        regression published as a baseline stops being a regression at all.

        (An exception with no expiry is left to S11.23.f rather than counted
        twice here.)

        24 spec tests; scan PASSES with 14 negative controls, three of them
        silence controls. -->
- [ ] S11.24 Complete an adversarial code review of the extracted kit. Evidence:
      findings are resolved and public API is frozen for domain P1.
  - [x] S11.24.a Freeze review commit/scope and map every public export,
        extension point, consumer, dependency direction, generated artifact,
        runtime boundary, and test evidence. Evidence: review manifest.
        <!-- AN ENTRY POINT IS REACHED BY AN IMPORTER OR BY A RUNNER. A
        REGISTER'S RUNNER IS ITS SCAN. AN ENTRY POINT WITH NEITHER IS REACHED BY
        NOTHING.

        This opens an adversarial review OF THIS KIT, and the first honest thing
        to say is that "nobody imports it" is the wrong test for most of it. A
        register exists to be RUN: its scan re-derives the numbers from the
        estate and reports a judgement, so a register with a working scan and no
        importer is doing exactly its job. Applying an import graph to it and
        calling the result dead code is the S11.21.g mistake made in the other
        direction.

        So reachability has FOUR states and three of them are fine.

        MEASURED ON THE KIT ITSELF, over 81,803 source files repo-wide:

          imported-by-a-consumer     48
          run-by-a-scan             178
          imported-and-run            0
          REACHED BY NOTHING        115   of 341 subpath exports

        THE ZERO IS ITSELF A FINDING. The kit has split cleanly into code
        somebody uses and registers something runs, with NO module doing both.

        AND 115 ARE REACHED BY NOTHING -- a public name with nothing behind it,
        indistinguishable from a working one to every check that reads the
        export map. That is the queue this review starts from. (activity-feed,
        actors, approval-workflows, artifact-rehash, assignments, audit, ...)

        AND THE SEVEN ASPECTS ARE NOT SEVEN LISTS: 5 distinct shapes across 7.
        A public export is a NAME; a consumer is a FILE SOMEWHERE ELSE; a
        dependency DIRECTION is a property of a PAIR; a runtime boundary is a
        claim about where code may run. A manifest rendering all seven as one
        table of names has flattened different questions into one.

        AND A CONSUMER COUNT IS A WHOLE-REPOSITORY CLAIM, so a manifest whose
        search was narrower than the estate it describes is refused -- exactly
        as a deletion gate is.

        20 spec tests; scan PASSES with 12 negative controls, three of them
        silence controls. -->
  - [x] S11.24.b Review abstraction quality for Tara leakage, one-consumer
        extraction, generic escape hatches, unsafe defaults, hidden coupling,
        runtime/type mismatch, and extension invariants. Evidence: API findings.
        <!-- AN ABSTRACTION IS A CLAIM ABOUT MORE THAN ONE CASE, AND EVERY ONE OF
        THE SEVEN CONCERNS IS A WAY OF HAVING ONLY ONE.

        None of the seven is a property of a token, so a grep count answers none
        of them. Each is a RELATION and the register reads pairs: the kit against
        the estate it was cut out of, a module against the PROJECTS that import
        it, a `??` against the declared optionality of the field it is written
        on, a name against every other declaration of that name, a lookup table
        against the keys that reach it, a rule against the thing that enforces
        it.

        THE HEADLINE, MEASURED OVER 347 MODULES AND 81,806 SOURCE FILES
        REPO-WIDE. Of 342 subpath exports, 48 are imported at all -- and ALL 48
        BY EXACTLY ONE PROJECT. The distribution has one bucket. The whole kit is
        consumed by TWO projects, `apps/oshun/web` and `apps/oshun/bff`, and
        neither is Tara: the extraction this phase exists to serve has not been
        exercised once by a second caller.

        A CONSUMER IS NOT A FILE, IT IS A PROJECT. 39 of those 48 are reached
        from several files inside ONE project, and a consumer count that counted
        files reports every one of them as reuse. Five files in one app exercise
        the abstraction from the one perspective it was extracted from, which is
        why the register keeps `one-project-through-several-files` apart from
        `shared-by-more-than-one-project` and reports the measurement fault
        separately from the thing measured. (S11.24.a's "a consumer is a FILE
        SOMEWHERE ELSE" is the right shape at the wrong altitude for this
        question.)

        AND ONE NAME MEANS MORE THAN ONE THING 123 TIMES. Of 2,451 alphabet names
        in the kit, 125 are declared in more than one module and 123 of those
        carry DIFFERENT members. `LIFECYCLE_FAULTS` is declared in EIGHT modules
        -- audit-integrity, audit-snapshots, collaborator-presence,
        comment-threads, lifecycle-corpus, mentions, notification-center,
        response-provenance -- with eight disjoint member lists of size
        3/6/8/8/9/11/11/14.

        AND 44 OF THOSE NAMES ARE ALSO SERVED BY THE ROOT ENTRY POINT, so two
        legal imports of one name give two values:
        `import { LIFECYCLE_FAULTS } from '@oshun/workbench-kit'` resolves to
        response-provenance's eight and
        `from '@oshun/workbench-kit/mentions'` to a different eleven. Both doors
        are supported and THE TYPE SYSTEM CANNOT OBJECT, because each door is
        separately consistent.

        AND A TARA PATH IN A DATUM IS A CITATION; ONE A FUNCTION READS IS A LEAK.
        20 modules name Tara; a name-grep would report all 20. ZERO name it in a
        type or an identifier -- the leak everybody looks for is absent. 9 hold
        Tara paths in a constant NOTHING reads, which is a register saying what
        it measured, and refusing it the vocabulary would make it unable to. 7
        hold one a function READS, and those are the leakage: the module computes
        from one estate's readings, so a second consumer gets that estate's
        answer under a neutral name.

        AND A DEFAULT IS NOT UNSAFE FOR BEING A DEFAULT, IT IS UNSAFE FOR ITS
        DIRECTION. Of 476 `??` written on a field whose declared type admits
        absence: 260 resolve toward a stricter reading, 66 toward a refusal, and
        150 TOWARD AN ANSWER NOBODY COMPUTED. A further 129 are written on fields
        no interface in the module declares as absent, so they cannot fire at all
        and a reader cannot tell whether they are dead or whether the object is
        not the one they think.

        AND TWO ABSENCES IN ONE OPTIONS BAG CAN BE ANSWERED IN OPPOSITE
        DIRECTIONS -- 33 modules do it, and it is the finding a per-field rule
        cannot make, because each field alone reads as defensible. The clearest
        is `scanValue` in errors.ts: an absent `tenant` makes it report EVERY
        tenant id as foreign (stricter, correct), and an absent `canaries` makes
        it report no planted value at all (blinder). Same function, same options
        object, six lines apart, and the bag defaults to `{}` so the easiest call
        to write gets one of each.

        AND `unknown` IS NOT AN ESCAPE HATCH, IT IS THE OPPOSITE -- it cannot be
        used until it is narrowed, so grouping its 219 occurrences with `any`
        because both are "loose" inverts the finding. `any` is at ZERO. AND A
        HATCH IN AN IMPLEMENTATION IS A CHOICE WHILE A HATCH IN A SIGNATURE IS A
        CONTRACT, so the 32 findings are the bags and index signatures on the
        public surface; the 1,341 casts onto a kit union are not dropped, they
        are the input to the next concern.

        THE SHARPEST FAULT NEEDS TWO FACTS AT ONCE. `noUncheckedIndexedAccess` is
        OFF across this repo, so `Record<K, V>[k]` types as `V`. Of 79 union-keyed
        frozen lookup tables, 33 have a key manufactured somewhere by an
        assertion rather than a check, and 31 of those never check it before the
        index -- the miss types as a hit. Neither fact alone is a fault: a cast
        alone is not one, a table alone is not one, and turning the compiler
        option on would fix all 31 without touching either.

        AND 3 OF 4 STATED EXTENSION INVARIANTS ARE ENFORCED, which is a good
        result honestly obtained. Both directions of declaration-vs-contribution
        are real fault kinds; the seven extension points are a closed set checked
        by `includes`; measurement-vs-verdict is carried in `decidedBy` where the
        type will not let a verdict forget who made it. The fourth --
        "ABSENCE IS A VALUE, NEVER AN EXCEPTION" -- IS ANSWERED AT THE CALL SITE,
        and plugins.ts contains ZERO `try`. It declares 18 function-typed fields
        and calls one at two sites: `policy.decide(measurement)` and, as the last
        line of `activatePlugin`, `plugin.activate(context)`. That function
        returns `not-configured` and `refused` as VALUES for every absence THE
        KIT can detect, argues in its own header that a plugin signalling absence
        by throwing would be indistinguishable from one that crashed -- and then
        hands control to the plugin unguarded, which is exactly the state the
        rule forbids. The rule holds for the absences the kit finds and is
        unenforced for the one the plugin declares.

        MY OWN INSTRUMENT WAS WRONG TWICE AND BOTH FIXES CHANGED AN ANSWER. The
        first detector for "both directions" looked for a phrase from the doc
        comment rather than the fault kinds, and reported a rule the validator
        really does enforce as unchecked. The second matched `plugin.<field>(`
        anywhere, so it "found" contribution calls in `plugin.descriptor.x` --
        it reached the right verdict on rule 4 for a reason that was not the
        reason. A DETECTOR THAT AGREES WITH THE ANSWER YOU EXPECTED IS NOT
        THEREBY CORRECT; both now read the fault alphabet and the call sites.

        AND THE REGISTER CARRIES NO NUMBER IT DID NOT DERIVE -- a scan check
        fails if a two-digit number appears in it, with ledger-cell references
        stripped first so `S11.24.b` is a citation and a measurement quoted in a
        doc comment is still caught.

        39 spec tests; scan PASSES with 30 negative controls, ten of them silence
        controls. -->
        <!-- The five registrations: kit package.json export, root
        verify:operations script, project.json target, export-map generator, and
        the tsconfig.base.json path alias -- which the generator's own
        `unaliased-approved-entry-point` check caught me omitting. -->
        <!-- Typechecked libs/oshun/workbench-kit directly: tsconfig.lib.json is
        clean; tsconfig.spec.json has 23 errors, none of them in this cell's
        files (adapter-compatibility, duplicate-fixtures, fixture-reachability,
        graph-parity, manifest-relations). -->
        <!-- NOT DONE HERE, and named rather than folded in: the 115 entry points
        S11.24.a found reached by nothing are not re-judged here; whether the
        1,341 casts are each justified is a per-site question this cell does not
        answer; and the 44 two-door names are reported, not resolved -- renaming
        them is a public API change, which is S11.24.h. -->

  - [x] S11.24.c Review identity/tenancy/object/property/function authorization,
        step-up, idempotency/concurrency, audit, errors, limits, fetch/upload,
        and secret/protected data paths adversarially. Evidence: security
        findings.
        <!-- A SECURITY DECISION IS ONLY AS BINDING AS THE WEAKEST LINK BETWEEN
        THE SUBJECT AND THE VERDICT.

        Reviewing fifteen dimensions one module at a time asks the same question
        fifteen times and misses the shape, because a bypass never needs the rule
        to be wrong -- it needs one of three links to be loose. HOW THE FACT
        ARRIVES: a decider that is TOLD the security-relevant fact is exactly as
        correct as whoever told it. HOW THE DECISION IS SHAPED: a verdict
        returned beside its subject can be dropped. WHAT THE VERDICT CAN SAY: a
        refusal that cannot name what disagreed sends its reader elsewhere. One
        finding under each, on 15 of 15 dimensions, every compound split.

        AND EACH LINK HAS A STATE THAT LOOKS LIKE THE FAULT AND IS NOT. That is
        where a naive count goes wrong, and getting the three separators right is
        most of this cell.

        1. TAKING THE ANSWER IS RIGHT WHEN THE DECIDER CANNOT COMPUTE THE
        QUESTION. `tenancy.ts` argues this itself, correctly and at length, for
        `stageIsImmutable`: the check "used to read `lifecycleStage ===
        'published'`, and that spelling protects a workbench whose terminal stage
        happens to be called `published` and silently protects nothing in one
        whose stage is `released`, `aired`, `filed` or `shipped`. The kit cannot
        know the word, so it takes the answer instead." That is not a loose link,
        it is the only correct design.

        AND THE SAME MODULE TAKES `sameTenant` THE SAME WAY. `AccessRequest`
        carries `sameTenant: boolean` and `sameWorkspace: boolean` and no tenant
        ids at all, so the module named for tenancy cannot compare tenants -- it
        can only be told. A tenant is NOT domain vocabulary: `TenantId` is one of
        17 identifier types the kit itself brands, and the decider holds both
        values. The separator is `theDeciderHoldsTheValues`, not the shape of the
        parameter, which is a boolean either way.

        AND THE ESTATE CONTAINS ITS OWN ANSWER, ONE MODULE AWAY. `authorization.
        ts` compares a tenant 4 times, and its header calls the re-compare after
        a scoped resolver "the single most valuable line in the module", because
        "a resolver that ignores its scope argument -- the most ordinary bug
        there is, `SELECT ... WHERE id = $1` -- is invisible to every test that
        only checks the happy path". One module distrusts its input on exactly
        this question and the neighbour accepts a claim.

        2. A DECISION THAT CARRIES NO SUBJECT IS NOT ADVISORY, IT IS A POLICY
        FUNCTION. A rule handed an object and returning a verdict about it has
        nothing to leak. Of 7 verdict types in the security surface carrying a
        nullable field, SIX carry a `reason`, an `error`, a `probe`, a `refusal`
        or an audit record -- all ABOUT the decision rather than subject to it,
        and reporting them would have been six false positives. The fault needs
        three facts at once: the decider LOADED the subject, it sits beside the
        verdict, and it is what the verdict is about.

        THE SEVENTH IS `AuthorizedLoad`, and `LoadOutcome` seventy lines above it
        is the same module getting it right. `LoadOutcome` is a SUM -- `metadata`
        exists only on the `loaded` variant, so it cannot be named without
        narrowing first. `AuthorizedLoad` is a PRODUCT: `{ outcome, metadata:
        ObjectMetadata | null, fault }`. One null check -- which every caller
        writes anyway -- yields the metadata, and the verdict beside it was never
        read.

        AND THE SAME FUNCTION BINDS AT ONE DISPOSITION AND NOT AT ANOTHER, which
        is the finding no per-function rule states. `authorizeObject` withholds
        the metadata at `conceal` and at `unavailable`, CORRECTLY and for the
        reason its header gives -- "a 403 is an answer: it says the object
        exists" -- and returns it on whatever the rules decided, which is allow,
        deny AND step-up. So a caller denied for not being assigned receives
        `owner`, the full `assignees` list, `team`, `classification` and the
        `sensitivity` classes: a denial that names who to ask is also a denial
        that names who to phish. Decision 2 of the module's own header bounds the
        blast radius -- `ObjectMetadata` has nowhere to put content, deliberately
        -- and decision 5 says a refusal "carries a stable reason and a
        remediation, and NEITHER CARRIES THE OBJECT". The return shape carries
        it. Binds at 2 of 3, and THE HALF THAT WORKS IS WHAT MAKES THE OTHER HALF
        HARD TO SEE.

        3. AND A MEMBER NAME IS NOT THE FIELD IT NAMES. Step-up is the best-built
        thing in this review and the finding is small on purpose. `Challenge`
        binds twelve fields including a `parameterDigest` -- "a different request
        is a different act" -- `CHALLENGE_BINDINGS` is DATA and `verifyChallenge`
        ITERATES it, so "a binding that stops being checked is a deletion from a
        list rather than a missing `if` nobody notices", and `bindingValue` is a
        real exhaustive switch onto real fields. Eight things really disagree and
        the alphabet names seven. The eighth is the NONCE, which has no member,
        and the caller must pass something -- so a nonce mismatch is filed as
        binding `'actor'`, which `bindingValue` maps to `actorId`. The rejection
        is CORRECT; it is reported as a thing that did not disagree.

        MY OWN INSTRUMENT WAS WRONG THREE TIMES AND EVERY FIX CHANGED AN ANSWER.
        The brand reader required the brand on one line, found 3 identifier types
        instead of 17, and so cleared `sameTenant` as a fact only the caller could
        know. The producer reader was a nested `indexOf` slice that never fired,
        so `AuthorizedLoad` came back as a policy function. And the vocabulary
        check compared MEMBER names against FIELD names, reporting `actorId` and
        `sessionId` as unnameable when the alphabet names them perfectly well as
        `actor` and `session` -- three unnameable things where one is real. The
        disposition reader then found nothing at all, because `search()` with a
        non-greedy `[\s\S]*?` matched from `domainPolicyRule` and stretched to
        `authorizeObject`'s return type: A NON-GREEDY SPAN FINDS THE EARLIEST
        START, NOT THE NEAREST. It now carries the producer the shape check
        already resolved, so the two readers cannot silently disagree about which
        function they are describing.

        AND THE REGISTER CARRIES NO NUMBER IT DID NOT DERIVE -- checked, with
        ledger-cell references stripped first.

        26 spec tests; scan PASSES with 20 negative controls, nine of them
        silence controls -- six of which assert that a correct design is NOT
        reported, since three of the five fault kinds exist to be silent on the
        state that resembles them. -->
        <!-- NOT DONE HERE: the fifteen dimensions are read through the three
        links, not exhausted individually -- idempotency's two-racer `claimed`
        and the reservation/settle path are named in their own cells, and this
        cell does not re-judge them. Nothing here is fixed: the shape changes are
        public API changes and belong to S11.24.g and .h. -->

  - [x] S11.24.d Review
        persistence/migrations/events/jobs/cache/blob/provenance/ publish for
        atomicity, races, replay, partial/unknown outcome, retention, and
        recovery. Evidence: correctness findings.
        <!-- A DOUBT NEEDS A NAME, A PRODUCER AND A READER, AND THEY FAIL
        SEPARATELY.

        The cell names six properties and five are answered the same way: find
        out which state you are in, then act on it. AN UNKNOWN OUTCOME IS NOT A
        SIXTH HAZARD, IT IS THE FAILURE OF THE PRECONDITION THE OTHER FIVE ARE
        ANSWERED WITH. A retry is safe when you know the effect was partial; an
        idempotency key protects a replay when you know the key was recorded; a
        recovery needs a state known to be good. When the system cannot tell, all
        of them degrade to a guess at once -- 6 of 7 properties presuppose that
        observation, and the seventh is its loss. (`partial/unknown outcome` is
        split for exactly this reason: a partial outcome is a state the system
        OBSERVED.)

        So the question is whether each subject can HOLD a doubt, and that has
        four answers rather than two: no module owns the subject, no name exists
        for the doubt, a name exists with no producer, or a producer exists with
        no reader.

        5 OF 8 SUBJECTS HOLD A DOUBT AND ACT ON IT, and `jobs.ts` is the
        exemplar because the rule is the estate's own: it says "its external
        effect is UNKNOWN, and unknown is not failed", it keeps "never started"
        apart from "outcome unknown", and it carries `unknownOutcomeMaxAttempts`
        as a separate LOWER ceiling -- "a lower ceiling for unknown outcomes:
        each attempt may be a second effect" -- with the answer DEPENDING on
        whether the effect is idempotent. `outbox`, `blobs`, `publishers` and
        `provenance-inspector` each branch on a doubt state too; `publishers`
        carries three states with nine reading sites apiece.

        THE THREE THAT CANNOT, AND THE WORST IS THE QUIETEST. `cache` has NO
        MODULE in the kit at all -- no file carries a cache in its topic line,
        and crediting `freshness-recompute.ts` would be crediting S11.20.d's own
        register with a question it was not asked. And `dual-write-divergence`
        has 5 alphabets and `migration-paths` has 3, and NOT ONE MEMBER BETWEEN
        THEM names an undeterminable outcome -- in the two places where doubt is
        structural. A dual write is exactly the operation where one side lands
        and the other's outcome is unknown. A half-applied migration is none of
        `START_STATES`' four (`empty`, `prior-supported-version`,
        `production-like-snapshot`, `live-database`). THE ALPHABET CANNOT SPELL
        IT, SO A DOUBTFUL CASE MUST BE FILED UNDER A CONFIDENT ONE, and nothing
        is missing from any file.

        AND A DEAD MEMBER IS A FINDING WHATEVER ITS NEIGHBOURS DO. `blobs`'
        `STALENESS.unknown` is assigned at one site and read by nothing, so a
        blob whose staleness could not be determined is stored and then treated
        exactly like `current`. `provenance-inspector`'s `RIGHTS_HINTS.unknown`
        appears EXACTLY ONCE in the whole module -- in its own declaration --
        beside `not-recorded`, which is a different doubt again ("nobody wrote it
        down" is not "we looked and cannot tell"). Both sit next to a working
        sibling, so a subject-level standing alone would have hidden them: the
        review reports the subject AND the member.

        AND A STATE AND A FAULT KIND ARE JUDGED BY OPPOSITE TESTS. This is the
        trap the whole measurement turns on. A STATE is live when something
        BRANCHES on it, because a value stored and never consulted behaves
        identically to its confident neighbour. A FAULT KIND is live when
        something PRODUCES it, because a detector emits it and branching on one
        would be the odd thing to do.

        MY OWN PROBE APPLIED THE BRANCH TEST TO BOTH AND CALLED FOUR OF THE
        ESTATE'S BEST-DETECTED FAULTS INERT -- including all three of
        `PUBLISH_RECONCILIATION_FAULTS`' members named for exactly this hazard
        (`unknown-outcome-retried-before-a-probe`,
        `unknown-outcome-marked-failed-with-no-evidence`,
        `unknown-outcome-marked-succeeded-with-no-evidence`), each of which IS
        produced by a real detector. A DEFECT IN THE INSTRUMENT REPORTED AS A
        DEFECT IN THE ESTATE, and it was caught only by opening the files. An
        earlier version was worse: it tested production by deleting every
        occurrence of the literal from the file, which deletes the production
        sites too, so it could only ever answer "none".

        AND THE EXEMPLAR READER LOOKED IN THE STRIPPED SOURCE. Both of the
        sentences that make `jobs.ts` the exemplar live in its DOC COMMENT, and
        the reader ran on comment-stripped text -- so it answered a question
        about the stripper and reported `false` about a module that says the
        thing plainly. It now reads the raw file.

        AND THE REGISTER CARRIES NO NUMBER IT DID NOT DERIVE -- checked.

        21 spec tests; scan PASSES with 16 negative controls, four of them
        silence controls, one of which drives the state/fault-kind trap BOTH WAYS
        on the same member and facts. -->
        <!-- NOT DONE HERE: the six properties are the lens, not a 8x7 matrix
        walked cell by cell -- races and retention are read through whether the
        subject can hold the doubt they turn on, and are not separately
        exhausted. Nothing is fixed: naming a doubt in `START_STATES` or
        `dual-write-divergence` widens a public union, which is S11.24.g and .h.
        -->

  - [x] S11.24.e Review provider/trust/gates/scores/fixtures/states for
        injection, fabricated truth/success, stale authorization,
        policy/version, cost, override, and human approval boundaries. Evidence:
        trust findings.
        <!-- AN ESTATE WANTS ZERO OF A HAZARD AND SOME OF A POWER, AND THE TWO
        ARE ANSWERED BY OPPOSITE INSTRUMENTS.

        The cell names seven slots and reading them as seven of a kind is the
        mistake. Ask of each whether ZERO IS THE GOAL. Nobody wants an injection,
        a fabricated success, or a result evaluated under a policy that is not
        the one in force -- those are HAZARDS, answered by a DETECTOR. But an
        estate that spends nothing does nothing, one that cannot override a bad
        gate cannot recover from it, and one with no human approval has removed
        the point of asking -- those are POWERS, answered by a CEILING. THE CELL
        ITSELF SAYS SO AND PUTS IT ON EXACTLY THE RIGHT THREE: it says
        "BOUNDARIES", and a boundary is what you give a thing you want some of.

        AND THE INSTRUMENTS DIFFER IN THE ONE PROPERTY A REVIEW DEPENDS ON. A
        DETECTOR PROVES ITSELF BY FIRING -- feed it the fault, watch it report,
        which is this phase's whole method. A CEILING CANNOT. A power exercised
        inside its limit and a power with NO limit anywhere leave THE SAME
        RECORD: work happened and nothing objected. The observation that would
        tell them apart is the one a working system never produces. So a ceiling
        is judged by its PATHS and never by its rule -- the rule is the part
        that is easy to read and the part that is never wrong.

        THE MEASUREMENT LANDS ON THAT LINE EXACTLY. All six hazards have a
        detector that fires: 43 of 43 fault kinds PRODUCED, across
        trust-zones.CONTROL_FAULTS, score-fabrication.SCORE_FABRICATIONS,
        provider-fabrication.PROVIDER_FABRICATIONS,
        reviewer-handoff.GRANT_AUTHORITY_FAULTS (17 alone),
        trust-results.INVALIDATION_FAULTS and .STAMP_FAULTS. And 0 OF 3 POWERS
        have a ceiling that binds every path it censused. THE ESTATE IS PERFECT
        WHERE IT COULD CHECK ITS OWN WORK AND IMPERFECT WHERE IT COULD NOT, and
        that is not a coincidence -- it is the property above.

        AND A FAULT KIND IS A PROPERTY OF A RECORD, SO THE WAY PAST A CEILING IS
        TO LEAVE NONE. This is why a no-record path ranks BELOW a path closed
        only by a convention: no alphabet, however long, reaches a route that
        writes nothing, because there is nothing for a fault to be ABOUT. THE
        ESTATE STATES THIS ITSELF, about its own worst case -- `a11y-waivers`
        says `skipAxe` is "a boolean fixture option ... there is no field on it
        to carry anything and no finding for a field to be about", over 247
        opt-outs, and 2 of its 3 APPLICATION_POINTS leave nothing to waive.
        `catalog-write-paths` names the write channel that "leaves no
        application record at all".

        AND A CLOSURE THAT NAMES AN INTENTION IS NOT ONE, discovered TWICE and
        named differently both times: `build-gates.GATE_STANDING` carries
        `has-a-documented-bypass` -- "the honest kind of hole and it is still a
        hole" -- and `catalog-write-paths` sets 2 of its 10 CLOSURES aside as
        CLOSURES_BY_CONVENTION (`documented-convention`, `code-review`). Neither
        module knows the other's word for it.

        AND A CENSUS IS A CLAIM ABOUT EVERY PATH. `limits` says "every one of
        these limits has a bypass, and the bypasses are more interesting than
        the limits" -- and `bypassFindings` walks RATE_DIMENSIONS, which is 2 of
        the 6 categories LIMIT_CATEGORIES_PUBLIC declares. Payload, upload,
        query-complexity and spend have no enumerated bypass.
        `cost-accounting.dispatchVerdict` is the exemplar of the other kind: it
        REFUSES at the point of use, with 5 named refusals, one of them for the
        unreserved path -- the bypass closed rather than reported.

        AND THE RULE AND ITS ROUTES ARE FILED APART: `override` keeps its
        ceiling and its two path censuses in THREE different modules, so a
        reviewer who opens the module deciding whether a waiver authorizes never
        meets the enumeration of the routes that never reach it.

        MY OWN INSTRUMENT WAS WRONG TWICE AND READING CAUGHT BOTH. A keyword
        search for path censuses found 2 and the estate has at least 5 --
        `limits`, `build-gates` and `isolation-paths` carry one under names no
        pattern matches. A KEYWORD-DERIVED ZERO IN THIS ESTATE IS ALMOST ALWAYS A
        FACT ABOUT THE KEYWORD (third recurrence). And a substring test for
        limit coverage would credit `upload` from `concurrent-uploads` -- a
        CONCURRENCY dimension, not the upload-size limit -- reporting a partial
        census as whole; the category mapping is READ, and the scan fails loudly
        on any dimension it does not classify rather than dropping it.

        30 spec tests; scan PASSES with 21 negative controls, four of them
        silence controls, two driving the hazard/power split BOTH WAYS on the
        same facts. -->
        <!-- NOT DONE HERE: the six named subjects are the cell's index, not the
        review's -- coverage is read where the estate files it, and every
        coverage count is reported as a FLOOR because the fault vocabulary is
        phrasal and any outside taxonomy under-counts it. Nothing is fixed:
        closing a no-record path or widening a census changes public surface,
        which is S11.24.g and .h. -->

  - [x] S11.24.f Review UI/API accessibility/localization/resilience/privacy/
        performance/operability and test harness ability to detect regressions.
        Evidence: quality findings.
        <!-- A QUALITY THE VALUE CARRIES TRAVELS WITH IT; A QUALITY AN INSTRUMENT
        VISITS IS ONLY AS REAL AS THE LAST VISIT.

        The cell names six qualities and then asks something it asks of nothing
        else in this whole review: whether the harness can DETECT A REGRESSION in
        them. THAT CLAUSE IS NOT A SEVENTH QUALITY, IT IS THE REASON THE OTHER
        SIX ARE GROUPED. Each of them fails by producing a PERFECTLY VALID VALUE
        -- an inaccessible button is a valid button, an untranslated string is a
        valid string, a leaked field is a valid field, a slow response is a valid
        response, an unoperable service is a running service, and a degraded
        answer is a valid answer, which is `degradation-plans`' own opening
        sentence. VALIDITY IS WHAT ALL SIX FAILURES HAVE, so no validator finds
        any of them and the only question left is whether anything can tell.

        THE KIT ANSWERS THAT IN TWO WAYS AND NEVER NAMES THE CHOICE. 5 OF 6 ARE
        ANSWERED BY MAKING THE VALUE CARRY THE FACT ABOUT ITSELF, under five
        names that are never connected: `ResolvedText` returns the text WITH the
        locale asked for, the locale it actually came from, the fallback step and
        eight notices including `fallback-used`; `publicError` and `projectFor`
        CONSTRUCT the safe shape rather than deleting fields, because "deletion
        is a blocklist, and a blocklist is wrong the first time somebody adds a
        field"; `degradation-plans` requires the returned value to NAME THE FIELD
        that says it is a fallback AND the consumer that branches on it;
        `service-objectives` requires the ratio to carry the denominator the
        failing system chose. THE ESTATE MADE THE SAME MOVE FIVE TIMES.

        THE SIXTH, ACCESSIBILITY, IS ANSWERED BY A VISIT -- and that is NOT a
        mistake, because no type can hold "this label is meaningful", so for part
        of the quality the visit is FORCED. But the two differ in exactly the
        property the cell's last clause asks about: a carried fact is in every
        consumer's hands on every call, and a visited fact exists only where the
        visit reached. That is why `accessibility-diff` must ask whether a
        falling count means "fewer problems" or "less was examined", and why none
        of the carried five has to.

        AND 1 OF THE 4 WAYS A VISIT STOPS BEING EVIDENCE HAS NO INSTRUMENT IN ANY
        ACCESSIBILITY MODULE. `not-run`, `reached-less-than-before` and
        `no-denominator-recorded` are all modelled (`a11y-waivers`,
        `accessibility-diff`). `run-on-another-subject` is modelled NOWHERE --
        and `build-gates` names that exact standing, "green every time, about a
        codebase this change did not touch", and attributes it in its own words
        to "S11.21.e's accessibility scripts". The estate has written down that
        its accessibility scan has this trap and has built nothing that detects
        it.

        AND THE PROTECTION IS INVERSE TO THE INVISIBILITY OF THE FAILURE. The kit
        brands 13 identifier types with a unique symbol, a strict parser and --
        its own words -- "no unsafe cast", so a mixed-up identifier CANNOT BE
        CONSTRUCTED, and that is also the failure the first test to run would
        have caught. The fields carrying the six silent qualities are 94 of 102
        bare `string`, a type that cannot distinguish a translated label from an
        untranslated one, a message key from an English sentence, or a real
        accessible name from a leftover default. The estate knows the hazard by
        name and answers it in ONE module at RUNTIME: `errors.validateMappings`
        "refuses a classifier that looks like a sentence, because that is how
        message-matching comes back after being removed" -- for a hazard the TYPE
        permits at all 24 bare `messageKey` declarations.

        MY OWN INSTRUMENT WAS WRONG FIRST, AND THE SCAN CAUGHT IT RATHER THAN THE
        ESTATE. Three cited sentences and one carrier name came back stale, and
        the review reported `resilience` as carried-by-nothing -- A DEFECT IN THE
        INSTRUMENT ABOUT TO BE REPORTED AS A DEFECT IN THE ESTATE. The cause was
        one thing: A PROSE CLAIM SPANS LINES AND THE COMMENT PREFIX SITS IN THE
        MIDDLE OF IT, so a regex over raw text answers a question about where the
        author happened to wrap. The reader now strips the prefix and collapses
        whitespace before matching. (S11.24.d's version of this was a reader
        searching comment-stripped source for a sentence that lives only in a
        comment; same family, opposite direction.)

        24 spec tests; scan PASSES with 18 negative controls, two of them silence
        controls, two driving the carried/visited split BOTH WAYS on the same
        facts. -->
        <!-- NOT DONE HERE: the six are read at the altitude the kit occupies --
        it is a library, so it can make a quality reachable or unreachable but
        cannot itself be accessible, and no browser or endpoint was driven. The
        field counts are a FLOOR over a deliberately tight field list. Nothing is
        fixed: naming a carrier or constraining `messageKey` changes public
        surface, which is S11.24.g and .h. -->

  - [ ] S11.24.g Classify blocker/major/minor/question, owner/fix/decision,
        retest, and resolve all blocker/major and every public API uncertainty.
        Evidence: closed finding ledger.
        <!-- STILL OPEN, DELIBERATELY. The ledger is BUILT, SEVEN ROWS HAVE BEEN
        FIXED AND RETESTED under a public-API authorisation given in the session
        of 2026-08-16, and it still cannot be closed here: `finding-ledger.ts` +
        `.spec.ts` (21 tests) + `tools/finding-ledger-scan.mjs` (PASSES, 18
        negative controls, four of them silence controls). The evidence clause
        asks for a CLOSED ledger; 10 of its 13 rows must close before the freeze
        and 6 are still open, so the checkbox stays `[ ]`.

        FIXED AND RETESTED (each with a citation the scan re-checks, rewritten
        from asserting the defect to asserting the fix, so a row cannot outlive
        its subject in either direction):

          B8  plugins.ts said "ABSENCE IS A VALUE, NEVER AN EXCEPTION" and then
              called `plugin.activate(context)` unguarded, with zero `try` in the
              file. PLUGIN_VIOLATION_KINDS gains `activation-threw` -- the only
              member of that alphabet found by RUNNING rather than by READING --
              and the throw becomes `refused`, never `not-configured`.
          C1  AuthorizedLoad was a PRODUCT in a module whose decision 5 says a
              refusal "carries a stable reason and a remediation, and NEITHER
              CARRIES THE OBJECT". It is now a SUM shaped like `LoadOutcome`
              seventy lines above it. This closes S11.24.c's second half too: the
              old shape returned the object on deny AND step-up, and A DENIAL
              THAT NAMES WHO TO ASK IS ALSO A DENIAL THAT NAMES WHO TO PHISH. The
              one consumer LOST a branch -- the state it tested for has no
              representation now.
          D3  `blobs.STALENESS.unknown` was produced and read by nothing.
              `dispositionFor` gives it a reader, and it gets its OWN disposition
              rather than either neighbour's, because a MOVED ancestor is known
              wrong and re-deriving fixes it, while an UNRESOLVED one is unknown
              and re-deriving from an ancestor you cannot read produces a second
              artifact with the same problem.
          D4  `RIGHTS_HINTS.unknown` was NEVER produced -- a caller-supplied
              field, so not D3's shape at all. TWO doubts sat side by side with
              nothing telling them apart, in the module whose stated subject is
              what a reader can TELL APART. "Nobody wrote it down" is closed by
              the holder writing it down; "we looked and cannot tell" is not.
          E1  `limits.bypassFindings` took `RateRule[]`, so a module that says
              "every one of these limits has a bypass" enumerated two of its six
              categories. It now takes a `LimitPolicy` naming all six, and both
              new members come from the module's OWN law rather than invented
              hazards: a spend scope nobody budgeted is skipped by `reserve` in
              silence, and -- since every cap is a required number, so a MISSING
              one has no representation -- the hazard the type cannot close is a
              cap that never refuses. EVERY COMPARISON WITH NaN IS FALSE.
          F1  `accessibility-subject.ts` is the instrument S11.24.f found
              missing. THE REASON IT WAS MISSING IS THE INTERESTING PART: a scan
              records what it RENDERED and a change records what somebody EDITED,
              and neither says which rendered thing an edited file is in. The
              S11.24.f scan now reports accessibility as
              `visited-with-every-loss-modelled`.
          B1  is NOT fixed and is now BOUNDED -- see below.

        AND THE REVIEW ITSELF WAS THE PROBLEM FIRST. Measuring B1 to decide how
        to fix it showed S11.24's own eight modules had added EIGHT name
        collisions while reporting that exact defect. `SURFACES` was declared by
        `quality-review` and `finding-ledger` -- two modules written in the same
        session -- with entirely different members, and `FACT_SOURCES` collided
        with `graph-views`, whose version the barrel serves, so the bare name and
        the subpath gave two different values. A REVIEW THAT GROWS THE DEFECT IT
        REPORTS HAS NO STANDING TO REPORT IT. All eight renamed; a ratchet
        (`two-door-names-scan`) now fails on a new one and holds the review
        modules at zero. THE FIRST RATCHET WAS BROKEN AND A POSITIVE CONTROL
        CAUGHT IT: it counted colliding NAMES, so a ninth `LIFECYCLE_FAULTS` did
        not move the number -- A RATCHET THAT COUNTS THE NAMES CANNOT SEE A NEW
        COLLISION ON AN OLD NAME. It counts duplicate DECLARATIONS now, against a
        baseline that is measured rather than guessed (the guess was 176 against
        a real 179).

        A FIX IS RETESTABLE AND A DECISION IS NOT, AND THE CELL'S THREE
        RESOLUTIONS SPLIT ON EXACTLY THAT LINE. A fix changes code, so running
        the test again is evidence. A decision changes a record -- an owner
        agreed the behaviour is intended, or that the cost is accepted -- and NO
        TEST PASSES BECAUSE SOMEBODY AGREED. A row claiming a retest on a
        decision is claiming evidence that cannot exist, and that is a fault kind
        here rather than a formality: it is how a ledger reports itself green.

        AND `question` IS NOT A SEVERITY. blocker/major/minor say how much damage
        a finding does; a question says nobody can say yet, because something has
        to be decided before the damage is knowable. It is kept OFF the ordered
        scale, so no sorting of it can make it read as at-least-major.

        AND WHAT MUST BE RESOLVED BEFORE A FREEZE IS DECIDED BY WHERE THE FINDING
        IS, NOT BY HOW BAD IT IS. The cell names two triggers and they select
        different rows. A major finding inside a private helper can ship and be
        fixed next week. A MINOR question about a public signature cannot,
        because S11.24.h freezes that signature and the freeze makes the answer
        permanent -- the cost of being wrong stops being the defect and becomes
        the support window. The two are OR-ed and neither implies the other.

        AND A ROW IS NOT EVIDENCE THAT A FINDING EXISTS. Every row cites
        something the scan RE-CHECKS against the source -- 21 distinct citations
        across 13 rows, all re-checked -- because a ledger is the one artefact in
        this review with an incentive to be wrong: a row saying "resolved" is
        cheaper to write than a fix, and a row whose subject has since changed
        reads exactly like a row that was always right.

        MEASURED: 13 rows over the six cells that found something -- 8 major, 3
        questions, 2 minor; 7 on the public API, 3 outside the kit, 2 on an
        instrument in this kit, 1 on a compiler setting. Exactly ONE row is
        closed, by a decision S11.24.d had already RECORDED (cache has no module,
        and crediting `freshness-recompute` would credit S11.20.d's register with
        a question it was not asked).

        A LEDGER CANNOT CLOSE ITSELF, and that is the finding rather than an
        excuse. Every remaining resolution is one of two things this cell cannot
        perform. Either it is a PUBLIC API CHANGE -- renaming the two-door names,
        turning `AuthorizedLoad` from a product into a sum, widening a union so a
        throwing plugin gets a typed refusal (checked: `PLUGIN_VIOLATION_KINDS`
        has no member that says a plugin threw, and all 46 describe declaration
        defects found by validation, so even the honest repair of `plugins.ts`'
        own rule 4 widens the public surface). Or it is an ACT OUTSIDE THIS KIT
        -- a revoked privilege on a write channel, a repository-wide
        `noUncheckedIndexedAccess` whose blast radius is every project, several
        hundred accessibility call sites. NO DECISION IS INVENTED HERE: an owner
        agreeing that a cost is acceptable is an act a scan cannot perform, and
        writing one down would be the fabrication this whole review exists to
        find. That is precisely the named approval S11.24.h is gated on, so .g
        and .h are not sequential in the way the list implies -- .g's remaining
        resolutions ARE .h's freeze content.

        WHAT REMAINS, AND WHY EACH ONE IS A DECISION RATHER THAN A FIX. Six rows,
        and not one of them is work somebody could just do.

          A1  whether each unreached entry point is intended surface. Only the
              freeze answers that; it IS the freeze.
          B1  which of eight `LIFECYCLE_FAULTS` is "the" one, per name, 46 times.
              Removing a name from the barrel breaks whoever imports it.
          B7  `noUncheckedIndexedAccess` is off; S11.24.b measured that turning
              it on fixes all 31 lookup-table faults without touching either the
              cast or the table. Its blast radius is every project in the
              repository, so the kit cannot take it alone.
          E2  the accessibility opt-outs: several hundred call sites in the
              subject estate, not in this kit. `a11y-waivers` already states the
              repair.
          E3  closing a write channel is an infrastructure act -- a revoked
              privilege, a read-only replica -- that no change to this kit
              performs.
          F2  whether to brand `messageKey`, parse it, or leave the runtime guard
              as the answer. S11.24.f explicitly declined to rule: "the repair is
              NOT to brand every label -- that would be the one-consumer
              abstraction S11.24.b exists to refuse." Doing it unilaterally would
              contradict this review's own finding.

        NO DECISION IS INVENTED HERE. An owner agreeing that a cost is acceptable
        is an act a scan cannot perform, and writing one down would be the
        fabrication this whole review exists to find. -->
        <!-- Retested together after every fix: authorization, preconditions,
        bulk-actions, graph-views, route-states, redaction, plugins, blobs,
        provenance-inspector, limits and the eight review specs; the kit
        typechecks clean (`tsc --noEmit -p tsconfig.lib.json`, exit 0) and all
        eight scans plus the ratchet exit 0. -->

  - [ ] S11.24.h Freeze public API/compatibility/version/support window only
        after clean build/contracts/Tara full suite and named architecture/
        package/security/domain approval. Evidence: P1 API signoff.

---

## Phase I — Isis governed-generation workbenches

Isis is not one new `/studio/isis-workbench` page. It has three governed
audiences: operator factory controls in `apps/isis/web`, AAA-creator controls in
Yemaya Studio, and curated generation cards plus the output gallery in Oshun
Studio. This phase closes the workflow across those hosts while preserving the
generation-tier resolver as the single boundary authority.

### I0 — Product boundary, reuse, and threat model

- [ ] I0.1 Reconcile every Isis route with `STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS`
      and the generation-tier resolver. Evidence: route-boundary test covers
      every discovered route.
      <!-- SUPERSEDED (2026-08-16). The old note said c-g were "runtime/design/
      human and stay unchecked". Six of the seven children are now closed:
      .a .b .c .d .f .g. The claim that .c needed a runtime was wrong -- the
      generation-tier resolver is a pure function and the generator runs it.

      THE PARENT STAYS OPEN ON .e, AND ITS OWN EVIDENCE CLAUSE IS SEPARATELY
      SATISFIED. "route-boundary test covers every discovered route" is true:
      I0.1.f reports 0 uncovered register routes and 0 uncovered BFF modules
      across 1263 executed cases. But .e is a child of this parent and 192 of
      192 route rows are owed at least one cell, on two columns no scan can
      fill -- a data classification for every route, and a keep/refactor/
      redirect/retire disposition for the 104 outside the studio tree. A parent
      cannot be closed over an open child by satisfying its own sentence. -->
  - [x] I0.1.a Enumerate Isis routes from `apps/isis/web`, Oshun Studio, Yemaya
        Studio, BFF/proxy, redirects, route inventories, and deep links with
        source/mount/host. Evidence: route register.
        <!-- evidence: .operatorRoutes (27 apps/isis/web react-router routes w/
                                                                                                                                                                                                                                                                        permission) + .studioRoutes (88 /studio/isis/* Next.js pages) + .bffRoutes
                                                                                                                                                                                                                                                                        (77 admin-isis-* BFF modules), each with source/mount/host. Yemaya Studio
                                                                                                                                                                                                                                                                        has no separate /studio/isis mount (Studio pages are the Oshun-Studio surface).
                                                                                                                                                                                                                                                                        generate-isis-route-register.mjs + 6 tests, wired into inventory/verify. -->
        <!-- CORRECTED IN PLACE (I0.1.f pass, 2026-08-16). Two of the 27
        permission labels were wrong and the test asserted the wrong ones.
        `operatorRoutes()` walked App.tsx line by line, LATCHING `underAuth` and
        `currentPermission` on the way into a guard and never clearing them on
        `</Route>`. That turns "the nearest ENCLOSING guard" into "the most
        recent guard anywhere above", which is a different thing the moment a
        block closes: `/404` and `/*`, declared at the top level of `<Routes>`
        after the RequireAuth block ends, were recorded as `models`-gated. They
        are public. A LATCH IS NOT A SCOPE.

        The parser now walks the TSX syntax tree with a guard STACK pushed and
        popped on element boundaries (self-closing elements enclose nothing, so
        they never contribute a frame). Every other row is byte-identical, which
        is the check that the rewrite reproduces the parts that were right.

        AND THE TEST HAD ENCODED THE DEFECT: `assert.deepEqual(publicRoutes,
        ['/login','/unauthorized'])` passed against the wrong output. Rewritten
        with the fix, plus a second test that pins the nesting semantics
        directly. Downstream: I0.1.d's matrix moves 2 routes from
        live-permission-gated to live-public (see its correction), and its test
        is now pinned to exact counts rather than `>=` bounds, which could not
        tell the corrected number from the wrong one.

        CORRECTED AGAIN IN PLACE (I0.18 pass, 2026-08-16). THE REGISTER WAS
        MISSING A WHOLE SURFACE. It described Isis as spanning three — the
        operator SPA, /studio/isis/* and the admin-isis-* BFF modules — and
        `apps/oshun/admin/src/app/isis/*` was in none of them. That is the
        Oshun ADMIN console: eight server-rendered operator pages
        (civitai-intake, comfy-nodes, lora-training, model-merging,
        output-gallery, runpod-endpoints, voice-cloning, workflow-editor), each
        guarded by `getAdminServerSession()` and
        `sessionCanEnterWorkspace(session, 'isis')`, each calling
        `/v1/admin/isis/*`.

        AND IT IS THE SURFACE THE OPERATOR API EXISTS FOR. `apps/isis/web` does
        not reference the Oshun BFF anywhere — it talks to the Isis
        micro-services on ports 3000-3002 — so a register without the admin
        console held 77 operator API modules whose UI it did not know about.
        The register now enumerates four surfaces; the count moves 192 → 200.

        THE TEST THAT SHOULD HAVE CAUGHT IT PASSED BOTH BEFORE AND AFTER. It
        was called "enumerates all three Isis surfaces" and asserted three
        counts plus the shape of the rows it was handed, so it could not tell
        "the register enumerates every surface" from "the register enumerates
        every surface it knows about". It now pins the SET OF HOSTS, which is
        the claim the cell makes, and a second test pins each admin page's
        session and workspace guard.

        DOWNSTREAM, FOUR CONSUMERS READ NAMED KEYS AND NOTICED NOTHING. Adding
        `adminRoutes` to the register left `isis-effective-route-matrix`,
        `isis-tier-decision-rows`, `isis-route-boundary-cases` and
        `isis-route-dispositions` byte-identical, and every drift check stayed
        green, because each reads `operatorRoutes` / `studioRoutes` /
        `bffRoutes` by name and never asks whether it has read them all.
        ALL FOUR CONSUMERS NOW COVER THE SURFACE. I0.1.d's matrix classifies
        the admin pages under a new `live-workspace-gated` class (200 routes)
        and REFUSES a register carrying a route-bearing key it does not
        classify, with a test that proves the refusal fires. I0.1.c's tier rows
        gain a fourth decider — `oshun-admin(sessionCanEnterWorkspace)` — and
        read 800 rows over 200 entries. I0.1.e's ledger gains eight
        `oshun-admin-page` rows and the pairing they make visible. I0.1.f
        generates forty admin cases whose expectations come from the DECLARED
        `OSHUN_ADMIN_WORKSPACE_MODEL.isis.requiredScopes` table and are executed
        against the real `canEnterAdminWorkspace`, with a control proving the
        authority refuses as well as grants.

        AND THE I0.1.g GATE FIRED, WHICH IS THE POINT OF IT. All eight new
        routes arrived without a disposition, so the gate failed until they were
        recorded in the accepted-undecided baseline — the deferral is now
        written down (112 undecided dispositions, 196 undecided canonical hosts
        over 200 routes) rather than silent.

        THE PAIRING THE FOURTH SURFACE MAKES VISIBLE: 7 of the 8 admin pages
        pair by name with an `admin-isis-*` module, and 70 OF THE 77 MODULES
        HAVE NO PAGE AT ALL. `/isis/workflow-editor` is the eighth page and
        there is no `admin-isis-workflow-editor`; its capability cell is
        recorded `owed` rather than filled from a near-miss like
        `admin-isis-workflow-definitions`.

        This cell stays `[x]`: its own sentence — routes with source, mount and
        host — is satisfied by the corrected register. -->
  - [x] I0.1.b Resolve each Oshun Studio segment against
        `STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS`, including the current empty/
        deny-all behavior. Evidence: segment decision rows.
        <!-- evidence: .segmentDecisions — all 87 distinct /studio/isis/* segments
                                                                                                                                                                                                                                                                        resolved against the EMPTY allow-list ⇒ every one is aaa-only-deny
                                                                                                                                                                                                                                                                        (fail-closed). Cross-checked with the documented 31-segment AAA catalog:
                                                                                                                                                                                                                                                                        56 segments are blocked but NOT in the catalog (doc-vs-code coverage gap). -->
  - [x] I0.1.c Resolve every route/action through the canonical generation-tier
        resolver for actor, tenant, audience, asset/workflow class, and host.
        Evidence: tier decision rows.
        <!-- evidence: isis-tier-decision-rows.json — 768 rows over 192 route
        entries (191 distinct host+path; the operator SPA mounts `/` twice, once
        under RequireAuth and once under RequirePermission(dashboard)) x 4 tiers.
        generate-isis-tier-decision-rows.mts + .test.mts (14 tests), wired into
        inventory + verify.

        THE PRIOR NOTE HERE SAID THIS CELL WAS "runtime, not static". IT IS NOT.
        `resolveGenerationTier` is a pure function of its argument -- no clock,
        no database, no network on any path -- so the generator IMPORTS THE REAL
        MODULE AND RUNS IT. Every verdict below is a return value.

        THE CANONICAL RESOLVER IS CONSULTED FOR ITS LABEL AND NEVER FOR ITS
        DECISION. 88 of 191 routes reach `resolveGenerationTier`; all 88 arrive
        through `resolveStudioBoundary`, which reads `.tier` to pick between two
        denials and never touches `.surfaceAllowlist`. TIER_ALLOWLISTS -- the
        table the whole module exists to hold -- decides ZERO routes. Its one
        production reader is `GET /v1/entitlements/generation/surface/:surface`,
        where the surface is a URL parameter: THE DECISION TABLE IS REACHABLE
        ONLY THROUGH A ROUTE THAT ASKS THE CALLER WHICH QUESTION TO ANSWER. A
        test binds that claim to `studio-boundary.ts` and `AaaGate.tsx`, so
        wiring the allowlist in fails here rather than leaving a confident row
        describing code that moved underneath it.

        A DIMENSION YOU CANNOT VARY IS A DIMENSION NOBODY READ. Of the five the
        cell names, three are fields of `GenerationEntitlement` (actor=userId,
        tenant=tenantId, audience=persona) and two have no argument position at
        all (asset/workflow class, host). ZERO are read. Established by a
        differential probe rather than by reading the parameter list -- hold the
        tier fixed, vary one field, re-ask all 28 surfaces and all 31 AAA-only
        segments, compare the whole answer set. A DECLARED, CARRIED, DROPPED
        FIELD LOOKS EXACTLY LIKE A READ ONE UNTIL YOU VARY IT. The only field
        `resolveTierFromTags` reads is `tags`, which is not among the five.

        AND A PROBE THAT ONLY EVER SAYS NO IS NOT A PROBE. The criterion is
        per-tier on purpose: asking "did anything move anywhere" counts the four
        tiers' own difference as evidence about the probed field. A control runs
        the same probe over `tags` and REQUIRES a positive, plus a nonexistent
        field that must come back negative.

        THE ESTATE HAS FOUR AUTHORITIES AND THE RESOLVER'S DOCBLOCK CLAIMS TO BE
        THE ONE. It says "BFF, Admin, and Studio middleware all consult it
        before rendering any generation surface." Measured: the 77 admin-isis-*
        BFF modules consult `hasAdminScope` over three scopes (admin:*,
        admin:studio, admin:workspace:isis) and none imports the resolver; the
        operator SPA consults `RequirePermission` over ten permission names; the
        Studio middleware consults the boundary. Four alphabets, and 7 of the
        resolver's 28 surfaces name a route in the register at all.

        A REFUSAL AND A RELOCATION READ THE SAME AND ARE NOT. 14 disagreements
        between the boundary and the deny-by-default guard, ALL host-only: the
        guard says allow, the boundary says `render-yemaya-cta`. That branch is
        reached only for aaa-creator/operator-admin and redirects to a page whose
        copy says the caller is already entitled, so it AFFIRMS entitlement and
        moves the host -- the dimension neither can express. Classifying it as a
        refusal was the first version of this measurement and it hid the
        escalation: a POSITIVE CONTROL (inject `audit-trail`, an operator-admin
        -only surface, as a studio segment) passed the ratchet, because "guard
        denies + boundary sends you to the CTA" scored as agreement. Corrected;
        the control now fails two ratchets. Entitlement disagreements: 0, and
        that zero is now a gate, because `resolveStudioBoundary` treats
        aaa-creator and operator-admin identically while TIER_ALLOWLISTS
        separates them by six admin surfaces.

        THE GATE THAT PLUGS THE LEAK SITS BEHIND THE DOOR THAT ALREADY REFUSES
        EVERYONE. `<AaaGate>` exists because route middleware cannot see inside
        a hub that embeds an AAA workspace as a lane. All 6 embeds are mounted
        by pages under `/studio/isis/*`, and with the allow-list empty the
        boundary refuses every one of those segments for all four tiers before
        the page renders. 6 of 6 unreachable in production; the only path that
        reaches them is OSHUN_ENABLE_STUDIO_ISIS_LANE_E2E_BYPASS=true plus
        ?__oshunStudioIsisLaneE2E=1 on localhost -- the test switch that turns
        the outer guard off. The env-var name is read from `proxy.ts` by the
        generator, and a truncating character class ([A-Z_] stops at the digit
        in E2E) shipped a variable nobody could set until a test caught it.

        AND WHERE THEY BOTH RUN, THEY DISAGREE. 6 rows: for aaa-creator and
        operator-admin on gpu-worker / animatediff-lightning / rife-interpolation,
        `proxy.ts` redirects to /aaa-upgrade and `<AaaGate>` renders the
        workspace. Same actor, same segment, same host, opposite answers. Both
        cannot be the product rule. Every instance is currently unreachable, and
        the row records that, because a contradiction you cannot reach is a
        different severity from one you can -- a test fails the day one becomes
        reachable. -->
        <!-- Retested: 14 tests pass; `--check` reports no drift; the positive
        control (an operator-admin-only surface injected as a studio route) fails
        the two ratchets and the surface-binding known-value, then the register
        is restored byte-identical. -->
  - [x] I0.1.d Classify source-mounted but unreachable, feature/provider-gated,
        redirected, proxied, embedded, compatibility, and live routes by
        effective middleware behavior. Evidence: effective route matrix.
        <!-- evidence: isis-effective-route-matrix.json — all 192 routes classified
                                                                                                                                                                                                                                                                        by effective behavior from the I0.1.a/b register + studio-boundary rule:
                                                                                                                                                                                                                                                                        88 /studio/isis/* = gated-concealed (source-mounted but unreachable for the
                                                                                                                                                                                                                                                                        contemplative product via the EMPTY allow-list ⇒ hard-block-404), 77 live
                                                                                                                                                                                                                                                                        operator-api (admin-isis-* BFF), 24 live-permission-gated + 2 live-public
                                                                                                                                                                                                                                                                        (operator SPA). No blank cells; no redirect/proxy/compat shims in the
                                                                                                                                                                                                                                                                        register. generate-isis-effective-route-matrix.mjs + 5 tests. -->
        <!-- CORRECTED IN PLACE (I0.1.f pass, 2026-08-16). The operator SPA
        split is 4 live-public + 1 live-auth-gated + 22 live-permission-gated,
        not 2 + 24. This matrix derives its classes from the I0.1.a register,
        and the register mislabelled `/404` and `/*` as `models`-gated because
        its parser latched the last guard it had seen instead of tracking
        enclosure. Both are public. The 88 gated-concealed and 77
        live-operator-api rows are unchanged, and the finding stands.

        THE TEST COULD NOT HAVE CAUGHT THIS: it asserted `live-public >= 2` and
        `live-permission-gated >= 10`, which both wrong numbers and both right
        numbers satisfy. A DERIVED COUNT ASSERTED AS A LOWER BOUND CANNOT
        DISTINGUISH ITS SOURCE BEING WRONG FROM ITS SOURCE BEING RIGHT. Pinned
        to exact values. -->
  - [ ] I0.1.e Assign canonical owner/host/audience/capability, allowed
        controls, data classification, and keep/refactor/redirect/retire
        disposition. Evidence: completed route rows.
        <!-- STILL OPEN, DELIBERATELY. The rows are BUILT and every cell is
        filled: isis-route-dispositions.json, 192 routes x 8 cells, from
        generate-isis-route-dispositions.mts + .test.mts (14 tests), wired into
        inventory + verify. The evidence clause asks for COMPLETED rows and 0 of
        192 are complete, because two columns cannot be assigned by anything
        that reads code.

        A ROW IS COMPLETE WHEN EVERY CELL EITHER CARRIES A DECISION OR NAMES WHO
        OWES ONE, and the three provenances are the whole design: `recorded`
        (the estate decided, and the cell cites where), `derived` (computed from
        a recorded fact by a rule the cell states), `owed` (nothing decides it;
        the cell names the alphabet the answer must come from and stops). A cell
        filled by this generator's opinion would be WORSE THAN AN EMPTY ONE --
        it reads like a ratified answer and nobody was ever asked. A test
        enforces that an `owed` cell cannot carry a decision and a decided cell
        cannot claim to be owed, in both directions.

        FILLED, WITH SOURCES: owner and owning domain on all 192 from
        `domains.json` (longest-prefix match, because `apps/oshun/` and
        `apps/oshun/bff` would otherwise race and the answer would depend on
        registry order); host-as-built on all 192; audience on all 192;
        capability on all 192 -- 140 admin-isis BFF endpoints parsed from the
        modules, the operator SPA's client operations from
        isis-page-inventory.json, the StudioIsis*Workspace components each
        studio page renders; allowed controls on all 192 (for a BFF module: its
        non-GET endpoints).

        THE ISIS DOMAIN OWNS 27 OF ITS 192 ROUTES. 165 live in another domain's
        applications -- 88 studio pages in `apps/oshun/web` and 77 modules in
        `apps/oshun/bff`.

        A RATIFIED DESTINATION IS STILL A CLAIM ABOUT A PLACE. The §24.11
        Surface Inventory is a real ratified record -- 25 surfaces, each with a
        canonical host and an entitlement -- and it assigns 16 of them to Yemaya
        Studio. Yemaya Studio's own route tables have 39 routes across BOTH
        hosts the document names (`studio-web` AND `studio-desktop`; checking one
        would be an absence claim about a place nobody looked) and NAME NONE OF
        THE 16. The claim is scoped to names on purpose and says so in the
        artifact: whether `/render-farm` is the RunPod operator dashboard under
        another label is a judgement, so the full destination list is published
        beside the number instead of being folded into it. A control proves the
        matcher can match when a route does exist, so "0 of 16" is not an
        artifact of the matcher.

        A KEYWORD CENSUS WOULD HAVE ANSWERED THIS WRONG. Grepping the
        destination for the surface names finds `gaussian` -- in `feGaussianBlur`,
        an SVG filter -- and `runpod` in a render-farm test. Matching is on route
        tables, not free text.

        THREE ARTIFACTS SPELL ONE THING THREE WAYS. Binding a route to a
        ratified row is VERBATIM only, after splitting the document's label on
        `/` (its own punctuation: "Model merger / comparison" is two surfaces on
        one row). 4 of 88 studio routes bind. The 3 surfaces that have a route
        and a plausible row but no shared name -- `3d-generation`,
        `model-comparison`, `music-generation` -- are listed by name with their
        loose candidates, unbound. A substring rule loose enough to catch "Model
        merger / comparison" from `model-comparison` also catches `comparison`
        out of any row that mentions comparing, and then the binding is the
        matcher's opinion rather than the document's.

        A REDIRECT IS NOT AN AUDIENCE. The first version of the audience rule
        was "tiers whose verdict is not hard-block-404", which made
        aaa-creator+operator-admin the audience of all 88 studio routes. They are
        307'd OFF the route. Corrected to "tiers whose verdict is render-as-is",
        and every studio route now reads `none-renders` with the redirect
        recorded in the source. THAT IS HOW A ROUTE WITH NO READERS REPORTS TWO.

        WHAT IS OWED, AND WHY NEITHER IS WORK SOMEBODY COULD JUST DO.

          data classification  0 of 192. The alphabet EXISTS and the estate
              already requires it: DATA_CLASSIFICATIONS (public/internal/
              confidential/restricted/personal) in workbench-kit `registry.ts`,
              and `routes.ts` makes `dataClassification` a required field of a
              registered surface. No Isis route is registered through it, and
              `libs/isis` + `apps/isis` contain zero occurrences of the field.
              Classifying 192 routes is a data-owner's judgement about what each
              one discloses; a scan can name the alphabet and cannot pick a
              member.
          disposition  88 of 192 recorded, 104 owed. The 88 studio pages carry a
              RECORDED `retire` with two citations the tests re-check -- the
              §24.11 "Routes Removed From Oshun Studio" section and the
              `STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS` comment calling the tree the
              "legacy provider-machinery tree" -- plus the named replacement
              (`/studio/generation/*`, `/studio/generation-gallery`). The 27
              operator SPA routes are owed by I0.3, which is the open cell that
              maps `apps/isis/web` pages to keep/refactor/merge/retire; assigning
              them here would pre-empt that cell with an invented decision. The
              77 BFF modules are owed by nobody yet -- nothing in the estate
              records a disposition for them, and that absence is the finding.
          canonical host  4 of 192 recorded. The ratified inventory covers
              generation SURFACES; it names no canonical host for the operator
              factory SPA, for the admin-isis-* BFF modules, or for the 84
              studio segments that are not one of its surfaces.

        4 rows are owed nothing but a data classification. -->
        <!-- Retested: 14 tests pass, `--check` reports no drift, and the
        adversarial stub scan over both new files returns zero hits. -->
  - [x] I0.1.f Add generated route cases that prove each discovered route is
        allowed only for its exact tier/host and concealed/refused elsewhere.
        Evidence: complete route-boundary test.
        <!-- evidence: isis-route-boundary-cases.json — 1263 generated cases,
        0 uncovered register routes, 0 uncovered BFF modules, 140 of 140
        endpoints. generate-isis-route-boundary-cases.mts + .test.mts (9 tests),
        apps/oshun/bff/src/__tests__/isis-route-boundary.spec.ts (3 tests, all
        840 BFF cases), and the generated sweep added to
        middleware-studio-boundary.test.ts (12 tests). All green.

        AN EXPECTATION COMPUTED BY RUNNING THE CODE UNDER TEST CANNOT FAIL.
        Every case's `expected` comes from a DECLARED rule and its `authority`
        is the code that will be executed: the §24.11 document for the studio
        tree, `ROUTE_PERMISSIONS`/`RequireAuth`/`RequirePermission` for the
        operator SPA, `createAuthPreHandler` + the modules' scope alphabet for
        the BFF. A control test enforces the separation — a studio case may not
        cite `studio-boundary.ts` as its expectation source.

        THE SPLIT: 352 studio (88 routes x 4 tiers), 71 operator SPA (each
        guarded route against its own permission, ANOTHER REAL PERMISSION FROM
        THE SAME ALPHABET rather than an invented string, and an empty set), and
        840 BFF (140 endpoints x anonymous / zero-scope / another domain's admin
        scope / each of the three Isis admin scopes).

        THE SWEEP REFUTED ITS OWN DECLARED RULE ON 140 ENDPOINTS. The zero-scope
        identity was written as 403 from `hasAdminScope`, and every endpoint
        answered 401. A GUARD IS A CHAIN AND THE FIRST LINK ANSWERS FIRST:
        `payloadToResolution` treats a token with an empty scope set as an
        INVALID token, so a zero-scope caller never reaches the module's scope
        check. The case is corrected to 401 with the earlier declaration cited,
        and the finding is recorded in the artifact:
        AN IDENTITY THAT IS AUTHENTICATED BUT UNPRIVILEGED HAS NO REPRESENTATION
        AT THIS BOUNDARY -- it collapses into unauthenticated. The 403 half is
        carried by `admin:workspace:aja`, a real scope from another domain, and
        it passes on all 140.

        A LIST OF OBSERVED ROUTES CANNOT OBSERVE A ROUTE ADDED AFTER IT WAS
        TYPED. The middleware suite's `observedLegacySegments` was 56 hand-typed
        strings tested at ONE tier (contemplative), and the `specList` beside it
        is 31 more. The generated sweep drives all 88 register segments at all
        four tiers through the real `proxy()`, so the 56-item list is removed --
        after checking that every one of its segments is in the register, because
        a hand-typed list is only safe to drop once something enumerates a
        superset of it. The doc-catalog `specList` stays: it checks the document,
        not the register, which is a different question.

        AND A SWEEP CAN BE REFUSED BY SOMETHING THAT IS NOT THE BOUNDARY. The
        first middleware run returned 429: `proxy.ts` rate-limits by IP before
        the §24.11 guard, so 352 requests from one address stop being boundary
        observations. Each case is a distinct visitor and now carries a distinct
        `x-forwarded-for`, and an explicit assertion fails on any 429 rather than
        letting a rate-limited request read as a pass. The BFF runner has the
        same guard for the same reason.

        ON "TIER/HOST": no Isis route has both a tier and a host to select
        between. The studio tree is decided by tier at one host; the operator SPA
        by permission; the BFF by scope. The cases test the authority that
        actually decides each route, and I0.1.c is the measurement of why there
        is no third dimension to test. -->
        <!-- Retested: 9 + 3 + 12 tests pass, `--check` reports no drift, the
        three new generators typecheck clean under strict tsc, and the
        adversarial stub scan returns only the documented `:param` URL
        placeholder. -->
  - [x] I0.1.g Fail inventory drift when a new Isis route or allowed segment
        lacks a tier/host disposition. Evidence: deliberate route addition test.
        <!-- evidence: isis-route-disposition-gate.mts (exit 1 with named
        violations) + isis-route-disposition-baseline.json (104 accepted
        undecided dispositions, 188 undecided canonical hosts) + .test.mts
        (9 tests, seven of them deliberate additions). Wired into verify.

        A `--check` DRIFT GATE CANNOT ANSWER THIS. Drift-checking compares a
        committed artifact against a clean regeneration, and a regeneration that
        happily emits a new row saying `owed` is not drift -- it is AGREEMENT.
        The gate asks a different question: which routes are ALLOWED to be
        undecided, and is this one of them.

        AND THE ANSWER IS A SET, NOT A COUNT. A COUNT CANNOT SEE A NEW ROUTE
        ARRIVE WHILE AN OLD ONE IS DECIDED -- the total holds and the new one is
        undecided anyway. The baseline lists route keys and is checked in BOTH
        directions: a route owed a disposition that is not listed fails, and a
        listing whose route has since been decided or removed also fails, so the
        ratchet tightens instead of becoming a permanent licence a future route
        could inherit by reusing a path.

        AN ALLOWED SEGMENT IS HELD HIGHER THAN A BLOCKED ONE. A segment added to
        STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS renders on the contemplative product,
        so its route needs a RECORDED disposition and no baseline entry excuses
        it. A test proves the baseline cannot buy it off: adding the route to the
        accepted list silences the two deferral violations and leaves the
        reachability one standing.

        THE DELIBERATE ADDITION WAS ALSO RUN END TO END, not only against the
        pure function. A real `page.tsx` was written into
        `apps/oshun/web/src/app/studio/isis/gate-e2e-probe/`, the register went
        88 -> 89 studio routes, the dispositions went 192 -> 193 rows, and the
        gate exited 1 naming the new path. Then removed, and all five artifacts
        re-checked byte-identical.

        AND THAT ONLY WORKS BECAUSE THE CENSUS WAS FIXED FIRST. The register
        enumerated studio pages and BFF modules with `git ls-files`, which lists
        TRACKED files only -- so a page that exists on disk and has not been
        `git add`ed is invisible, and THE GATE PASSED FOR EXACTLY THE DEVELOPER
        WHO WAS ADDING THE ROUTE IT EXISTS TO CATCH. Measured on the probe:
        `git ls-files` saw 0, `--cached --others --exclude-standard` saw 1. The
        same fix removed a `catch { pages = [] }` on both enumerations, which
        made "git is broken" and "there are no Isis pages" produce the same
        register.

        THE GATE FIRED ON THE DIMENSION THAT WAS ACTUALLY UNDECIDED. The probe
        produced ONE violation -- no ratified canonical host -- and not a
        disposition violation, because the studio tree's `retire` is recorded for
        the tree and a new page in it inherits that. A gate that had reported
        both would have been reporting its own rules rather than the estate.

        AND THE GATE'S FIRST DRAFT FAILED 77 TIMES ON NOTHING. It compared the
        disposition rows' `path` (a BFF module name) with the case table's
        `route` (an endpoint) and declared all 77 modules untested. A COVERAGE
        CHECK ACROSS TWO ARTIFACTS IS A JOIN, AND A JOIN NEEDS A KEY BOTH SIDES
        CARRY. The covered set now carries both identifiers, and a control test
        asserts both are present. -->
- [ ] I0.2 Ratify operator, AAA-creator, curated-creator, and contemplative
      capabilities separately. Evidence: signed audience-capability matrix.
      <!-- THE MATRIX IS BUILT AND EXECUTED; NOTHING HERE IS SIGNED, so the
      parent and all seven children stay `[ ]`.
      isis-audience-capability-matrix.json — 160 cells over 40 capabilities x 4
      audiences, every cell a return value from the estate's own authority
      (`checkSurfaceAccess`, `checkCardEntitlement`, `validateBulkAction`,
      `resolveGenerationCreatorTier`) run over the real card catalog and the
      real scope declarations. generate-isis-audience-capability-matrix.mts +
      .test.mts (10 tests, 5 of them ratchets on findings), wired into
      inventory + verify.

      SIX ALPHABETS NAME THE SAME FOUR AUDIENCES AND TWO OF THEM CANNOT NAME ONE
      OF IT. `GENERATION_TIERS`, `GENERATION_CREATOR_TIERS`,
      `checkCardEntitlement.creatorTier` and `GalleryView['tier']` all carry four
      members. `CardSpec.minimumTier` and `AuthorizationCheck.tier` carry three,
      and the missing member is `contemplative` -- the contemplative product's
      own audience. A TYPE THAT CANNOT NAME AN AUDIENCE CANNOT REFUSE IT: those
      four cells read `not-expressible`, which is a different fact from `deny`
      and is recorded as one.

      FINDING 1 -- THE PRIMARY AUDIENCE IS ENTITLED TO SURFACES ON WHICH EVERY
      CAPABILITY REFUSES IT. `checkSurfaceAccess` ALLOWS a contemplative caller
      onto `curated-image`, `curated-living-scene` and `curated-voice-clip`.
      `checkCardEntitlement` REFUSES that caller on all 8 catalog cards, because
      every card's `minimumTier` is at least curated-creator and the type cannot
      express anything lower. The §24.11 Surface Inventory agrees with the cards
      (curated-creator+). So the resolver whose docblock calls it "the single
      source of truth" is the one out of step, and the effect today is
      fail-closed -- but `GET /v1/entitlements/generation/surface/:surface`
      answers from the resolver, so THE API THAT ANSWERS THE ENTITLEMENT
      QUESTION AND THE CODE THAT ENFORCES IT DISAGREE ABOUT THE PRODUCT'S
      PRIMARY AUDIENCE. Which of the three is wrong is a product decision.

      FINDING 2 -- THE SAME PREFIX MEANS "ANY ADMIN" IN ONE MODULE AND "THESE
      THREE" IN ANOTHER. Every `admin-isis-*` BFF module accepts exactly
      `admin:*`, `admin:studio`, `admin:workspace:isis`.
      `resolveGenerationCreatorTier` admits ANY scope matching
      `scope === 'admin:*' || scope.startsWith('admin:')` as the top Isis
      audience. Executed: `admin:workspace:aja` and `admin:workspace:yemaya`
      resolve to `operator-admin`, and I0.1.f already measured that both are
      refused 403 by all 140 Isis operator endpoints. ONE IDENTITY, REFUSED BY
      EVERY ISIS OPERATOR ROUTE AND ADMITTED AS ISIS'S TOP GENERATION TIER --
      which is what unlocks every curated card's `minimumTier`. Narrowing the
      rule is an authorization change with a blast radius outside this cell.

      FINDING 3 -- A DECLARED ENFORCEMENT LAYER WITH NO PRODUCER. The card gate's
      docblock says it "is consulted at three layers: component (UI render),
      route (Studio router), and BFF". Measured across every production source in
      `apps/oshun/{bff,web}/src` and `libs/isis`: `bff` has 3 producers,
      `component` has 1, and `route` has NONE -- only a test ever passes it. The
      authorization survives (the BFF enforces), what is missing is the
      route-level concealment, so a refused card's page renders a denial instead
      of not existing. That is the "without relying on UI hiding" question in
      .f, answered: at the route layer, the hiding is all there is.

      FINDING 4 -- AN AUTHORIZATION LADDER WHOSE AUDIENCE ARGUMENT IS A LITERAL.
      `validateBulkAction` distinguishes four audiences across four actions
      (re-tag and send-to-editorial need curated-creator or better; revoke-consent
      and takedown need operator-admin). All three production call sites pass
      `tier: 'operator-admin'` as a constant -- `output-gallery-store.ts:170`,
      `admin-isis-output-gallery-actions.ts:124`, `server.ts:3389` -- so the
      ladder can never refuse. The routes are admin-scope gated, which is why
      this is not a hole today; but an admin SCOPE is not an operator-admin TIER
      (finding 2 is the same seam), and three of the ladder's branches cannot
      fire.

      WHAT IS OWED, PER CHILD. .a-.d ask to DEFINE each audience's actors,
      operational purpose, canonical host, visibility, authority and exclusions.
      The matrix supplies every cell the estate DECIDES; it cannot supply an
      operational purpose or an exclusion nobody wrote down, and I0.1.e already
      measured that the ratified canonical host for 16 of 25 surfaces names a
      destination that holds none of them. .e asks for zero blank cells across
      eight applicability kinds (exposure/observe/control/review/approve/publish/
      recall/support) -- that alphabet does not exist anywhere in the estate, so
      there is nothing to derive it from. .f is MEASURED but not RESOLVED: its
      four findings are two authorization changes and one product decision. .g
      is a named signature from seven parties, which no scan performs.

      NO DECISION IS INVENTED HERE. -->
  - [ ] I0.2.a Define operator actors, operational purpose, canonical host, raw
        workflow/model/provider/resource visibility, mutation/override/emergency
        authority, and exclusions. Evidence: operator matrix rows.
  - [ ] I0.2.b Define AAA-creator actors, Yemaya project/asset context, advanced
        workflow/model/training/3D/voice/music controls, release handoff, and
        forbidden fleet/admin authority. Evidence: AAA rows.
  - [ ] I0.2.c Define curated-creator actors, approved task/asset cards, high-
        level controls, gallery/editorial handoff, and forbidden raw details.
        Evidence: curated rows.
  - [ ] I0.2.d Define contemplative actors, permitted narrow assistive
        generation, consent/safety constraints, output use, and prohibited AAA/
        operator capabilities. Evidence: contemplative rows.
  - [ ] I0.2.e Decide exposure/observe/control/review/approve/publish/recall/
        support applicability per canonical capability and audience. Evidence:
        zero blank matrix cells.
  - [ ] I0.2.f Resolve shared capabilities whose behavior/data detail differs by
        audience without relying on UI hiding. Evidence: contract and
        authorization variants.
  - [ ] I0.2.g Obtain named Isis, Yemaya, Oshun Studio, product, safety/rights,
        security/privacy, and architecture approval. Evidence: signed matrix.
- [ ] I0.3 Map `apps/isis/web` pages to keep, refactor, merge, or retire.
      Evidence: every page and route has one disposition.
      <!-- SUPERSEDED (2026-08-16). The old note said b-g were "runtime/design/
      human and stay unchecked". .b is now closed and the runtime claim was
      WRONG: `apps/isis/web` has no fixture mode at all, so fixtures-off is the
      only mode it has, and what a page does in it is decided by whether the
      service listening at the base URL the client was handed serves the
      endpoint the page calls. That is answered by constructing each service's
      real router, which needs no browser and no running stack.

      THE PARENT STAYS OPEN ON .c-.g, WHICH ARE DISPOSITIONS AND AN APPROVAL.
      Its own evidence clause -- "every page and route has one disposition" --
      is NOT yet satisfied: 0 of 26 pages carry a keep/refactor/merge/retire
      disposition. The walkthrough matrix (a behaviour class per page, with
      121 operations resolved to a service and a verdict) is the input those
      cells were missing, not a substitute for them. -->
  - [x] I0.3.a Enumerate every page/layout/component route, navigation entry,
        deep link, API/provider/store/job dependency, role, state, and test.
        Evidence: source-linked page inventory.
        <!-- evidence: .pages — 26 apps/isis/web pages, each source-linked to its
                                                                                                                                                                                                                                                                        App.tsx route + permission role, with isis-client apiOperations
                                                                                                                                                                                                                                                                        (client.<ns>.<method>, 118 distinct), react-query query/mutation counts,
                                                                                                                                                                                                                                                                        localStateHooks, hooks[], hasTest. Surfaces 2 unrouted page components
                                                                                                                                                                                                                                                                        (JobSubmissionStudio, WorkflowControlBlendPanel) + 6 pages without a test.
                                                                                                                                                                                                                                                                        generate-isis-page-inventory.mjs + 7 tests, wired into inventory/verify. -->
  - [x] I0.3.b Walk each reachable page with fixtures off and classify live,
        partial, fixture, not-configured, dead, duplicate, and unreachable
        behavior. Evidence: page walkthrough matrix.
        <!-- evidence: isis-page-walkthrough.json — 26 pages x a behaviour class
        with its reason, 121 client operations each resolved to a verb, a path,
        the service the SPA actually sends it to and that service's verdict.
        generate-isis-page-walkthrough.mts + .test.mts (22 tests, 6 of them
        ratchets on findings), wired into inventory + verify.

        "WITH FIXTURES OFF" NAMES A MODE THIS APP DOES NOT HAVE, AND THAT IS THE
        FIRST MEASUREMENT. `apps/isis/web` declares no fixture, mock, MSW or
        env-gated fake backend anywhere: 0 of 26 pages carry a fixture signal.
        Every page reads through one `IsisClient` built in
        `IsisClientProvider.tsx` from three base URLs, so fixtures-off is the
        only mode the app has, and a page's behaviour in it is decided by
        whether the service listening at the URL that client was handed serves
        the endpoint the page calls. That is a question about delivery, and it
        is answered by CONSTRUCTING each service's real Hono router and reading
        `app.routes` — not by a regex over route files.

        AN ENDPOINT THAT EXISTS AND AN ENDPOINT THAT ARRIVES ARE DIFFERENT
        CLAIMS, AND THE SPA REACHES ONE OF ITS FOUR NAMESPACES.

          generation  -> generationApiUrl    :3001 -> isis-workflow-registry
          models      -> generationApiUrl    :3001 -> isis-workflow-registry
          workflows   -> workflowRegistryUrl :3003 -> nothing listens
          outputs     -> outputRegistryUrl   :3002 -> isis-output-registry  OK

        The defaults govern, and that is checked rather than assumed: the three
        `VITE_ISIS_*_URL` names appear in `apps/isis/.env.example` only under
        its "referenced by this app but NOT in the canonical .env.example"
        list, so nothing in the estate assigns them a value. Meanwhile
        `isis-generation-api` — 59 routes, the service that owns
        `/api/v1/jobs` and `/api/v1/models` — defaults to PORT 3000, WHICH NO
        CLIENT BASE URL NAMES.

        86 OF 121 OPERATIONS DO NOT ARRIVE, AND ALL 121 ARE BUILT. Asking every
        service the same question, not just the intended one, is what separates
        misdelivery from absence: 0 operations are fictional. 47 reach a service
        that answers 404, 38 reach a port with no listener, 35 are served, and 1
        is served by a service it did not mean to ask.

        A PORT FIX WOULD NOT MAKE THE `workflows` NAMESPACE WHOLE.
        `workflows.run`, `workflows.getExecution` and
        `workflows.getExecutionHistory` are served ONLY by
        `isis-generation-api`, never by the workflow registry. The client models
        `workflows` as one host and the estate splits it across two, so no
        single base URL can be correct for all 37 of its operations.

        THE HEALTH PANEL IS GREEN FOR A SERVICE IT NEVER CONTACTED.
        `client.checkHealth()` is one call that fans out to `GET /health` on all
        three base URLs. `/health` exists on all three services, so the
        generation probe SUCCEEDS — answered by the workflow registry — and
        DashboardPage labels that answer "Generation API: Healthy". The
        workflows probe reports unhealthy because nothing is at :3003. An
        operator's status board is right about outputs, wrong about generation,
        and correct-by-accident about workflows.

        PER-PAGE: 15 not-configured, 9 live, 2 partial, and zero of fixture,
        dead, duplicate or unreachable. EVERY page that touches generation,
        models or workflows is not-configured; the only three that work against
        a backend — OutputsPage, OutputsParityPage,
        OutputComparisonWorkspacePage — are the ones that talk to the output
        registry. The remaining 6 "live" pages call no backend at all, and the
        row says so rather than borrowing the same word silently.

        THE TWO UNROUTED COMPONENTS ARE NOT UNREACHABLE. I0.3.a recorded
        JobSubmissionStudio and WorkflowControlBlendPanel as unrouted, which is
        true; the walkthrough adds that both are RENDERED BY a routed page
        (JobsPage and WorkflowDetailsPage), so neither is dead and neither needs
        its own disposition. `unreferenced` is 0 and so is `dead`.

        FOUR WAYS THE INSTRUMENT WAS WRONG BEFORE IT WAS RIGHT, ALL FIXED AND
        ALL NOW UNDER A CONTROL TEST. An unauthenticated probe returns 401 for
        EVERY path, so "absent" and "refused" are indistinguishable until auth
        is satisfied — the walkthrough forces `allowAnonymous` so the ROUTER
        answers. `client.checkHealth()` is not namespaced, and keying on
        `client.<ns>.<method>` reported DashboardPage as calling nothing.
        ApiParityMonitorPage receives the client as a `currentClient` parameter,
        and keying on the literal name `client` reported it as calling nothing
        either. Widening the receiver to any identifier then swept in
        `workflow.outputs.map(...)`, an array — so the receiver is constrained
        to bound clients plus the `*[Cc]lient` convention, and every rejected
        receiver is PRINTED IN THE ARTIFACT rather than dropped.

        .c-.g stay open and are not blocked on a runtime any more. They are
        Assign/Reconcile cells: keep/refactor/merge/retire is a disposition
        somebody owes, and this matrix is the input they were missing. Note that
        the wiring finding is not itself a disposition — a page is
        `not-configured` because of how the SPA is addressed, not because of
        anything the page is or does, so "retire the not-configured pages" would
        be the wrong reading of this table. -->
        <!-- Retested: 22 tests pass, `--check` reports no drift, and the
        adversarial stub scan over both new files returns zero hits. -->
  - [ ] I0.3.c Assign keep where audience/host/capability and live
        implementation are correct; record missing hardening/proof separately.
        Evidence: keep rows.
  - [ ] I0.3.d Assign refactor with target shared/domain boundary,
        contract/data/ authorization changes, migration slice, compatibility,
        and rollback. Evidence: refactor rows.
  - [ ] I0.3.e Assign merge with canonical destination, source-only behavior to
        preserve, deep-link/state migration, consumer impact, and deletion gate.
        Evidence: merge rows.
  - [ ] I0.3.f Assign retire with replacement/redirect/export/retention/notice/
        owner/date and verify no unique capability is lost. Evidence: retirement
        rows.
  - [ ] I0.3.g Reconcile source routes/navigation to dispositions and obtain
        operator owner approval. Evidence: zero undispositioned pages/routes.
- [ ] I0.4 Map existing Oshun Studio generation cards and gallery routes.
      Evidence: no planned route duplicates an existing approved surface.
  - [x] I0.4.a Enumerate generation cards, entry actions, contexts, high-level
        controls, tier resolver calls, BFF/API operations, output destinations,
        and tests. Evidence: card inventory.
        <!-- evidence: isis-curated-surface-inventory.json .cards — all 8
        CURATED_CARD_CATALOG entries with cardId/kind/displayName/binding/
        minimumTier/tenantAllowlist/killSwitchActive/provenance, the declared
        high-level controls per kind parsed from the card-input interfaces
        (e.g. ambient-audio = durationSeconds, loopable, mood, tempoBpm), the
        BFF endpoints of the 3 modules that consult the card gate, the executor
        each route is wired to, and the tests naming each kind (every card has
        at least one). generate-isis-curated-surface-inventory.mts + .test.mts
        (13 tests), wired into inventory + verify.

        OUTPUT DESTINATION IS A SEAM, AND THAT IS THE ANSWER RATHER THAN A GAP.
        Each route takes a `*Executor` whose default throws `*_not_configured`
        and 503s; `server.ts` injects the real one. Read from the wiring:
        curated-card, music and Nyx-3D get real executors,
        `registerIsisGenerationRoute` keeps `notConfiguredProviderExecutor`. A
        default whose only behaviour is to throw is an honest fail-loud seam,
        so the surface it serves is `not-configured`, not `live`, and the row
        says which.

        TWO SURFACES ARE ENTITLEMENT-CHECKED AGAINST ANOTHER SURFACE'S CARD.
        There is no `music` or `nyx-3d` card kind. `music-route.ts` checks
        against `ambient-audio` and `nyx-3d-route.ts` against `illustration`.
        A KILL SWITCH IS A CLAIM ABOUT ONE SURFACE: `killSwitchActive` and
        `tenantAllowlist` on those two cards now decide for two surfaces each,
        and the borrowing surface has no control of its own. Proven by running
        the gate with the switch flipped, not by reading it. -->
  - [x] I0.4.b Enumerate gallery/list/detail/compare/replay/promotion/history/
        provenance/routes and deep-link state. Evidence: gallery route
        inventory.
        <!-- evidence: .gallery — ONE route, `/studio/generation-gallery`,
        carrying 7 surface families enumerated from the client's own `data-*`
        markers: branch (replay-with-tweak), compare (grid + slots + status),
        filter (10 facets), live (provenance inspector), output, gallery, action.

        THE CELL ASKS FOR ROUTES AND THE ESTATE HAS ONE. The compare grid, the
        branch drawer and the provenance inspector are in-page state, not
        routes, and the inventory says so rather than inventing route names for
        them.

        AND THE DEEP-LINK STATE IS EMPTY. Measured: `useSearchParams`,
        `searchParams`, `useRouter`, `pushState` and `window.location` are all
        absent from the gallery client. A COMPARE GRID NOBODY CAN LINK TO IS A
        COMPARE NOBODY CAN SHOW ANYONE -- ten filters, a comparison and a
        provenance selection, none of which survives a reload or can be sent to
        a colleague. That is the answer to "and deep-link state", and it is a
        finding rather than an omission in the inventory. -->
  - [x] I0.4.c Classify each surface by authorized audience, asset/workflow
        class, live/fixture/provider state, backing contract/service/store, and
        current gaps. Evidence: surface matrix.
        <!-- evidence: the same artifact's per-route and per-card columns.
        Audience: `tierSource` per route (session / request-header / none) plus
        the 160-cell audience matrix from I0.2. Asset/workflow class:
        `binding.workflowClassId` and `provenance.workflowClassId` per card.
        Live/fixture/provider state: read from the executor wiring above --
        3 of 4 generation routes configured, 1 deliberately not. Backing
        service: the BFF module and endpoints each route reaches. Gaps: the
        three sections below.

        ONE CURATED ROUTE TAKES ITS AUDIENCE FROM A CALLER-SUPPLIED HEADER, ON A
        PUBLIC PATH. `/studio/generation/living-scene` is in `proxy.ts`'s
        PUBLIC_PATHS and reads six entitlement inputs straight off the request:
        `x-oshun-tier`, `x-oshun-living-scenes-enabled` (absent ⇒ ENABLED),
        `x-oshun-living-scene-templates`, `-frozen-workflow-classes`,
        `-locales`, `x-oshun-locale`. THE ESTATE ALREADY FIXED THIS ONE LAYER
        DOWN: `libs/shared/bff-kit/src/entitlements.ts` honours `x-oshun-tier`
        only outside production, precisely so "a client can no longer
        self-assert 'pro'". The page has no such guard. The BFF still refuses
        the submission, so this is what the surface OFFERS rather than what it
        grants -- which is exactly the "without relying on UI hiding" question
        I0.2.f asks. It is also the surface that advertises
        `data-living-scene-route-entitlement-gate="route"`, i.e. it claims to BE
        the route-layer gate I0.2 measured as having no producer, while taking
        its input from the caller.

        THE RATIFIED INVENTORY AND THE CARD ALPHABET DO NOT LINE UP. §24.11
        ratifies 9 Oshun Studio surfaces; 3 have no card kind of the same name
        ("Curated music card", "Nyx sky-event 3D briefing", "Output gallery")
        and 2 card kinds have no ratified row (`video`, `video-from-image`).
        The first two are the borrowed-card finding seen from the other side.

        NO SURFACE IS FIXTURE-BACKED IN THIS TREE. The seed/production split
        that exists in the admin output-gallery store has no counterpart on the
        curated routes: they are either wired to a real executor or 503. -->
  - [ ] I0.4.d Match every proposed curated capability/route against existing
        card/gallery behavior and exact reusable component/contract. Evidence:
        proposal-to-existing map.
        <!-- OPEN: `.d`/`.e`/`.f` all take "every PROPOSED curated capability/
        route" as their input, and no proposal register exists. The proposals
        live dispersed through the I1-I6 prose of this file; extracting them is
        a reading of intent rather than a measurement, and a proposal-to-existing
        map built from a guess at the proposal set would be a map of the guess.
        `.g` says "after decisions", and the decisions are .d-.f.

        WHAT IS ALREADY DONE FOR THEM: the existing side of the comparison is
        complete (I0.4.a-.c), and the uniqueness and boundary tests `.g` asks
        for run today against it -- no two curated routes collide, no two share
        a source file, card ids and kinds are unique, and every curated route
        sits under one of the two prefixes `studio-boundary.ts` itself names as
        approved. -->
  <!-- DEDUPED (2026-08-16): I0.4.d appeared twice, the second copy verbatim and
       noteless. A duplicated checkbox is double-counted and only one copy can
       ever be marked, so the bare copy was removed and the annotated one kept. -->
  - [ ] I0.4.e Select extend/refactor/merge/redirect/retire instead of a new
        route whenever an approved surface owns the same audience/workflow.
        Evidence: zero unexplained duplicate proposals.
  - [ ] I0.4.f Record missing approved card/gallery capability as an extension
        task with canonical route and no raw operator/AAA control leakage.
        Evidence: gap rows.
  - [ ] I0.4.g Validate route/navigation/deep-link inventory after decisions.
        Evidence: uniqueness and boundary tests.
- [ ] I0.5 Map Yemaya Studio generation, workflow, model, training, 3D, voice,
      and music controls. Evidence: deep-link and ownership matrix.
  - [x] I0.5.a Inventory high-level and raw generation brief/run/variant/output
        controls and their Isis contract/provider/job paths. Evidence:
        generation rows.
        <!-- isis-generation-controls.json: derived from typed schema (GenerationType/
                                                                                                                                                                                                                                                                        JobStatus/JobPriority/OutputFileType/OutputStatus enums + GenerationJob/
                                                                                                                                                                                                                                                                        GeneratedOutput Prisma models + libs/isis/client resources). 15 GenerationType
                                                                                                                                                                                                                                                                        kinds; the GenerationJob run entity (32 fields bucketed: brief=prompt/
                                                                                                                                                                                                                                                                        negativePrompt/inputUrl, run-lifecycle=status/progress/retry, job-execution=
                                                                                                                                                                                                                                                                        queueName/workerId/gpuType/gpuTimeSeconds/tokensUsed/costEstimate) with
                                                                                                                                                                                                                                                                        JobStatus(6)/JobPriority(4); the GeneratedOutput entity (29 fields) with
                                                                                                                                                                                                                                                                        OutputFileType(11)/OutputStatus(5); contract paths = 4 client resources
                                                                                                                                                                                                                                                                        (generation/models/outputs/workflows); 30 ai-provider paths. brief/run/output/
                                                                                                                                                                                                                                                                        contract/provider/job all map to typed evidence; VARIANT has NO dedicated
                                                                                                                                                                                                                                                                        entity — variants are GeneratedOutput rows sharing a jobId (recorded honestly,
                                                                                                                                                                                                                                                                        not fabricated). generate-isis-generation-controls.mjs + 6 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. This COMPLETES the I0.5.a-d control-inventory family;
                                                                                                                                                                                                                                                                        I0.5.e (3D) and I0.5.f (audio) are now CLOSED and the reason given here
                                                                                                                                                                                                                                                                        for skipping them was wrong: "scattered across many libs (no single typed
                                                                                                                                                                                                                                                                        registry)" describes the shape of the work, not an obstacle to it. A
                                                                                                                                                                                                                                                                        family is covered by DECLARING its owning packages and folding their
                                                                                                                                                                                                                                                                        control-shaped exports mechanically; no single registry is needed. This
                                                                                                                                                                                                                                                                        COMPLETES I0.5.a-f. I0.5.g/h are Assign/Define (design). -->
  - [ ] I0.5.b Inventory workflow graph/template/node/parameter/version/import/
        validation/execution controls. Evidence: workflow rows.
        <!-- SPEC 2026-09-11: this checkbox line had been lost in an earlier
        edit — only its continuation line survived and the evidence comment
        below was left hanging under I0.5.a. Restored as `[ ]`;
        `generate-isis-workflow-controls.mjs` and the comment below still exist,
        so re-verify per §2.1 and flip in the current conversation. -->
        <!-- isis-workflow-controls.json: derived from typed schema + the comfyui
                                                                                                                                                                                                                                                                        surface. 5 graph engines (WorkflowEngine: COMFYUI/BLENDER/UNREAL/GODOT/
                                                                                                                                                                                                                                                                        CUSTOM), 12 WorkflowCategory, 4 WorkflowStatus (DRAFT/PUBLISHED/DEPRECATED/
                                                                                                                                                                                                                                                                        ARCHIVED), 4 WorkflowVisibility (PRIVATE/TEAM/ORGANIZATION/PUBLIC); 4 Prisma
                                                                                                                                                                                                                                                                        entities (Workflow 22 fields incl. runCount/successfulRuns/avgExecutionTime,
                                                                                                                                                                                                                                                                        WorkflowTemplate, WorkflowVersion, WorkflowStar); 9 3d-comfyui-nodes node
                                                                                                                                                                                                                                                                        definitions + 25 comfyui-sdk node types (InputSlot/OutputSlot) + a
                                                                                                                                                                                                                                                                        node-validator (5 exports: checkTypeCompatibility/validateValue/
                                                                                                                                                                                                                                                                        validateNodeSpec) + 3 execution modes (always/on_change/manual). All 8 of
                                                                                                                                                                                                                                                                        I0.5.b's named controls map to typed evidence, NONE absent: graph→
                                                                                                                                                                                                                                                                        WorkflowEngine, template→WorkflowTemplate, node→3d-comfyui-nodes, parameter→
                                                                                                                                                                                                                                                                        InputSlot/OutputSlot, version→WorkflowVersion, import→changelog, validation→
                                                                                                                                                                                                                                                                        node-validator, execution→run-stats+ExecutionMode.
                                                                                                                                                                                                                                                                        generate-isis-workflow-controls.mjs + 5 tests, wired into inventory/verify +
                                                                                                                                                                                                                                                                        CI. Generation (I0.5.a) and training (I0.5.d) controls are separate. -->
  - [x] I0.5.c Inventory
        model/checkpoint/adapter/LoRA/version/intake/evaluation/ selection
        controls. Evidence: model rows.
        <!-- isis-model-registry.json: derived from typed schema (the ModelType/
                                                                                                                                                                                                                                                                        ModelFormat/HashAlgorithm Prisma enums + the ModelCard/ModelRegistry/
                                                                                                                                                                                                                                                                        ModelVersion Prisma models). 7 model kinds (ModelType: CHECKPOINT, LORA,
                                                                                                                                                                                                                                                                        CONTROLNET, VAE, EMBEDDING, UPSCALER, CUSTOM) in 8 ModelFormats
                                                                                                                                                                                                                                                                        (SAFETENSORS/PYTORCH/ONNX/GGUF/BLEND/GLTF/FBX/OTHER), managed by 3 typed
                                                                                                                                                                                                                                                                        entities (52 scalar control fields parsed from *MinAggregateOutputType,
                                                                                                                                                                                                                                                                        each categorized by control dimension identity/classification/version/
                                                                                                                                                                                                                                                                        intake/evaluation/selection). All 8 of I0.5.c's named controls map to typed
                                                                                                                                                                                                                                                                        evidence, NONE absent: model/checkpoint/adapter/LoRA → ModelType (adapter =
                                                                                                                                                                                                                                                                        CONTROLNET/LORA); version → ModelVersion + version/releaseNotes/baseModel;
                                                                                                                                                                                                                                                                        intake → ModelRegistry storagePath/uploaderId/sizeBytes; evaluation →
                                                                                                                                                                                                                                                                        status/isVerified/isActive + 4-algo hash provenance; selection → isPublic/
                                                                                                                                                                                                                                                                        usageCount/downloadCount/starCount ranking. generate-isis-model-registry.mjs
                                                                                                                                                                                                                                                                        + 6 tests, wired into inventory/verify + CI. Generation controls (I0.5.a),
                                                                                                                                                                                                                                                                        workflow controls (I0.5.b), and training controls (I0.5.d) are separate. -->
  - [x] I0.5.d Inventory dataset/training/fine-tune/checkpoint/evaluation/
        promotion/resource/cost controls. Evidence: training rows.
        <!-- isis-training-controls.json: derived from the lora-training-surface
                                                                                                                                                                                                                                                                        typed contracts (libs/isis/lora-training-surface/src/training-run.ts). A
                                                                                                                                                                                                                                                                        9-state TRAINING_RUN_STATES lifecycle (pending-rights-attestation → pre-flight
                                                                                                                                                                                                                                                                        → training → paused → evaluating → pending-promotion → promoted → rejected →
                                                                                                                                                                                                                                                                        aborted), 5 DatasetRightsAttestation assertions (owns-or-licensed/no-pii/
                                                                                                                                                                                                                                                                        no-csam/no-likeness-without-consent/no-third-party-trademarks), 4 evaluation
                                                                                                                                                                                                                                                                        metrics (fid/clip-score/psnr/ssim), 38 typed control fields (rank/epochs/
                                                                                                                                                                                                                                                                        learningRate/costEstimateCents/checkpointStep/datasetSizeBytes/
                                                                                                                                                                                                                                                                        promotedManagedModelId/…) across 4 training libs (lora-training-surface,
                                                                                                                                                                                                                                                                        model-fine-tuning, training-data, gaussian-splatting) + a multi-GPU
                                                                                                                                                                                                                                                                        orchestration resource provider. All 8 of I0.5.d's named controls map to
                                                                                                                                                                                                                                                                        typed evidence, NONE absent: dataset→rights-attestation, training→lifecycle,
                                                                                                                                                                                                                                                                        fine-tune→LoRA-rank, checkpoint→CheckpointSample, evaluation→metrics,
                                                                                                                                                                                                                                                                        promotion→promoted-state, resource→multi-gpu, cost→costEstimateCents.
                                                                                                                                                                                                                                                                        generate-isis-training-controls.mjs + 7 tests, wired into inventory/verify +
                                                                                                                                                                                                                                                                        CI. (Also removed a duplicated I0.5.d anchor line.) I0.5.a (generation) and
                                                                                                                                                                                                                                                                        I0.5.e+ (scene/mesh/rig/DCC) remain. -->
  - [x] I0.5.e Inventory scene/mesh/rig/material/texture/splat/animation/DCC/
        engine generation and post-process controls. Evidence: 3D rows.
        <!-- evidence: isis-3d-controls.json — all NINE named families resolved
        to typed surfaces: 7,386 controls across 273 files, every family
        `typed`, nothing owed. generate-isis-3d-controls.mjs + .test.mjs
        (13 tests), wired into inventory + verify. Shares its extractor with
        I0.5.f, so both inventories read source the same way.

        Each family declares its OWNING PACKAGES and every control-shaped export
        in them is folded in mechanically. Two families share one library and
        are split by a declared file pattern: `libs/isis/universal-rigging`
        holds both the skeleton controls (rig) and the clip/blend controls
        (animation), and folding it whole into either row would misreport both.

        A SURFACE CAN BELONG TO TWO FAMILIES, AND THE ARTIFACT SAYS WHICH. 20
        surfaces are claimed twice and each is listed in `overlaps`. They are
        not mistakes: a Godot resource export is an interchange format AND an
        engine target, and `humanoid-animation-retargeting` is retargeting (a
        rig concern) applied to animation. A first version of the test asserted
        the rig/animation split was disjoint; the split is right and the
        assertion was wrong, so the test now requires that every shared file be
        RECORDED as an overlap rather than absent.

        AN ALPHABET CAN BE DECLARED AND EMPTY, AND THE FIELD IT FEEDS DECIDES
        WHAT THAT MEANS. 18 constants are `[] as const`, so every caller sees a
        named vocabulary with no word in it. Reading them without their consumer
        would report one fact where there are three. 12 are named `*_PLANNED_*`.
        Five feed `deferredTaskIds`, where empty says NOTHING IS DEFERRED — the
        opposite of a gap. The last one is the interesting one:
        `LOCAL_3D_UNIVERSAL_RIGGING_SUPPORTED_INPUT_FORMATS` is `[]`, feeds
        `supportedInputFormats`, and sits beside
        `..._PLANNED_INPUT_FORMATS = ['obj','glb','fbx','vrm']` with a test
        pinning the empty one. THE RIGGING LIBRARY SAYS PLAINLY THAT IT READS NO
        INPUT FORMAT YET AND NAMES THE FOUR IT INTENDS TO. That is an honest
        declaration, not a stub, and it is recorded as one.

        EXTRACTOR CORRECTIONS MADE WHILE BUILDING THIS, BOTH NOW UNDER TEST. An
        alias chain routinely passes through a MODULE-PRIVATE type
        (`export type X = UnrealImportMode` where
        `type UnrealImportMode = (typeof MODES)[number]`); refusing to follow an
        unexported link reported 34 real alphabets as having no members. And an
        interface field whose type WRAPS onto the next line
        (`readonly humanBones?:` then a wrapped `Partial<Record<…>>`) was read as
        no field at all, which made an interface with members report zero — a
        wrong answer rather than a missing one. Both fixes flow back into I0.5.f.

        The boundary is published: 422 files export a symbol in the 3D
        vocabulary, 273 are folded into rows, and the rest are listed by name
        with their symbols. Eight symbols are skipped and listed; each is an
        alphabet that really is empty, not an extraction failure.

        This CLOSES the I0.5.a-f control-inventory family. .g (Assign each
        control to an owner) and .h (Define signed deep-link/handoff context)
        are design cells and stay open. -->
        <!-- Retested: 13 tests pass, `--check` reports no drift, and the
        adversarial stub scan over both new files returns zero hits. -->
  - [x] I0.5.f Inventory voice/TTS/cloning/dubbing/conversation/foley/audio and
        music/stem/composition/mastering generation controls. Evidence: audio/
        music rows.
        <!-- evidence: isis-audio-controls.json — all ELEVEN named families
        resolved to typed surfaces: 1,947 controls across 79 files, 10 `typed`
        and 1 `partial`. generate-isis-audio-controls.mjs + .test.mjs (14 tests,
        2 of them ratchets on findings), wired into inventory + verify.

        Method follows I0.5.a-.d: a family is covered only by a TYPE, and the row
        carries that type's actual members. Four shapes are read from source —
        interfaces with their optional flags, string/numeric unions, `as const`
        arrays, and zod `object`/`enum`. The zod reader matters: the real
        generation parameters for music, voice and SFX are module-PRIVATE consts
        in the generation-API pack schemas, and being unexported does not make a
        control surface less of one.

        A ROW'S COVERAGE MUST NOT DEPEND ON WHICH SYMBOLS A HUMAN LISTED. Each
        family pins the shared types it needs by name, and then its OWNING
        PACKAGES are folded in mechanically by the estate's own naming
        convention for a control (`*Request`/`*Options`/`*Config`/`*Settings`/
        `*Params`/`*Schema`/`*Kind`/`*Mode`/`*Format`/`*Strategy`/`*Profile`).
        Hand-listing had given `cloning` 5 controls; the package fold gives it
        317, because `libs/isis/voice-cloning` carries a `*Request` per
        capability — age-modified synthesis, archival restoration, multilingual
        preservation, speech-to-speech, consent rights, watermarking — and none
        of them was in anybody's list.

        MASTERING IS THE ONE PARTIAL, AND THE SHAPE OF THE GAP IS THE FINDING.
        Loudness is typed twice over: as a TARGET the caller sets
        (`target_lufs` on the pack schema, range -32..-6), as a MEASUREMENT
        taken afterwards (`SectionLoudnessSchema.integratedLufs` /
        `.truePeakDb`, `SongSeamMeasurement.loudnessDeltaLu`), and as a ceiling
        on the bus (`MixBus.limiterCeilingDbFs`). What has NO type anywhere is
        the processor chain that would get from one to the other — zero hits for
        a mastering chain, master bus, multiband compressor or mastering preset
        across 3,646 tracked Isis sources. THE ESTATE CAN STATE A LOUDNESS
        TARGET AND CHECK WHETHER IT WAS MET, AND TYPES NOTHING THAT WOULD MEET
        IT.

        AN ABSENCE IS ONLY RECORDED WHEN IT WAS LOOKED FOR, AND THE PROBE THAT
        LOOKS IS PART OF THE BUILD. Two absences claimed on a first pass were
        WRONG and the probes killed them: `composition` was called partial for
        having no section model, and `libs/isis/workflows/src/ledger/
        song-ledger.ts` types a song as eight ordered `SongSectionKind` parts
        with a `MusicalKey` and `SongLocks`; `mastering` was called absent for
        having no loudness control, and LUFS appears in 35 files. Both errors
        came from reading a `grep | head` as if it were the whole answer. The
        generator now THROWS if a negative probe starts matching, so the same
        mistake cannot be committed.

        WHAT THIS INVENTORY DOES NOT FOLD IN IS PUBLISHED, NOT OMITTED. The same
        audio vocabulary is swept across every tracked Isis source: 91 files
        export a matching symbol, 43 are folded into rows, and the other 48 are
        listed by name with their symbols. Most are providers, quality gates, UI
        helpers and 3D files that matched on `Mix`. One deliberate exclusion is
        recorded in the source: folding `libs/isis/workflows/src/ledger` as a
        package added 638 controls about lecture overlays and narration arcs to
        a row about musical composition, so the song ledger is pinned by file
        and the rest stays in the published gap. A ROW THAT GAINS VOLUME AND
        LOSES MEANING IS WORSE THAN A GAP SOMEBODY CAN SEE.

        NOT RECORDED AS A DEFECT, DELIBERATELY: the control-conditioned variant
        of all three audio packs carries the image ControlNet vocabulary
        (`pose_map`, `depth_map`, `lineart_map`, `use_canny_control`,
        `*_strength`). It looks wrong on a voice line until you find
        `control_applicability_mode: 'auto' | 'where-applicable' | 'strict'`
        beside it, which is a declared seam for exactly that. Whether a uniform
        cross-modal control contract is right is a product decision, so the
        measurement is recorded and the verdict is not. -->
        <!-- Retested: 14 tests pass, `--check` reports no drift, and the
        adversarial stub scan over both new files returns zero hits. -->
  - [ ] I0.5.g Assign each control to Yemaya, Isis operator, domain plugin, or
        excluded surface with owner, audience, capability, contract, route, and
        permitted detail. Evidence: ownership matrix.
  - [ ] I0.5.h Define signed deep-link/handoff context for project/asset/shot/
        timeline/run/output/return path and test tenant/expiry/permission.
        Evidence: deep-link matrix.
- [ ] I0.6 Map all `libs/isis/*` packages to operator, creator, worker, shared
      service, or out-of-scope. Evidence: all discovered packages classified.
      <!-- partial: package capability inventory (a) done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/isis-package-inventory.json. b-d (assign operator/
                                                                                                                                                                                                                                                                      creator/worker ownership per package) are design decisions and stay
                                                                                                                                                                                                                                                                      unchecked; roleSignal is provided as raw input.

                                                                                                                                                                                                                                                                      .e IS NOT PURELY A DESIGN DECISION AND THE OLD NOTE SAID IT WAS. Its
                                                                                                                                                                                                                                                                      sentence states two NECESSARY conditions, and applying them is a
                                                                                                                                                                                                                                                                      measurement: isis-shared-package-gate.json excludes 55 of 78 packages on
                                                                                                                                                                                                                                                                      those grounds alone. It stays unchecked because a necessary condition
                                                                                                                                                                                                                                                                      cannot confirm a class -- see the note on .e itself. -->
  - [x] I0.6.a Enumerate every Isis library/export/consumer/dependency/test/
        runtime/contract/entity/job/provider and atomic capability. Evidence:
        package capability inventory.
        <!-- evidence: .packages — all 76 libs/isis/* packages consolidated from the
                                                                                                                                                                                                                                                                        BASE.3 library graph (exports/inbound/outbound/disposition) + BASE.9
                                                                                                                                                                                                                                                                        capabilities (496 entity+event attributed via source.file) + BASE.1 tests
                                                                                                                                                                                                                                                                        (1138 files). App-surface capabilities (api-operation/command from apps/isis,
                                                                                                                                                                                                                                                                        apps/oshun/bff) stay in I0.1/I0.3 by design. 29 no-consumer retire-candidates
                                                                                                                                                                                                                                                                        surfaced. generate-isis-package-inventory.mjs + 7 tests, wired into inventory/verify. -->
  - [ ] I0.6.b Classify operator-owned packages that expose fleet, provider,
        workflow/model registry, policy, emergency, economics, parity, or
        administration. Evidence: operator rows.
  - [ ] I0.6.c Classify creator-facing packages for brief/workflow/variant/
        preview/compare/review/gallery or authorized project handoff. Evidence:
        creator rows.
  - [ ] I0.6.d Classify worker/runtime packages for generation, training,
        processing, validation, lineage, storage, queue, and provider adapters
        with operational projection. Evidence: worker rows.
  - [ ] I0.6.e Classify truly domain-neutral contract/service helpers as shared
        only with two real consumers and correct dependency direction. Evidence:
        shared rows.
        <!-- STILL OPEN, DELIBERATELY, AND THE GATE IS BUILT.
        isis-shared-package-gate.json — all 78 `libs/isis/*` packages put through
        the two conditions this cell states, with the evidence for each beside
        it. generate-isis-shared-package-gate.mjs + .test.mjs (12 tests), wired
        into inventory + verify.

        A NECESSARY CONDITION CAN EXCLUDE AND CANNOT CONFIRM. "only with two real
        consumers and correct dependency direction" is a gate on the way in, not
        a definition of the class. `truly domain-neutral` is the sufficiency test
        and this cell reserves it, so the artifact records `classifiedShared: 0`
        and never guesses. 55 packages are EXCLUDED on the cell's own grounds —
        that half is decided and is not owed to anybody. 23 are admissible and
        await one judgement each.

        WHY THE CONSUMER EDGES ARE RECOMPUTED RATHER THAN REUSED. The BASE.3
        library graph counts test importers deliberately — its own header says
        "application/service/worker/CLI/test/library consumers" — so a package
        imported only by somebody's spec file reads as consumed there. This cell
        says REAL consumers, so consumption is recounted at file level and a
        production import is distinguished from a test one. Result: 52 of 78
        packages do not have two production consumers, and 29 HAVE NO CONSUMER
        AT ALL — the same 29 I0.6.a's inventory reports as no-consumer
        retire-candidates, reached by a different route. Two instruments
        agreeing is worth more than either alone. The 29 are the input I0.6.f
        needs for its dead class; the disposition itself is .f's to make.

        DEPENDENCY DIRECTION IS ABOUT THE SCOPE, NOT THE COUNT. A shared helper
        sits BELOW every domain, so the clause is "imports no domain-scoped
        package at all". `@oshun/*` is the shared scope and does not count
        against it; `@isis/*` does, because a package that imports its own domain
        cannot move to `libs/shared` without dragging the domain with it. 11
        packages fail on that alone — `@isis/3d-semantic-editing` importing
        `@isis/universal-rigging` is the shape.

        TWO WAYS THE INSTRUMENT COUNTED THINGS THAT WERE NOT THERE, BOTH FIXED
        AND BOTH NOW UNDER A CONTROL TEST. A path that is not a project is not a
        domain: the first version fell back to the first two path segments for
        any importer, which made `eslint.config.js` and `scripts/v1-workbenches`
        — THIS GENERATOR'S OWN DIRECTORY — read as domains, and eight packages
        looked domain-neutral on the strength of a lint config importing them.
        And A QUOTED PACKAGE NAME IS NOT AN IMPORT: matching every
        `'@scope/name'` string made THIS GATE'S OWN TEST FILE, which compares
        against package names in its assertions, a consumer of the packages it
        tests — the artifact drifted the moment the test was written. Specifiers
        must now sit in an import position. That correction also removed `v2`
        from the consumer domains: `apps/v2/isis-*` projects NAME Isis packages
        in strings and import none of them.

        DOMAIN SPREAD IS REPORTED BESIDE THE VERDICT, NOT FOLDED INTO IT. Of the
        23 admissible, the ones consumed by another PRODUCT domain (yemaya,
        metis, nous, psyche, neith) are the strongest candidates; the rest are
        consumed by `isis` and by the host app `apps/oshun/*`, which is not the
        same fact as being used by two products. That distinction is
        evidence for the judgement, and making it here would BE the judgement. -->
        <!-- Retested: 13 tests pass, `--check` reports no drift, and the
        adversarial stub scan over both new files returns zero hits. -->
  - [ ] I0.6.f Classify compatibility/dead/experimental/vendor/test-only/
        superseded/non-V1 packages with retain/retire/exclude rationale,
        consumers, owner, and date. Evidence: out-of-scope rows.
  - [ ] I0.6.g Resolve mixed packages at symbol/capability level rather than one
        broad package label. Evidence: split mapping.
  - [ ] I0.6.h Reconcile every filesystem package to exactly one or more
        capability rows with no unclassified library. Evidence: path check and
        domain-owner signoff.
- [ ] I0.7 Decide whether any new Isis workbench domain library is needed.
      Evidence: ADR prefers existing `@oshun/generation-control-isis`/domain
      packages unless a concrete gap is demonstrated.
  - [ ] I0.7.a Define the exact missing capability/ownership/dependency/API/
        runtime/test problem that cannot live in an existing Isis package.
        Evidence: source-linked gap statement.
  - [x] I0.7.b Evaluate extending `@oshun/generation-control-isis`, another
        existing domain package, the shared workbench kit, an application-local
        adapter, and no-new-code options. Evidence: option matrix.
  - [x] I0.7.c Compare public API, dependency direction, consumer count,
        domain-specific semantics, runtime boundary, version/release cadence,
        migration, and deletion cost. Evidence: trade-off table.
  - [x] I0.7.d Prototype the smallest interface in the preferred existing
        boundary and record why it succeeds or fails. Evidence:
        compile/dependency proof.
  - [ ] I0.7.e If a new library is justified, define owner, scope/non-goals,
        exports, dependencies, two consumers, contracts, migration, and deletion
        threshold. Evidence: proposed package design.
  - [ ] I0.7.f Record the chosen decision, rejected options, consequences,
        compatibility, review date, and named architecture/Isis approval.
        Evidence: ratified ADR.
        <!-- MEASURED 2026-08-17 (I0.7.a–f). THE ANSWER IS NO, AND THE REASON IS
        MORE USEFUL THAN THE ANSWER: THE OBSTACLE IS NOT A MISSING BOUNDARY, IT
        IS AN UNREACHED ONE. `evidence/v1-workbenches/isis-library-gap-decision.json`
        (writer + 14 tests, wired into `verify:inventory:v1-workbenches` before
        the design doc) measures the five gaps this lane has already recorded and
        **0 of 5 justify a new library**.

        THE GAPS ARE NOT INVENTED IN THE INSTRUMENT. Each row is read out of
        another committed artifact by dotted path, so the list is the estate's own
        record: asset-kind taxonomy (I0.8), data-class register (I0.11),
        emergency-control commands (I0.12), threat-to-test map (I0.10),
        output-format columns (I0.19). A gap whose source stops reporting it
        leaves the matrix; a new one appears in it.

        TWO OF THE FIVE HAVE AN EXISTING HOME THAT NOTHING ON THE ISIS PATH
        REACHES. `libs/contracts/src/common/asset.ts` already declares
        `AssetTypeSchema` (image|video|audio|model_3d|texture|material|animation|
        …) and `libs/contracts/src/common/data-rights-scopes.ts` already declares
        `OshunArtifactType`, and **zero on-path modules reach either**. So those
        two gaps are `existing-home-unreached`: no library is missing, a boundary
        is unvisited. A third (`output-format-columns`) is
        `existing-home-reached-needs-extending` — `OutputArtifactManifest` exists
        and is reached, and three columns are simply absent from it. A fourth
        (`emergency-control-commands`) must name Isis domain types, so its home is
        a `libs/isis/*` package, of which 78 already exist. The fifth is not a
        library question at all: A MAPPING BETWEEN A THREAT LIST AND A TEST ID IS
        DATA, AND NO PACKAGE CAN OWN IT.

        A LIBRARY IS A DEPENDENCY DIRECTION BEFORE IT IS A FOLDER, so the
        direction was measured before any option was scored: **45 `libs/isis/*`
        packages depend on `@oshun/*` and exactly ONE `libs/oshun/*` package
        depends on `@isis/*`** — `libs/oshun/privacy`, whose own header calls it
        "the account-deletion fan-out's intentional, cross-cutting reach into a
        domain data store… the one place the privacy deletion engine must touch
        domain persistence". A rule with one exception that documents itself is
        still a rule; the instrument throws if that exception moves. That
        direction is what decides the host: a capability expressible over shared
        contracts fits the preferred `@oshun/*` boundary, one that must name an
        `@isis/*` type does not.

        (.c) IS EIGHT MEASURED COLUMNS, NOT FOUR AND A PARAGRAPH. Every column
        the cell names maps to an OptionRow member and a test asserts the mapping
        is total, so a column cannot be answered in prose. The table is worth
        reading for the shape of the choice: the preferred library is **452
        public symbols / 24 consumers / `layer:domain` / esm, side-effect-free,
        source entry**, and the shared workbench kit is **14,298 symbols / 128
        consumers / `domain:neutral`** — so adding an Isis vocabulary there would
        change what the package IS, which its own Nx tag declares. The
        application-local adapter is the cheapest to delete (nothing outside the
        BFF can import it) and the kit the dearest.

        (.d) IS EXECUTED, NOT ASSERTED. The smallest interface that would give
        I0.11 its missing row — a binding from an Isis record kind to the
        canonical artifact type that already carries an export scope and a
        retention rule — was WRITTEN into
        `libs/oshun/generation-control-isis/src/`, compiled by `tsc` with the
        repo's real path mappings (**exit 0, zero diagnostics**), and DELETED in a
        `finally` whether it passed or failed: a speculative interface must never
        survive the measurement that used it. A test asserts the file is gone, and
        `.gitignore` covers it so a hard kill mid-measurement cannot leave
        something committable.

        THE DEPENDENCY HALF IS A SEPARATE CHECK AND THAT SEPARATION IS THE POINT.
        `tsconfig.base.json` maps **`@isis/*` → `./libs/isis/*/src/index.ts`** — a
        WILDCARD — so every Isis package resolves for every file in the repo, and
        none of them is a dependency of the preferred package. A COMPILER THAT
        RESOLVES AN IMPORT HAS NOT TOLD YOU THE IMPORT IS ALLOWED: the mapping is
        repo-wide, the dependency is per package, and only the second is a
        boundary. So the compile proof and the dependency proof are asked and
        reported separately.

        TRAPS. (1) `git ls-files 'dir/**/*.ts'` MATCHES NOTHING — the `**`
        pathspec is not expanded like a shell glob, and it silently reported ZERO
        exports for a 42-file package; `git ls-files 'dir'` is already recursive,
        and a control test pins the collision. (2) The first run claimed
        `apps/isis/output-registry/src/types.ts` declares `ArtifactManifest`; it
        declares `OutputArtifactManifest`, and the word-anchored check correctly
        refused to match inside it — the verification caught the author's own
        error rather than emptying a row.

        CELLS MARKED: .b (the option matrix covers all five options the cell
        names, each with measured columns), .c (all eight columns measured for
        every option, mapping asserted total), .d (prototype written, compiled at
        exit 0, dependency-checked and deleted). CELLS LEFT UNCHECKED: .a — "define
        the exact missing problem that cannot live in an existing package"
        presupposes such a problem, and the measured answer is that there is none
        today, which is an ongoing obligation rather than a closed one; .e is
        conditional on a new library being justified and the condition is unmet,
        so offering a design anyway would smuggle a decision in as a deliverable;
        .f needs a ratified ADR with named architecture/Isis approval, which is
        not locally actionable. The PARENT stays open on .a/.e/.f even though its
        own bar — "ADR prefers existing packages unless a concrete gap is
        demonstrated" — is now satisfied on the evidence side: no gap is
        demonstrated, and the ADR that would say so has no author. -->
- [ ] I0.8 Define the asset-kind taxonomy for image, portrait, texture, video,
      audio, foley, voice, mesh, rig, gaussian splat, and composite scenes.
      Evidence: registry maps each kind to contract, preview, gates, and
      publisher.
      <!-- THERE IS NO REGISTRY. THE PARENT AND ALL THIRTEEN CHILDREN STAY
      `[ ]`.

      evidence/v1-workbenches/isis-asset-kind-registry.json —
      generate-isis-asset-kind-registry.mts + .test.mts (10 tests, 4 ratchets,
      3 controls), wired into inventory + verify. The eleven kinds and the four
      columns are parsed out of THIS parent's own sentence, so its bar is the
      measurement's bar.

      THE CONTRACTS ARE REAL AND THEY ARE A REFERENCE. Executed against
      `evidence/v1-workbenches/asset-contracts`: all ELEVEN kinds' minimal
      fixtures parse and round-trip byte-stably through the public API, an
      unexpected field is refused with `image: unexpected field "sneakyExtra"
      (no unknown-payload bag)` rather than absorbed, and each kind declares its
      own required fields (5 for image up to 11 for scene). That is a genuine
      contract per kind — and NOTHING UNDER apps/ OR libs/ IMPORTS IT, so all
      eleven kinds have no product contract.

      THE ESTATE HAS SEVENTEEN ASSET-KIND ENUMERATIONS AND NONE IS THIS
      TAXONOMY. The best overlap with the eleven kinds is six, and it belongs to
      another evidence reference. On the Isis surface itself the only one is
      `unity-package-export`'s four-member `['material','model','prefab',
      'texture']`. THE LARGEST BY MEMBER COUNT IS A TRAP: `studio-authoring`
      declares eleven kinds — ambient-loop, manuscript-scan, ritual-sound-pack,
      voiceover — a DIFFERENT taxonomy that happens to be the same size, and a
      first cut reported it as the product's asset-kind enum on the strength of
      the number alone.

      TWO AXES THAT DO NOT MEET. The estate gates by ASSET FAMILY — 32 of them,
      each with a quality gate and a regression golden dataset — and this
      taxonomy is by ASSET KIND. NOT ONE of the 32 families declares a kind;
      they declare OUTPUT CLASSES instead (`audio_master`, `metadata_manifest`,
      `preview_image`, `project_archive`, `source_frames`, `video_master`), so
      `material-pbr-pack` — a texture family — announces itself as producing a
      `preview_image`. The gates column cannot be filled from the families
      because nothing maps a family to a kind.

      A MENTION IS NOT A MAPPING, and that distinction is the difference between
      this cell being nearly done and not started. Fifty-two Isis modules whose
      file name says gate or quality contain the word "image"; counting those
      reported SEVEN OF ELEVEN kinds as having all four columns. The artifact
      records the mention counts as context and answers the parent's question
      with `columnsMappedByARegistry`, which is 0 for every kind because there
      is no registry to do the mapping.

      AND I0.8.a'S NINE ATTRIBUTES HAVE ONE CARRIER BETWEEN THEM. Identity,
      version, parent/family, aliases, media/profile, lifecycle, owning package,
      audience/host, extension policy: a bare string-literal union carries the
      first and none of the rest. -->
      <!-- I0.8.m's validation questions — unique ids and aliases, no ambiguous
      kind resolution — are answered for the REFERENCE by the round-trip and
      required-field cases above; for the product there is no registry to
      validate. -->
  - [ ] I0.8.a Define taxonomy entry identity/version, parent/family, aliases,
        media/profile, lifecycle, owning package, audience/host, and extension
        policy. Evidence: asset-kind registry schema.
  - [ ] I0.8.b Define generic image kind controls/outputs, metadata, visual
        preview/semantic alternative, quality/safety/rights/provenance gates,
        and publishers. Evidence: image registry row and fixtures.
  - [ ] I0.8.c Define portrait kind identity/reference/consent controls, face
        preview/alternatives, impersonation/biometric/safety/quality gates, and
        publishers. Evidence: portrait row and fixtures.
  - [ ] I0.8.d Define texture kind tiling/channels/color/PBR target controls,
        material preview/metadata, seam/technical/rights/provenance gates, and
        publishers. Evidence: texture row and fixtures.
  - [ ] I0.8.e Define video kind duration/frame/rate/size/codec/motion/audio/
        caption controls, synchronized preview/alternatives, temporal/safety/
        rights/quality gates, and publishers. Evidence: video row and fixtures.
  - [ ] I0.8.f Define general audio kind duration/sample/channel/format/content
        controls, waveform/audition/transcript alternative, loudness/safety/
        rights/quality gates, and publishers. Evidence: audio row and fixtures.
  - [ ] I0.8.g Define foley kind event/action/material/scene/time/variation/
        synchronization controls, audiovisual preview, originality/rights/
        technical gates, and publishers. Evidence: foley row and fixtures.
  - [ ] I0.8.h Define voice kind speaker/language/style/text/timing/delivery and
        consent constraints, audio/transcript preview, cloning/impersonation/
        safety/rights/quality gates, and publishers. Evidence: voice row.
  - [ ] I0.8.i Define mesh kind units/axes/topology/geometry/material/UV/LOD/
        collision/format controls, 3D/metadata preview, OpenUSD/geometry/rights/
        provenance gates, and publishers. Evidence: mesh row and fixtures.
  - [ ] I0.8.j Define rig kind skeleton/joint/naming/orientation/constraints/
        skin/controls/target controls, hierarchy/pose preview, deformation/
        compatibility/rights gates, and publishers. Evidence: rig row.
  - [ ] I0.8.k Define Gaussian-splat kind coordinate/scale/SH/point-count/
        cameras/compression/format controls, spatial preview/metadata,
        quality/privacy/rights/interchange gates, and publishers. Evidence:
        splat row.
  - [ ] I0.8.l Define composite-scene kind hierarchy/reference/variant/camera/
        light/material/animation/audio/dependency controls, scene/semantic
        preview, cross-asset/rights/provenance/target gates, and publishers.
        Evidence: scene row.
  - [ ] I0.8.m Validate unique IDs/aliases, inheritance, audience exposure,
        contract/preview/gate/publisher completeness, unsupported behavior, and
        no ambiguous kind resolution. Evidence: registry validation tests.
- [ ] I0.9 Define the workflow-class taxonomy and audience allowlist. Evidence:
      raw graph, model hash, LoRA, scheduler, and provider details cannot leak
      to curated payloads.
  - [x] I0.9.a Inventory current workflow templates/graphs/nodes/providers/
        models and classify generation, edit/transform, upscale/enhance,
        animate/video, audio/voice/music, 3D, training, analysis, and composite
        classes. Evidence: workflow class register.
        <!-- isis-workflow-class-register.json: built from CANONICAL committed
                                                                                                                                                                                                                                                                        artifacts (the codebase's OWN classification, not imposed judgment). The
                                                                                                                                                                                                                                                                        committed workflow-taxonomy-registry.json enumerates 66 workflow mappings
                                                                                                                                                                                                                                                                        (registry declares 64 — both recorded, discrepancy surfaced) across 11
                                                                                                                                                                                                                                                                        in-code categories (sacred-art/video/fractal/flagship/audio/avatar/
                                                                                                                                                                                                                                                                        composition/utility/3d/nft/texture), 37 asset families, 5 output profiles,
                                                                                                                                                                                                                                                                        38 control modalities. Canonical typed vocabulary from the generated Prisma
                                                                                                                                                                                                                                                                        enums: WorkflowCategory(12: IMAGE_GENERATION..COMPOSITING..), GenerationType
                                                                                                                                                                                                                                                                        (15), WorkflowEngine(5: COMFYUI/BLENDER/UNREAL/GODOT/CUSTOM), ModelType(7)/
                                                                                                                                                                                                                                                                        ModelFormat(8) = models, OutputFileType(11). Providers: 30 ai-providers +
                                                                                                                                                                                                                                                                        llm provider-registry. workflow-classes lib = 7 modules. The 9 proposed
                                                                                                                                                                                                                                                                        I0.9.a classes are covered by the canonical vocab EXCEPT "training"
                                                                                                                                                                                                                                                                        (honestly flagged as a real gap — no training workflow category/GenerationType
                                                                                                                                                                                                                                                                        exists). generate-isis-workflow-class-register.mjs + 6 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. The class SCHEMA (ID/version/capability/cost/lifecycle/
                                                                                                                                                                                                                                                                        owner, I0.9.b) and audience allow/deny (I0.9.c) DEFINE new structure and
                                                                                                                                                                                                                                                                        stay unchecked. -->
  - [ ] I0.9.b Define class ID/version, supported asset kinds/operations,
        required inputs/outputs, parameter schema, capabilities, cost/resource
        class, lifecycle, and owner. Evidence: class schema.
  - [ ] I0.9.c Assign operator, AAA, curated, and contemplative allow/deny/
        request/inspect/edit/execute/replay exposure per class. Evidence:
        audience allowlist.
  - [ ] I0.9.d Define operator payload with authorized raw graph/node/model/
        adapter/LoRA/scheduler/provider/resource/debug detail. Evidence:
        operator contract tests.
  - [ ] I0.9.e Define AAA payload with approved advanced workflow/model/training
        detail but no fleet credentials, private provider internals, or operator
        emergency authority. Evidence: AAA contract tests.
  - [ ] I0.9.f Define curated/contemplative payload as task-specific high-level
        intent/constraints/reference/quality/budget/delivery choices and stable
        public labels. Evidence: curated contract tests.
  - [ ] I0.9.g Redact raw graph, node IDs/parameters, model/checkpoint hashes,
        LoRA/adapter identity, scheduler/sampler internals, provider/endpoint,
        secrets, pricing, and fleet topology from lower tiers. Evidence:
        property/bundle leak tests.
        <!-- THE ESTATE HAS A LEAK DETECTOR AND IT IS POINTED AT THE REQUEST.
        I0.9.d–g STAY `[ ]`.

        evidence/v1-workbenches/isis-curated-payload-leak.json —
        generate-isis-curated-payload-leak.mts + .test.mts (8 tests, 3 ratchets,
        2 controls), wired into inventory + verify. The nine vectors are parsed
        out of THIS cell's own redaction list.

        THE DETECTOR IS DOCUMENTED OUTBOUND AND APPLIED INBOUND.
        `auditRawGraphLeak`'s own comment says it "Scans an outbound payload".
        It has ONE call site outside its module, at
        `curated-card-route.ts:150`, and it is handed
        `JSON.stringify(body.inputs)` — the REQUEST. So it stops a curated
        caller SMUGGLING a raw graph IN, which is a real and useful check, and
        it is not the check this cell asks for. NOTHING AUDITS WHAT GOES OUT.

        AND WHAT GOES OUT CARRIES A PROVIDER KEY. `GET
        /v1/isis/curated-cards/catalog` returns `{spec, provenance}` per card to
        any admitted caller, and `ProvenancePreview` declares `providerKey`,
        `providerRegion` and `costClass` — PROVIDER, ENDPOINT REGION AND
        PRICING, three of the nine vectors this cell asks to be redacted from
        lower tiers. Executed: the real catalog produced eight cards, each with
        a provenance object of `[costClass, providerKey, providerRegion,
        workflowClassId, workflowClassVersion]`, and the detector run over that
        exact payload returned `leakDetected=false`.

        THE VALUES SHIPPED TODAY ARE THE LITERAL "pending" and the catalog's own
        comment says the runtime fills them in when it resolves the tenant's
        provider. THE LEAK IS THEREFORE A PROPERTY OF THE RESPONSE TYPE, not of
        the current fixture — which is exactly what a contract-level cell asks
        about.

        THE DETECTOR COULD NOT HAVE OBJECTED ANYWAY. Handed one payload per
        vector it caught five — a raw ComfyUI graph, numeric node ids, a 64-hex
        model hash, a `lora-…` id and a scheduler name — and MISSED the
        provider, the secrets, the pricing and the fleet topology. Its patterns
        were written for the four kinds of thing a client might smuggle in, and
        the two vectors the response actually carries are not among them. That
        it fires at all is what makes its silence on the catalog meaningful
        rather than inconclusive.

        A CONTROL THE CLASSIFIER NEEDED: word-anchoring the direction vocabulary
        classified a call site handed `responsePayload` as `unknown`, because
        `\bresponse\b` does not match a camelCase compound. A response argument
        is now recognised, and a test pins it. -->
        <!-- I0.9.d–f (the operator / AAA / curated payload definitions) are
        answered from the same measurement: the curated payload is `CardSpec` +
        `ProvenancePreview`, and its fields are listed in the artifact. -->
  - [ ] I0.9.h Resolve workflow/tier at authoritative BFF/service boundary and
        reject direct IDs or parameter injection outside allowlist. Evidence:
        bypass negative tests.
        <!-- PARTIAL, READ-ONLY 2026-08-16 (I0.9.h). NO INSTRUMENT — this note
        records only what was read, and the cell stays unchecked because the
        executed negatives its Evidence line asks for were NOT built. THE
        ALLOWLIST THE STYLE PRESET IS CHECKED AGAINST ARRIVES IN THE SAME
        REQUEST AS THE STYLE PRESET. `libs/isis/curated-cards/src/card-validators.ts:118`
        asks `if (!input.supportedStylePresets.includes(i.stylePreset))`, and
        `apps/oshun/bff/src/isis/curated-card-route.ts:159` fills that array from
        `body.supportedStylePresets ?? []`. Lines 121/146 and 160 are the same shape for
        `tenantSupportedLocales`. So the second bypass class the cell names — parameter
        injection OUTSIDE AN ALLOWLIST — is checked against a caller-supplied allowlist,
        which constrains nobody. The first class is in better shape: the route resolves
        the workflow class from `CURATED_CARD_CATALOG` by `cardId` and never reads a
        `body.workflowClassId`, and the creator tier and tenant come from the
        authenticated principal (route header comment, lines 11–15). NEXT SESSION: the
        executed negative is cheap — `validateInputs` is pure — but it needs a real
        `CuratedCardInputs` for a catalogued kind; a synthetic `{kind, brief,
        stylePreset, locale}` made `validateInputs` return undefined for every
        catalogued kind. THAT IS NOT A SECOND FINDING — checked: `validateInputs`
        switches over exactly the eight kinds the catalog ships (`illustration`,
        `narration`, `ambient-audio`, `explainer`, `caption-dub`, `accessibility-pass`,
        `video`, `video-from-image`), so it is exhaustive and the `undefined` came from
        the harness, not the product. The real cause is the entry shape:
        `listCuratedCards()` returns catalogue entries DIRECTLY (no `.card` wrapper) and
        card ids are not always `curated-<kind>`. Build the inputs from the entry's own
        `binding` and read `entry.kind` off the entry. -->
  - [ ] I0.9.i Define unsupported/not-configured/degraded/version-skew and class
        migration behavior per audience. Evidence: capability state matrix.
- [ ] I0.10 Threat-model brief injection, workflow injection, malicious graphs,
      model supply chain, unsafe media, face/voice impersonation, child safety,
      biometric data, training rights, and cost abuse. Evidence: mitigations map
      to test IDs.
  - [ ] I0.10.a Threat-model brief/prompt/reference/negative-prompt/metadata
        injection across audience tiers and downstream provider/tools. Evidence:
        brief attack tree and tests.
  - [ ] I0.10.b Threat-model workflow/template/node/parameter/import/version
        injection and unauthorized class/tier escalation. Evidence: workflow
        attack tree and tests.
  - [ ] I0.10.c Threat-model malicious graph cycles, resource bombs, unsafe
        nodes/scripts/custom code, filesystem/network access, deserialization,
        dependency escape, and hidden outputs. Evidence: graph tests.
  - [ ] I0.10.d Threat-model model/checkpoint/adapter/LoRA/plugin/container/
        dependency origin, license, signature/hash, malicious serialization/
        code, poisoning, backdoor, update, and compromise. Evidence:
        supply-chain tests.
  - [ ] I0.10.e Threat-model uploaded/fetched/generated image/video/audio/3D/
        archive malformed, active, oversized, steganographic, illegal, or parser
        exploit content. Evidence: media threat tests.
  - [ ] I0.10.f Threat-model face and voice cloning/reenactment/dubbing/
        impersonation, identity theft, fraud, consent scope, public figure,
        disclosure, and revocation. Evidence: impersonation tests.
  - [ ] I0.10.g Threat-model child sexual abuse/exploitation, age ambiguity,
        grooming/sexualization, identity/biometric processing, reporting,
        quarantine, and mandatory escalation. Evidence: child-safety controls.
  - [ ] I0.10.h Threat-model face/voice/body/motion embeddings/templates,
        inference, cross-context linking, retention, access, provider transfer,
        breach, and deletion. Evidence: biometric tests.
  - [ ] I0.10.i Threat-model dataset/reference/output/model training rights,
        license/consent, attribution, opt-out/revocation, laundering, derivative
        use, and territorial limits. Evidence: rights tests.
  - [ ] I0.10.j Threat-model token/GPU/storage/provider/egress/retry/batch/
        training/render cost exhaustion, quota bypass, cryptomining/denial,
        invoice fraud, and kill-switch failure. Evidence: cost abuse tests.
  - [ ] I0.10.k Map
        asset/threat/actor/entry/control/detection/response/recovery/ residual
        risk/owner to exact automated/manual test and stop-the-line policy.
        Evidence: zero unmapped high-risk scenarios.
        <!-- MEASURED 2026-08-16 (I0.10.a–k). THERE IS NO THREAT-TO-TEST MAP, SO
        "ZERO UNMAPPED HIGH-RISK SCENARIOS" IS UNEVALUABLE — EVERY SCENARIO IS
        UNMAPPED BY CONSTRUCTION. `evidence/v1-workbenches/isis-threat-mitigation-map.json`
        (writer + 8 tests, wired into `verify:inventory:v1-workbenches`) parses the ten
        threats out of I0.10's own text and asks three SEPARATELY-FAILING questions of
        each: is there a control ON THE ISIS PATH, is there a TEST naming it, and is the
        threat MAPPED TO A TEST ID. `threatMapArtifacts: 0` — no artifact anywhere in the
        repo maps a threat to a test id, so the third answer is `false` for all ten
        regardless of the first two. 9 of 10 are controlled on the Isis path; `training
        rights` is CONTROLLED ONLY IN ANOTHER DOMAIN (32 modules implement it, none of
        them on the Isis generation path) and `brief injection` is CONTROLLED AND NOT
        TESTED (4 on-path modules, 0 tests naming them). The remaining eight are
        `controlled-and-tested-not-mapped`: the control and the test both exist and
        nothing ties either to a scenario id, so the parent's bar ("mitigations map to
        test IDs") is unmet even where the mitigation is real. THE CONTROLS THAT EXIST DO
        REFUSE, EXECUTED, so the gap is the map and not the machinery: a ComfyUI-shaped
        graph inside a curated request is caught by `auditRawGraphLeak` (2 tokens); a
        below-tier caller and a kill-switched card are both denied by
        `checkCardEntitlement`; and an ordinary brief passes unflagged, so the refusals
        are not noise. TRAP, and the reason every vocabulary here is word-anchored:
        unanchored, `csam` matches `dedTemporalSemanticSample` and credited the
        gaussian-splatting index as a child-safety control, inflating that threat from 3
        modules to 11. A control test pins the collision. Cells left unchecked: .a–.j
        each need their own enumeration of entry points and residual-risk owners, which
        this instrument does not attempt, and .k needs a map that does not exist. -->
- [ ] I0.11 Define data classification and retention for prompts, references,
      biometric embeddings, model artifacts, outputs, logs, and provenance.
      Evidence: privacy/security approval.
  - [ ] I0.11.a Classify prompts/briefs/templates/negative prompts/system
        policy/ tool arguments by content, tenant, source identity, secrecy, and
        purpose. Evidence: prompt data rows.
  - [ ] I0.11.b Classify uploaded/fetched/link/reference media, metadata,
        protected identities, rights/consent, source, and derived analysis.
        Evidence: reference data rows.
  - [ ] I0.11.c Classify face/voice/body/motion embeddings/templates/features/
        similarity scores and prohibit or constrain persistence by purpose/
        consent. Evidence: biometric rows.
  - [ ] I0.11.d Classify model/checkpoint/adapter/LoRA/training dataset/
        optimizer/artifact/config/evaluation/license/security metadata.
        Evidence: model artifact rows.
  - [ ] I0.11.e Classify generated/intermediate/partial/quarantined/rejected/
        approved/published/recalled outputs, derivatives, hashes, and metadata.
        Evidence: output rows.
  - [ ] I0.11.f Classify provider/worker/API/security/audit/debug logs/traces/
        metrics/profiles/support exports and prohibit sensitive payloads by
        default. Evidence: telemetry rows.
  - [ ] I0.11.g Classify provenance manifests/ledger/ingredient/action/actor/
        signature/trust and protected/redacted identity fields. Evidence:
        provenance rows.
  - [ ] I0.11.h For each class define collection/minimization, legal basis/
        consent, purpose, encryption, region/provider, access, sharing, training
        use, retention, hold, deletion, backup, incident, and subject rights.
        Evidence: complete lifecycle matrix.
  - [ ] I0.11.i Obtain named privacy/security/safety/rights/domain approval and
        test enforcement against stores/providers/logs/exports/deletion.
        Evidence: approvals and data lifecycle tests.
        <!-- MEASURED 2026-08-17 (I0.11.a–i). THE ESTATE HAS THE REGISTER THIS
        CELL ASKS FOR AND IT IS GOOD. `OshunArtifactType` in `@oshun/contracts`
        is a 28-value alphabet, and every value carries an export scope
        (`OSHUN_ARTIFACT_SCOPES`: exportable, formats, deletable, deletion mode,
        retention-after-deletion, customer visibility) plus a retention rule
        (`OSHUN_RETENTION_RULES`: default window, legal-hold extension,
        retention driver, purge cadence, policy slug). So the question is never
        whether a row SHAPE exists; it is WHICH ROW, and the answers are three.
        `evidence/v1-workbenches/isis-data-classification-matrix.json` (writer +
        17 tests, wired into `verify:inventory:v1-workbenches`) parses the seven
        classes out of .a–.g's own text — each states its bar as a ROW — and asks
        four separately-failing questions of each.

        FIRST, FOUR OF THE SEVEN CLASSES ARE NAMED ONLY IN PART AND ONE NOT AT
        ALL. `isKnownArtifactType` returns false, EXECUTED, for `prompt`,
        `negative_prompt`, `reference_media` and `biometric_template`. So
        `GenerationJob.prompt` and `negativePrompt` — `@db.Text` columns holding
        the most sensitive free text Isis accepts — have no register row at all,
        and .b (reference media) has none of any kind: it persists as a bare
        `GenerationJob.inputUrl` and inside `Provenance.inputs`.
        `workflow_template` is a row for templates and NOT for a prompt;
        `voice_profile` is a row for a voice and NOT for a face embedding. A
        NEAR-MISS THAT FILLS A COLUMN IS WORSE THAN AN EMPTY ONE, so each row
        lists `termsWithNoRegisterRow` rather than counting itself covered.

        SECOND, THE ISIS PATH REACHES THE REGISTER AND BINDS THE WRONG ROW. 11
        of 68 Isis BFF modules bind an `OSHUN_RETENTION_RULES` rule and the
        histogram is `{review_package: 9, session: 1, billing_receipt: 1}` — NOT
        ONE binds `generated_artifact`, the row written for Isis output. Measured
        against the register itself: `review_package` is 2555 days on a
        `compliance_window` driver purging every 30, `generated_artifact` is 365
        on `creation_timestamp` purging every 7. A DIFFERENCE OF 2190 DAYS, so
        cost tracking and texture quality are held seven years under a
        trust-and-safety audit rationale written for appeals. A further 19
        modules declare a self-described disposition instead
        (`not_applicable` ×10, `request_only` ×4, `request_lifetime` ×2, `none`,
        `inherits_output_gallery`), which is honest where nothing durable is
        held and is not a classification.

        THIRD, THE CLASSIFICATION ITSELF IS ABSENT. The Isis Prisma schema
        declares NO classification, sensitivity, legal-basis or subject-rights
        column of any kind, and zero modules on the Isis path name a
        data-classification vocabulary. The positive control is NAMED rather
        than counted — a repo-wide count inside a `--check`ed artifact goes red
        on somebody else's merge — so the artifact carries six alphabets the
        estate declares elsewhere (Iris, Lilith, Lakshmi, Maat, Aphrodite,
        Yemaya) and every one is a ladder of sensitivity VALUES:
        `public|internal|confidential|restricted|top_secret`,
        `PERSONAL|SENSITIVE_PERSONAL|PSEUDONYMIZED|ANONYMIZED`. A SENSITIVITY
        LEVEL IS AN ANSWER AND THIS CELL ASKS FOR THE QUESTION — none of the six
        can name a prompt, a reference or a provenance manifest, because they are
        columns looking for rows.

        LIVE DEFECT, AND IT IS NOT A POLICY QUESTION: THE SUBJECT-ERASURE PATH
        DELETES FROM A TABLE NOTHING PRODUCES. `server.ts` composes the
        account-deletion fan-out's `generated_artifact` partition from
        `resolveGeneratedArtifactDeleter`, which reaches `@isis/database` and
        runs `generatedOutput.findMany`/`deleteMany` against `generated_outputs`.
        Of the 24 models the Isis schema declares, 6 have a producer among the
        Isis Prisma client's 25 consumers and ALL SIX are the `pipeline_state_*`
        family; `GeneratedOutput` is produced by NOTHING and touched only by
        `libs/oshun/privacy` — the export bundler reads it, the eraser deletes
        from it. Meanwhile the DEPLOYED `apps/isis/output-registry`
        (`infra/terraform/ecs-services/isis.tf`, built in CI) issues `CREATE
        TABLE IF NOT EXISTS outputs`, inserts into `outputs`, and every one of
        its six retention-sweep statements writes `outputs` — on the same
        `ISIS_DATABASE_URL ?? DATABASE_URL`. The eraser and the deployed service
        share ZERO tables. `if (outputs.length === 0) return 0` makes a zero-row
        erasure indistinguishable from a subject who owned nothing, so the
        fan-out signs SUCCESS and purges no bytes. THE ESTATE CONTAINS ITS OWN
        ANSWER AND THE ERASURE PATH DID NOT READ IT:
        `apps/oshun/bff/src/isis/output-lineage-store.ts` already states "No
        production generation path writes those normalized tables" and declares
        `databaseSchema: 'declared_not_producer_bound'`. NOT REPAIRED HERE ON
        PURPOSE — the fix is either repointing a GDPR deletion path or
        re-plumbing a deployed service's storage, both decisions with blast
        radius across a signed-attestation contract, and picking one silently is
        worse than reporting it. A ratchet test pins
        `tablesTheEraserAndTheServiceShare: []` so the divergence cannot close by
        accident or widen unnoticed.

        THE MACHINERY IS NOT THE PROBLEM AND WAS EXECUTED TO PROVE IT. The
        register resolves a real scope (`generated_artifact` → exportable,
        deletable, `soft_delete_then_purge`, 30 days after deletion; 22 of 28
        exportable, 17 deletable). The Isis-owned 3D evaluator returns a real
        `deny` with two obligations for a customer upload past its window, and
        REFUSES an `artifactKind` of `"prompt"` outright — "3D model retention
        policy does not define a rule for \"prompt\"" — so the alphabet
        genuinely cannot express the class. And `@oshun/audit-platform`'s
        retention engine (repaired under S10.7) tiers a tagged event to
        `past_retention` correctly while returning the IDENTICAL untagged
        `domain: "isis"` event, whose `newState` carries a prompt, as `tier:
        hot, policyId: null` at ageDays 1461 — four years old and still in the
        hot tier, because nothing on the Isis path writes a `retentionTag`.

        (.h) THEREFORE CANNOT BE STARTED FOR ONE CLASS AND IS ONE-THIRD BUILT
        FOR THE REST: of 98 lifecycle cells, 30 are defined (retention, hold,
        deletion, sharing, subject rights — each naming the register field that
        carries it), 14 are unstartable because .b has no row at all, and 54 name
        an attribute the register has no field for (collection/minimization,
        legal basis/consent, purpose, encryption, region/provider, access,
        training use, backup, incident). "Complete lifecycle matrix" is unmet.

        TRAPS THAT COST TWO FALSE RESULTS HERE. (1) `git grep` without `-E`
        does not honour `\b`, and that reported the wired
        `createGeneratedArtifactSubjectDeleter` as having no caller when
        `server.ts` composes it — a false absence in the direction of a
        finding, corrected before it was written down. (2) Expanding the Prisma
        consumer set by BARE BASENAME put `index` and `types` in the pattern,
        `from './index'` matched 7,077 files across every domain, and `AuditLog`
        was credited with a producer in Iris; the second hop is now
        path-qualified to the importer's own directory and a control test caps
        the set. (3) `exports` as `\bexport(s|ed)?\b` matched the TypeScript
        keyword in every module and manufactured eight enforcement tests for a
        point with none — each point token now names the SUBJECT of the point.

        CELLS LEFT UNCHECKED, ALL NINE. .a–.g each need rows this instrument
        measures as absent or partial, and authoring them means choosing
        retention windows and legal bases — policy, not code. .h needs the 68
        undefined cells. .i's approval half is a NAMED HUMAN SIGN-OFF and is not
        locally actionable; its enforcement half has tests at 2 of the 5 points
        it names (`logs`, `deletion`) and none at `stores`, `providers` or
        `exports`. -->
- [ ] I0.12 Define the operator emergency controls for provider disable,
      workflow revoke, model quarantine, output recall, and spend kill switch.
      Evidence: authority and blast-radius decisions.
  - [ ] I0.12.a Define provider disable scope by provider/endpoint/operation/
        model/class/region/tenant/global, trigger, in-flight/queued behavior,
        fallback prohibition/allowance, recovery, and authority. Evidence:
        provider control decision.
  - [ ] I0.12.b Define workflow revoke scope by template/version/class/audience/
        host, future dispatch, active jobs, outputs/releases, cache,
        replacement, and restoration. Evidence: workflow control decision.
  - [ ] I0.12.c Define model quarantine scope by
        model/version/checkpoint/adapter/ LoRA,
        intake/evaluation/dispatch/training/outputs/derivatives, evidence,
        investigation, revoke/restore. Evidence: quarantine decision.
  - [ ] I0.12.d Define output recall scope by artifact/run/ingredient/model/
        workflow/release/target, dependent derivatives, gallery/catalog/cache,
        notifications/tombstone, provider/target withdrawal, and verification.
        Evidence: recall decision.
  - [ ] I0.12.e Define spend kill scope by actor/project/tenant/provider/model/
        operation/resource/global, reservation/new dispatch/in-flight/retry/
        queued behavior, essential exceptions, and recovery. Evidence: spend
        control decision.
  - [ ] I0.12.f Define request/approve/activate/revoke/expire/recover workflow
        with least privilege, step-up, separation/quorum, emergency break-glass,
        immutable audit, communication, and evidence. Evidence: common control
        contract.
  - [ ] I0.12.g Preview direct/transitive blast radius and make all actions
        idempotent, rapid, fail-closed, observable, and reconcilable. Evidence:
        blast-radius and fault tests.
  - [ ] I0.12.h Rehearse each control and combined incidents, measure
        propagation and recovery, and link stop-the-line/runbooks. Evidence:
        game-day report.
        <!-- MEASURED 2026-08-17 (I0.12.a–h). A CONTROL IS A VERB AND EVERY OTHER
        PART OF SPEECH FAILS DIFFERENTLY. The five controls differ not in how
        much of each is built but in WHAT KIND OF THING each is, so `kind` is the
        first column of `evidence/v1-workbenches/isis-emergency-controls.json`
        (writer + 17 tests, wired into `verify:inventory:v1-workbenches`) and it
        is derived, never asserted: `{provider disable: absent, workflow revoke:
        absent, model quarantine: absent, output recall: operator-command, spend
        kill switch: frozen-literal}`. Each of .a–.e has the shape "Define
        <control> scope by <slash-list>, <clause>, …", so the cell hands over its
        own matrix — 33 scope dimensions and 23 further aspects, all parsed from
        the checklist.

        EXACTLY ONE OF THE FIVE IS AN OPERATOR COMMAND, AND IT IS REAL. `POST
        …/bulk-actions` in `admin-isis-output-gallery-actions.ts` is admin-scoped
        (`hasAdminScope`), takes a `reason`, is idempotent on `invocationId`, and
        has an undo endpoint. Its scope is an explicit `outputIds` LIST, not a
        query, so of the seven dimensions .d names it can express ARTIFACT and
        nothing else: `run`, `ingredient`, `release` and `target` are absent, and
        `model`/`workflow` are `filterable-only` — A FIELD THE RECORD CARRIES IS
        NOT A SCOPE THE ACTION ACCEPTS, and crediting `OutputRecord.model` would
        have credited the caller's own filtering to the control. Executed: a
        takedown sets `takenDownAtUnixSeconds` on the listed id and leaves the
        bystander `null`, and `OutputRecord` carries NO lineage field at all — so
        "dependent derivatives" is not unimplemented, it is UNEXPRESSIBLE ON THE
        SHAPE. The estate agrees with itself: `output-lineage-store.ts` declares
        `takedownCascade: 'unavailable'`. Only 2 of 33 dimensions across all five
        controls are expressible.

        THE SPEND KILL SWITCH IS TWO THINGS AND NEITHER IS A SWITCH. Executed:
        `evaluateBudget` at 97% projected returns `status: "kill-switch-armed",
        killSwitchArmed: true` and ZERO fields that could block, cancel or stop
        anything — the word "armed" is carrying the whole claim, and its only Isis
        consumer is `RunpodDashboardPanel.tsx`, which renders it. The curated-card
        `killSwitchActive` is worse: all EIGHT catalog cards carry `false`, every
        `spec` is `Object.freeze`d, and assigning `true` raises "Cannot assign to
        read only property 'killSwitchActive'" while the value stays `false`. A
        SWITCH THAT ONLY A DEPLOY CAN THROW IS NOT AN EMERGENCY CONTROL. The
        enforcement point is fine and was checked as a control: the gate returns
        `deny / "kill-switch active"` when the flag IS set, so the gap is the
        switch and not the gate.

        PROVIDER DISABLE IS AN AUTOMATIC BREAKER, WHICH IS A DIFFERENT QUESTION.
        `unified-generation-pipeline.ts` keeps a PRIVATE, in-process
        `Map<string, {open, failures}>` that trips on consecutive failures — no
        operator trigger, no authority, no route, and per-instance so it does not
        propagate to another worker. AN AUTOMATIC BREAKER ANSWERS "IS IT FAILING"
        AND AN EMERGENCY CONTROL ANSWERS "STOP USING IT": a provider that is
        healthy and unsafe passes every breaker. It is recorded as an
        `automaticSurrogate` and kept OUT of `kind`, so the row can be neither
        misread as a control nor as an empty domain. WORKFLOW REVOKE AND MODEL
        QUARANTINE DO NOT EXIST under any verb — the only `quarantine` on the
        path is 3D delivery OUTPUT quarantine, a different subject, and I1.2.d
        separately measured six of ten model-version stages, quarantine among
        them, as having no command. Their 18 dimensions are reported `no-control`
        rather than `absent`, because a zero invites the reading that somebody
        looked at the dimension.

        (.f) THE COMMON CONTRACT HAS NO TEETH ON ANY CONTROL. Of its seven
        properties, 2 land on a module that implements one of the five (`least
        privilege` via `hasAdminScope`, `evidence` via marketplace abuse
        reporting); `emergency break-glass` and `communication` are absent from
        the entire Isis surface; and `step-up`, `separation/quorum` and `immutable
        audit` match REAL machinery aimed at a different subject — CLI login
        reauthentication, a release gate, a provenance bundle. Every match is
        therefore partitioned by whether it lands on a control, because a
        property of the domain is not a property of the control. The closest
        common contract is 3D-model-governance exception approval
        (requested/approved/renewed/revoked/expired/evaluated): it covers four of
        .f's six verbs, misses `activate` and `recover`, and binds none of the
        five. (.g) A REVERSE DEPENDENCY INDEX EXISTS AND NO CONTROL CONSULTS IT —
        I5.13 shipped `evidence/v1-workbenches/blast-radius.json`, and zero
        control modules reference it; of .g's five action properties, 3 land on a
        control (`idempotent`, `fail-closed`, `reconcilable`), `observable` is
        real elsewhere and on no control, and `rapid` (a propagation bound) is
        absent from the whole surface. (.h) NO GAME-DAY ARTIFACT REHEARSES ANY OF
        THE FIVE, and real game-day artifacts do exist, so the zero is about the
        controls.

        THE HONEST SEAMS ARE RECORDED RATHER THAN COUNTED AGAINST THE ESTATE.
        `budget-management-store.ts` declares `killSwitchEnforcement:
        'unavailable'`, `mutations: 'unavailable'`, `authorityBinding:
        'not_configured'` and says returning evaluated samples "would create a
        second, fabricated authority" — a refusal to invent an authority is the
        right behaviour for a control that does not exist, and it is why these
        rows are `absent`/`frozen-literal` and not stubs.

        FOUR MATCHERS MANUFACTURED A POSITIVE BEFORE CORRECTION, EACH PINNED BY A
        CONTROL TEST. (1) Matching the NOUN instead of the verb made
        `apps/isis/generation-api/src/routes/jobs.ts` — a breaker consumer — a
        provider-disable route; worse, the route filter `/isis/` matched because
        THE DOMAIN NAME IS IN THE PATH, so the test is now the basename
        `admin-isis-*.ts`. (2) `killSwitchActive:` matched an interface member and
        a frozen object literal, reporting a compile-time constant as a mutation
        site; state writes are now measured at LINE level and declaration lines
        (`const`/`let`/`readonly`) are dropped, which also stopped `const
        killSwitchArmed = …` reporting the pure evaluator as mutating what it
        evaluates. (3) `recall` is never the vocabulary here — on the Isis path it
        is the ML metric (`rougeLRecall`, `minRecall`, `recallPass`,
        `totalRecall`) and it credited an evaluation suite and a grounding gate as
        recall controls; the estate's verb is `takedown`. (4) A game-day artifact
        is one whose PATH names a game day: filtering on files that merely MENTION
        one put `docs/systems/lib-v6.md` and a workbench export map in the set,
        and a phrase match inside them then read as a rehearsal.

        CELLS LEFT UNCHECKED, ALL EIGHT. .a, .b and .c need controls that do not
        exist; .d and .e need scopes, cascades and an authority the shapes cannot
        express; .f needs a contract that binds the controls; .g needs a preview
        that consults the index that already exists; .h needs a rehearsal of
        controls three of which cannot be rehearsed. Every cell's bar is a
        DECISION about authority and blast radius, which is a human call, so
        nothing here was authored on the instrument's behalf. -->
- [ ] I0.13 Author and ratify
      `V1/ISIS_GENERATION_WORKBENCHES_DESIGN_2026-07-23.md` with current-state,
      audience, host, data, API, job, UI, trust, migration, rollout, and
      decision records. Evidence: approved doc and resolved review comments.
  - [ ] I0.13.a Document code-derived current applications/routes/components/
        contracts/packages/entities/jobs/providers/tests, live/fixture/dead
        status, and reusable/gap disposition. Evidence: current-state section.
  - [ ] I0.13.b Document operator/AAA/curated/contemplative actors, roles,
        capabilities, forbidden detail/actions, approvals, and boundary matrix.
        Evidence: audience section.
  - [ ] I0.13.c Document canonical hosts/routes/navigation/deep links/embeds/
        handoffs/proxies/redirects and migration from noncanonical surfaces.
        Evidence: host section.
  - [ ] I0.13.d Document
        asset/workflow/model/brief/plan/run/output/lineage/gate/
        rights/consent/provenance/release data contracts, stores, retention, and
        migrations. Evidence: data section.
  - [ ] I0.13.e Document APIs/events/jobs/providers/bundles/publishers,
        authentication/authorization, idempotency/concurrency, limits, errors,
        health, and compatibility. Evidence: service sections.
  - [ ] I0.13.f Document queue/worker/resource/cost/cancel/retry/checkpoint/
        fault/recovery/emergency operations. Evidence: job/operations section.
  - [ ] I0.13.g Document restrained operator/AAA/curated/gallery workspace
        hierarchy, responsive behavior, states, accessibility/nonvisual media/
        graph/3D alternatives, localization, and utility copy. Evidence: UI
        section.
  - [ ] I0.13.h Document threat model, data classification, safety/child/
        impersonation, rights/consent, provenance/trust, gates/overrides,
        incident/recall, and evidence. Evidence: trust section.
  - [ ] I0.13.i Document incremental reuse/refactor/data/route/consumer
        migration, compatibility, rollback, deprecation/deletion,
        environment/standards, verification, rollout/cohort, SLO, support, and
        DR. Evidence: migration/ rollout sections.
  - [ ] I0.13.j Record every
        product/architecture/security/privacy/safety/rights/
        host/ownership/standard decision with options/evidence/rationale/
        consequences/approver/review. Evidence: decision appendix.
  - [ ] I0.13.k Run source link/schema/route/inventory/requirement consistency
        checks, obtain named reviews, resolve every comment, and ratify exact
        document revision. Evidence: approval record and closed comment ledger.
        <!-- AUTHORED 2026-08-17 (I0.13.a–k). THE DOCUMENT NOW EXISTS:
        `V1/ISIS_GENERATION_WORKBENCHES_DESIGN_2026-07-23.md`, 343 lines, eleven
        sections, GENERATED by `scripts/v1-workbenches/generate-isis-design-doc.mts`
        (+ 13 tests) and `--check`ed byte-for-byte as the LAST step of
        `verify:inventory:v1-workbenches`.

        IT IS GENERATED BECAUSE .a FIXES THE STANDARD FOR ALL ELEVEN SECTIONS:
        the current state must be CODE-DERIVED. So every number and every list in
        it is read out of a committed evidence artifact at build time — **133
        measured claims from 19 artifacts**, each read by a dotted path through a
        `fact()` accessor that THROWS when the path is absent rather than
        interpolating `undefined` into a design document, and each recorded so the
        document carries its own source ledger (§13). Every repo path it cites is
        checked to exist. A DESIGN DOCUMENT ROTS AND A MEASUREMENT DOES NOT: a
        route that appears, a package that is retired or a gate that starts
        passing now makes this document stale LOUDLY. A ratchet test also refuses
        any interpolated `undefined`, `NaN`, `[object Object]` or bare `null` —
        the first run rendered `the registry module is null`, which reads as a
        fact and is not one.

        WHAT THE DOCUMENT ESTABLISHES THAT WAS NOT WRITTEN DOWN ANYWHERE. §12 is
        a decision register derived from the checklist itself: every Phase I cell
        whose own bar names a decision, an approval or a ratification —
        **29 cells, all open by construction** since each is unchecked. It is
        split three ways by a PUBLISHED rule, because "decision appendix",
        "decision tests" and "threshold decision record" all contain the word
        *decision* and none of them is a decision anybody has to take: **14 are
        waiting on a named person** (including all five I0.12 control decisions
        and I0.11), 10 on a person AND on engineering work, and 5 are engineering
        work about decisions. Mixing them would make the register useless for the
        one thing it is for. `approver` and `review` are EMPTY in every row and
        the document says why — recording an approver who does not exist is the
        exact failure this lane was built to catch.

        TWO FINDINGS CAME OUT OF WRITING IT, BOTH CORROBORATING I0.12 FROM
        ARTIFACTS THAT WERE NOT LOOKING FOR IT. (1) The endpoint DISPATCH MODEL
        (I1.3.b–e) names an eight-state lifecycle including `disabled`, and NO
        ENUM IN SOURCE IMPLEMENTS IT — the only endpoint states the code has are
        the four health states `healthy|degraded|unhealthy|unknown`. A PROPOSED
        LIFECYCLE IS NOT A CONTROL, and a reader who took `disabled` from the
        model would conclude an operator can disable a provider. I checked this
        BEFORE trusting the I0.12 verdict, and the verdict survived. (2) The
        workflow job machine's unimplemented transitions are
        `submit|review|approve|REVOKE` — measured independently of I0.12.b, which
        found workflow revoke absent under any verb.

        §13.1 IS THE REASON EVERY CELL STAYS UNCHECKED, and it is a table rather
        than a paragraph: per child, which sections answer it and WHAT THE CHILD
        ASKS FOR THAT THE DOCUMENT DOES NOT CONTAIN. That column is a stated
        judgment, published so it can be argued with, because a generated
        document could claim completeness by matching words and A WORD APPEARING
        IN A SECTION IS NOT THE SAME AS THE SECTION DOCUMENTING THE SUBJECT. The
        gaps, in short: .a lacks a component-level inventory below the page; .b
        lacks the forbidden-detail lists and approval matrix (decisions, I0.2.e/g);
        .c lacks deep links, embeds, handoffs, proxies and redirects; .d lacks
        per-record migrations and the retention rules themselves (I0.11); .e lacks
        idempotency, concurrency, limits, error taxonomy, health and compatibility
        per API, and events/bundles/publishers entirely; .f documents only the
        emergency half and not queue/worker/resource/cost/cancel/retry/checkpoint;
        .g lacks workspace hierarchy, responsive behaviour, states, localization
        and utility copy; .h lacks safety/child/impersonation and rights/consent
        detail, gates and overrides, and the incident/recall runbook; .i lacks
        rollback, deprecation, cohorts, SLO, support and DR; .j lacks the approver
        and review; .k lacks named reviews, a resolved comment ledger and a
        ratified revision. A test asserts that no I0.13 cell can be checked while
        its row states a gap.

        CELLS LEFT UNCHECKED, ALL ELEVEN PLUS THE PARENT. The parent's bar is
        "approved doc and resolved review comments" and .k's is an approval
        record — neither is locally actionable. .a–.i each have a stated gap
        above; several of those gaps are themselves decisions listed in §12, so
        closing them is not writing prose. The document reports its own status as
        **unratified** in its first line and quantifies it in §13.2 (Phase I: 757
        of 1582 cells checked, counted between the `## Phase I` and `## Phase M`
        headers rather than by a line range — the range I first used was stale by
        the length of these notes), so a thorough current-state section cannot be
        mistaken for approval of it. -->
- [ ] I0.14 Reconcile the complete `ISIS_TODOS_2.md` asset-family matrix:
      character concept, turnaround, expression, costume, prop-interaction,
      NPC-crowd and creature packs; environment concept, matte-painting,
      tileset, architecture, interior, prop, weapon, vehicle, PBR material,
      decal, terrain, foliage, sky, VFX, UI/HUD, storyboard, animatic,
      cinematic, marketing, social, audio-SFX, voice-line, and music-cue
      outputs. Evidence: every matrix row has an audience, authorized host,
      workflow/template, controls, output bundle, preview, quality profile, and
      release disposition.
      <!-- THE RECONCILIATION IS BUILT AND IT FOUND SOMETHING; THE PARENT AND ITS
      CHILDREN STAY `[ ]`. isis-asset-family-matrix.json —
      generate-isis-asset-family-matrix.mjs + .test.mjs (14 tests, 4 of them
      ratchets on findings), wired into inventory + verify.

      The family list is read from `ISIS_TODOS_2.md` itself, not from a list in
      the generator: a family the matrix names and the estate never built would
      otherwise be invisible, which is the failure mode a reconciliation exists
      to catch. 32 families, 18 MXA items each, 576 of 576 marked `[x]` in that
      document.

      WHAT THE ESTATE ACTUALLY HAS IS UNIFORM AND SUBSTANTIAL. All 32 families
      carry all 8 conventional artifacts. All 32 declare the SAME four workflow
      variants (baseline / image-conditioned / control-conditioned /
      consistency-locked) — one shape, no exceptions. All 32 expose five SDK
      methods (a generic dispatcher plus one per variant), a quality evaluator, a
      cost estimator and a budget guardrail, and a golden regression dataset;
      192 golden cases in total. File existence is a banned proxy, so each of
      those is the artifact's CONTENTS, read from source.

      SEVENTEEN OF THIRTY-TWO FAMILIES ARE GATED ON A FACE AND A WARDROBE.
      Grouping the families by their quality metric set — the one thing a
      per-family read cannot see — gives 14 distinct sets, and the largest is
      shared by 17: `face_embedding_cosine_similarity`,
      `facial_landmark_drift_px`, `garment_attribute_match_ratio`,
      `wardrobe_palette_delta_e`. Seven of those 17 are character families and
      the metrics fit. The other TEN are `architecture-kitbash-pack`,
      `environment-concept-art`, `environment-matte-painting`,
      `environment-tileset-pack`, `interior-scene-pack`, `prop-pack-general`,
      `terrain-heightmap-pack`, `vehicle-exterior-pack`,
      `vehicle-interior-pack` and `weapon-pack`. A TERRAIN HEIGHTMAP IS SCORED
      ON HOW WELL IT MATCHES A WARDROBE PALETTE.

      IT IS NOT INERT AND IT IS NOT ENFORCED, AND BOTH HALVES MATTER. Every one
      of the 17 gates DECLARES those metrics in its own type union, thresholds
      each from its own env key, and its golden regression cases SUPPLY VALUES
      FOR THEM — `terrain-heightmap-pack` ships a case with
      `face_embedding_cosine_similarity: 0.842`. So the metrics are live in the
      regression suite. But all 32 gates read
      `ISIS_<FAMILY>_QUALITY_GATE_ENABLED` defaulting to FALSE, so nothing is
      being wrongly rejected today. The defect is in what the estate would
      measure if it turned the gates on.

      THE ESTATE KNOWS HOW TO WRITE THESE, WHICH IS WHY THIS IS A COPY AND NOT A
      LIMITATION. Twelve families carry metrics written for their subject:
      `material-pbr-pack` checks seam tileability and roughness/metallic
      accuracy, `ui-icon-pack` stroke-weight consistency and contrast
      compliance, `vfx-element-pack` temporal flicker and alpha edge halo,
      `foliage-pack` canopy coverage and species mix, `voice-line-pack` phoneme
      alignment and prosody drift, `music-cue-pack` harmonic drift and cue
      timing. A second shared set is legitimate: storyboard, animatic and
      cinematic genuinely share blocking/camera/composition/style-lock.

      WHY THE PARENT AND ALL CHILDREN STAY OPEN. The evidence clause wants every
      row to carry audience, authorized host, workflow/template, controls,
      output bundle, preview, quality profile AND release disposition. Four of
      those are measured here. Audience and authorized host are the same
      question I0.2 measured as unratified and I0.1.e recorded as owed, and a
      release disposition is a decision. Filling them from this generator's
      opinion would read like a ratified answer nobody gave.

      THREE WAYS THIS INSTRUMENT UNDER-REPORTED BEFORE IT WAS RIGHT, ALL NOW
      UNDER A CONTROL TEST. A PascalCase guess at symbol names reported
      `ui-icon-pack` as having NO SDK method, NO quality evaluator and NO cost
      estimator — the estate writes acronyms in caps, so they are
      `runUIIconPack` and `evaluateUIIconPackQualityGate`; matching is now
      case-insensitive on the de-hyphenated family name. Requiring `(` straight
      after a method name missed every family's GENERIC dispatcher
      (`async runX<TWorkflowId extends …>(`), counting four SDK methods where
      there are five. And reading every `name: <number>` in a golden dataset
      swept in each case's REQUEST parameters — `cfg_scale`, `focal_length_mm`,
      `height` — and reported dozens of quality signals no gate reads; signals
      are now read only from `qualitySignals` blocks, and the true count is
      zero. -->
  - [ ] I0.14.a Reconcile character-concept outputs independently, including
        single/multi-view intent, identity/style references, anatomy/silhouette,
        design variation, audience/host, workflow, controls, bundle, preview,
        quality/rights/provenance gates, and release. Evidence: completed
        character-concept row.
  - [ ] I0.14.b Reconcile character-turnaround outputs independently, including
        canonical views, camera/pose/scale consistency, identity/wardrobe locks,
        sheet layout, downstream modeling, and release proof. Evidence:
        completed turnaround row.
  - [ ] I0.14.c Reconcile expression-sheet outputs independently, including
        expression taxonomy/intensity, identity/anatomy consistency, layout,
        rig/reference handoff, and safety/quality proof. Evidence: completed
        expression row.
  - [ ] I0.14.d Reconcile costume outputs independently, including garment/
        material/accessory variants, identity/body/era/style locks, rights/
        cultural review, turnaround/fit preview, and downstream handoff.
        Evidence: completed costume row.
  - [ ] I0.14.e Reconcile prop-interaction outputs independently, including
        character/prop identity, grasp/contact/occlusion/physics, action poses,
        scale, rig/animation handoff, and quality gates. Evidence: completed
        prop-interaction row.
  - [ ] I0.14.f Reconcile NPC-crowd pack outputs independently, including
        population taxonomy, identity diversity, variation rules, LOD/atlas/
        instancing, bias/safety, engine bundle, and performance gates. Evidence:
        completed NPC-crowd row.
  - [ ] I0.14.g Reconcile creature-pack outputs independently, including
        species/ anatomy/size variants, turnaround/expression/motion/rig needs,
        safety, downstream formats, and quality. Evidence: completed
        creature-pack row.
  - [ ] I0.14.h Reconcile environment-concept outputs independently, including
        biome/location/era/mood, composition/scale/perspective, camera/lighting/
        palette locks, production breakdown, and world-building handoff.
        Evidence: completed environment-concept row.
  - [ ] I0.14.i Reconcile matte-painting outputs independently, including plate/
        camera/perspective/lighting/color/depth, alpha/layer/overscan,
        projection, EXR delivery, and compositing/rights/quality proof.
        Evidence: completed matte-painting row.
  - [ ] I0.14.j Reconcile tileset outputs independently, including grid/tile/
        adjacency/rules, seamless variants, collision/metadata, atlas/engine
        packaging, preview map, and validation. Evidence: completed tileset row.
  - [ ] I0.14.k Reconcile architecture outputs independently, including style/
        period/function, dimensions/units/modularity, interior/exterior,
        material/ code considerations, LOD/collision, DCC/engine handoff, and
        gates. Evidence: completed architecture row.
  - [ ] I0.14.l Reconcile interior outputs independently, including room
        program/ layout/scale, furniture/props/materials/lighting,
        camera/navigation, modular breakdown, DCC/engine bundle, and quality.
        Evidence: completed interior row.
  - [ ] I0.14.m Reconcile standalone prop outputs independently, including
        function/scale/material/wear/variants, turnaround, mesh/texture/LOD/
        collision targets, rights, and engine/DCC release. Evidence: completed
        prop row.
  - [ ] I0.14.n Reconcile weapon outputs independently, including fictional/
        historical context, safety policy, dimensions/mechanism/material/
        variants, hands/animation, mesh/LOD/collision, rights, and audience
        restrictions. Evidence: completed weapon row.
  - [ ] I0.14.o Reconcile vehicle outputs independently, including class/scale/
        design/variants, exterior/interior, wheel/part/rig/animation,
        materials/decals, LOD/collision, and simulation/engine handoff.
        Evidence: completed vehicle row.
  - [ ] I0.14.p Reconcile PBR-material outputs independently, including
        workflow/ shader model, base
        color/normal/roughness/metallic/AO/height/emissive/ opacity channels,
        tiling/scale/color space, preview, and target export. Evidence:
        completed PBR-material row.
  - [ ] I0.14.q Reconcile decal outputs independently, including purpose/
        dimensions/alpha/channel packing/projection/atlas/variants, trademark/
        rights, surface preview, DCC/engine format, and validation. Evidence:
        completed decal row.
  - [ ] I0.14.r Reconcile terrain outputs independently, including extent/units/
        heightfield/erosion/biomes/masks/material
        layers/LOD/collision/streaming, GIS/source rights, preview, and engine
        package. Evidence: completed terrain row.
  - [ ] I0.14.s Reconcile foliage outputs independently, including species/
        growth/season/variation, cards/geometry/material/wind/LOD/collision/
        density, ecological/source rights, and engine bundle. Evidence:
        completed foliage row.
  - [ ] I0.14.t Reconcile sky outputs independently, including time/weather/
        location/HDRI/cloud/sun/stars/color/exposure, environment-map format,
        lighting preview, rights, and engine/DCC handoff. Evidence: completed
        sky row.
  - [ ] I0.14.u Reconcile VFX outputs independently, including effect taxonomy/
        duration/space/timing, sprites/flipbooks/particles/volumes/materials/
        simulation, loop/LOD/performance, preview, and engine package. Evidence:
        completed VFX row.
  - [ ] I0.14.v Reconcile UI/HUD outputs independently, including platform/
        resolution/state/components/icons/text/localization/accessibility/
        interaction, style consistency, atlas/vector/source delivery, and
        rights. Evidence: completed UI/HUD row.
  - [ ] I0.14.w Reconcile storyboard outputs independently, including script/
        shot/scene/panel IDs, composition/action/dialogue/camera/timing/notes,
        continuity, revision, accessibility, and editorial package. Evidence:
        completed storyboard row.
  - [ ] I0.14.x Reconcile animatic outputs independently, including ordered
        panels/shots, timing/camera/transitions/dialogue/music/SFX/temp media/
        captions, EDL/timecode, review, and editorial export. Evidence:
        completed animatic row.
  - [ ] I0.14.y Reconcile cinematic outputs independently, including sequence/
        shots/cameras/animation/lighting/VFX/audio/titles/captions/color/
        delivery, render jobs, editorial/DCC/engine package, and release gates.
        Evidence: completed cinematic row.
  - [ ] I0.14.z Reconcile marketing outputs independently, including campaign/
        channel/audience/territory/format/copy/brand/rights/disclosure/
        accessibility/approval/variant tracking and delivery. Evidence:
        completed marketing row.
  - [ ] I0.14.aa Reconcile social outputs independently, including platform/
        placement/dimensions/duration/safe area/caption/audio/copy/hashtags/
        accessibility/moderation/rights/schedule and derivatives. Evidence:
        completed social row.
  - [ ] I0.14.ab Reconcile audio-SFX outputs independently, including event/
        category/material/action/perspective/variation/duration/sample/channel/
        loop/loudness/metadata/rights and engine package. Evidence: completed
        audio-SFX row.
  - [ ] I0.14.ac Reconcile voice-line outputs independently, including
        character/ speaker/actor
        consent/language/text/pronunciation/emotion/timing/takes/
        processing/loudness/caption/localization and engine package. Evidence:
        completed voice-line row.
  - [ ] I0.14.ad Reconcile music-cue outputs independently, including brief/
        scene/mood/style/key/tempo/meter/duration/structure/stems/loop/hit
        points/ mix/master/rights and engine/editorial package. Evidence:
        completed music-cue row.
  - [ ] I0.14.ae Validate that every asset-family row independently names its
        audience, authorized host, workflow/template, typed controls, output
        contract/bundle, preview/semantic alternative, quality profile, safety/
        rights/provenance gates, downstream handoff, and release/exclusion.
        Evidence: zero incomplete matrix rows.
- [ ] I0.15 Reconcile every consistency and conditioning mode: seed and latent
      reuse; identity, style, wardrobe, prop, scene, lighting, palette, lens,
      camera, pose, depth, temporal, and motion locks; ControlNet pose/depth/
      canny/line-art/segmentation/normal; IP-Adapter style/face; InstantID;
      open-pose sequences; camera paths; motion vectors; masks; reference
      ensembles; and multi-control blends. Evidence: per-mode audience exposure,
      typed contract, diagnostics, gate, and unsupported-state matrix.
      <!-- THE RECONCILIATION IS BUILT AND EVERY ROW CAME BACK INCOMPLETE; THE
      PARENT AND ALL THIRTY-ONE CHILDREN STAY `[ ]`.
      evidence/v1-workbenches/isis-conditioning-mode-matrix.json —
      generate-isis-conditioning-mode-matrix.mjs + .test.mjs (15 tests, 6 of
      them ratchets on findings, 4 controls that prove each scan can come back
      both ways), wired into inventory + verify. The mode list is read from THIS
      document, not from a list in the generator; `.ae` is separated from the
      thirty by its own wording ("Validate", not "Reconcile") rather than by
      being the last letter.

      SIX ALPHABETS SPEAK ONE VOCABULARY AND THEY DO NOT AGREE. The canonical
      dictionary (ISIS_V2_CONTROL_TAXONOMY.json) has 38 control ids; the I2.16
      conditioning reference has 11 typed kinds; the workflow registry has 8
      consistency dimensions; workflow.schema.ts has 7 continuity locks; the
      consistency runtime has 3 domains; and the entitlement resolver — the ONLY
      mechanism in the estate that decides which audience may reach which
      capability — has 28 generation surfaces, NOT ONE of which names a mode.
      Zero exact matches, and the three token overlaps are coincidental
      (`curated-living-scene` shares "scene" with `scene-lock`). So per-mode
      audience exposure is not owed as a decision here the way I0.14's was: it
      is NOT EXPRESSIBLE, and that is a measured negative on all thirty rows.
      Three modes have no canonical control id at all — prop lock, lighting lock
      and multi-control blends — and the first two DO have typed runtime state,
      so what they lack is a dictionary entry, not an implementation.

      THE GRAPHS REFUTE 401 OF THE 736 CONTROL CLAIMS THE REGISTRY MAKES. A
      control names the node families that implement it, and a graph containing
      none of them cannot apply it. 23 of the 37 families the dictionary names —
      CameraControl, PromptComposer, OpenPose, InsightFace, InstantID, Canny,
      ColorPaletteGuard, KeyframeControl, TileControl among them — do not exist
      as a node class in ANY of the 66 workflow graphs. The single workflow
      declaring InstantID (`consistent-character`) has no InstantID, no
      InsightFace and no ControlNet node. All five declaring controlnet-inpaint,
      including `inpaint-edit` and `outpaint-expand`, have no inpaint node at
      all. All four declaring lens-profile-lock lack its only family.

      THE TEST ONLY EVER REFUTES, WHICH IS WHY IT IS TRUSTWORTHY. A family is
      classified GENERIC or DISCRIMINATING by measuring its reach across the 66
      graphs rather than by assertion, so `seed-lock` riding on KSampler is
      reported as backing that proves nothing; a templated `class_type` is
      INDETERMINATE, never absent (8 pairs).

      THE DICTIONARY NAMES 37 REQUIRED INPUTS AND THE WORKFLOWS ACCEPT ONE.
      `seed` is a declared parameter on 63 of 66 workflows; no workflow declares
      a parameter named after any of the other 36. Eleven controls go further —
      latent-reuse, pose-constraint, depth-constraint, palette-lock,
      identity-adapter, reference-ensemble, keyframe-anchor-lock,
      motion-vector-lock and three ControlNet arms — their required input has
      ZERO occurrences in any TypeScript file and zero in any workflow, and
      exists only in the documents that declare it. reference-ensemble is
      declared on 31 of 66 workflows and nothing in the estate can supply it.

      A MODALITY CONTRADICTION NAMES A PAIR, NOT A CULPRIT (76 of them). An
      audio workflow cannot wear a wardrobe, and `sfx-sound-designer`,
      `music-cue-composer`, `voice-chatterbox` and `voice-performance-synthesis`
      all declare wardrobe-lock, camera-lock, pose-constraint and
      depth-constraint. But `song-section` — the one hand-written registry row —
      declares sampler-scheduler-lock, which the dictionary omits `audio` from,
      and there it is the DICTIONARY that is short an entry. A test pins that
      counter-example so the finding cannot harden into "the registry is wrong".

      THE SEVEN TYPED LOCKS THAT DO EXIST ARE PARSED AND NEVER READ.
      workflow.schema.ts declares character_identity_lock, style_lock,
      scene_lock, camera_lock, wardrobe_lock, prop_lock and temporal_lock,
      defaults every one to `unlocked`, and no file under libs/isis or apps/isis
      outside a spec mentions any of them. The one rigorous typed conditioning
      contract in the estate — the I2.16 reference with its declared conventions
      and named refusals — lives under evidence/ with its own tsconfig, is
      reachable by no path alias, and is imported by zero product files. For 15
      of the 30 modes that reference IS the whole typed contract.

      192 CERTIFICATION REPORTS TREAT THE STRATEGY AS A LABEL, NOT A VARIABLE.
      12 consistency strategies x 16 target classes. For 15 of the 16 targets
      all 12 reports are identical in metric names, thresholds, seeds AND
      observed values — only the filename and the caseId prefix differ. 158 of
      them carry a title naming a different strategy ("Seed Lock"). The
      depth-constraint x voice-persona-pack report observes speaker identity,
      phoneme alignment, prosody, timbre and LUFS, and nothing about depth.
      scene-lock is the control that proves the fields are meant to vary: it is
      titled correctly on all 16 and is the one strategy that swaps a metric
      (sceneLayoutIoU for wardrobePaletteDeltaE), so neither 158 nor 15 is an
      artifact of a comparison that always fires.

      AND THE VALIDATOR THAT WOULD HAVE CAUGHT THE DRIFT IS WRITTEN, CORRECT AND
      UNCALLED. `scripts/isis/validate-v2-certification-matrix.mjs` exits 1 with
      905 errors today: the certification matrix was built over 48 workflows and
      the registry now holds 66, leaving 18 — every flagship workflow among them
      — with no certification row at all. Its only mentions in the repo are
      inside two generators that print the command into documentation.

      TWO REPAIRS SHIPPED WITH THIS PASS, BOTH VERIFIED. (1) The registry header
      declared `workflowCount: 64` while heading 66 mappings and no test could
      see it, because the field was read into the document type and never
      exported: every count-shaped assertion in the suite compared the mapping
      count with the mapping count. It is now exported as
      WORKFLOW_TAXONOMY_REGISTRY_DECLARED_WORKFLOW_COUNT, corrected to 66, and
      pinned by an assertion that reads the DECLARED number. (2)
      `libs/isis/workflows/src/index.js` and `index.d.ts` were committed build
      output sitting beside `index.ts`, and Vite resolved `./index` to the .js —
      which is how the new export read back `undefined` and how this was found.
      Measured before claiming: the shadow was a stale EXPORT LIST over live
      implementations, not a stale build. It re-exported from extensionless
      specifiers (`./consistency`, `./utils/registry`) that resolve to the real
      `.ts` sources, so the values the specs imported were current; what it could
      not carry was any symbol added to `index.ts` after the snapshot — 16 of the
      barrel's 191 runtime exports (the catalog renderer, the expression
      evaluator, the seed helpers) plus anything new. Only the two in-package
      specs were affected; `tsconfig.base.json` aliases `@isis/workflows` to
      `src/index.ts`, so every consumer outside the package already read source.
      Removed; all 34 spec files / 377 tests pass, and tsconfig.lib.json
      typechecks clean. 113 more tracked `.js` files still shadow a sibling `.ts`
      elsewhere (111 in `libs/isis/ai-providers`, 26 of those barrels —
      including the controlnet, ip-adapter and instantid provider barrels); that
      sweep needs its own pass.

      WHY EVERY CELL STAYS OPEN. Each child asks for a completed row naming
      audience exposure, diagnostics, a gate and an unsupported state for its
      mode. Every one of the thirty rows names at least one of those as missing:
      30 lack per-mode audience exposure because no mechanism can express it, 17
      have no in-product diagnostic, 3 have no canonical control id, and 3 have
      every registry declaration refuted by the graphs. I0.15.ae wants zero
      incomplete mode rows and there are thirty. -->
  - [ ] I0.15.a Reconcile seed reuse with provider/workflow/version scope,
        deterministic limits, exposure by audience, replay provenance,
        diagnostics, and unsupported behavior. Evidence: seed row.
  - [ ] I0.15.b Reconcile latent reuse with latent
        format/model/workflow/version/ storage/privacy scope, compatibility,
        audience restriction, diagnostics, and reproducibility. Evidence: latent
        row.
  - [ ] I0.15.c Reconcile identity lock with consent/biometric/reference/
        embedding/strength/scope/version, drift measures, impersonation gates,
        and audience policy. Evidence: identity-lock row.
  - [ ] I0.15.d Reconcile style lock with reference/licensing/artist-style
        policy, adapter/embedding/strength/scope, consistency diagnostics, and
        audience exposure. Evidence: style-lock row.
  - [ ] I0.15.e Reconcile wardrobe lock with garment/accessory/material/color/
        era references, character/shot scope, occlusion/fit diagnostics, and
        variant behavior. Evidence: wardrobe-lock row.
  - [ ] I0.15.f Reconcile prop lock with prop identity/scale/material/state/
        placement/interaction references, shot scope, diagnostics, and failure
        behavior. Evidence: prop-lock row.
  - [ ] I0.15.g Reconcile scene lock with environment/layout/object/spatial/
        weather/time references, shot/sequence scope, diagnostics, and change
        control. Evidence: scene-lock row.
  - [ ] I0.15.h Reconcile lighting lock with
        source/rig/HDRI/time/exposure/color/ shadow references, shot/sequence
        scope, diagnostics, and target color profile. Evidence: lighting-lock
        row.
  - [ ] I0.15.i Reconcile palette lock with color list/space/roles/tolerance/
        source, local/global scope, accessibility/brand constraints, and drift
        metrics. Evidence: palette-lock row.
  - [ ] I0.15.j Reconcile lens lock with focal length/sensor/FOV/distortion/
        aperture/focus and camera/model/shot scope, unit validation, and
        diagnostics. Evidence: lens-lock row.
  - [ ] I0.15.k Reconcile camera lock with pose/path/orientation/intrinsics/
        framing/stabilization/coordinate system and shot/sequence scope.
        Evidence: camera-lock row.
  - [ ] I0.15.l Reconcile pose lock with
        skeleton/keypoints/confidence/visibility/ proportions/contact,
        actor/character/shot scope, diagnostics, and accessibility alternative.
        Evidence: pose-lock row.
  - [ ] I0.15.m Reconcile depth lock with depth type/units/range/confidence/
        occlusion/resolution/camera alignment, normalization, diagnostics, and
        unsupported state. Evidence: depth-lock row.
  - [ ] I0.15.n Reconcile temporal lock with frame/shot identity, cadence/time
        base, consistency window, memory/reference state, flicker/drift metrics,
        and reset behavior. Evidence: temporal-lock row.
  - [ ] I0.15.o Reconcile motion lock with optical flow/trajectory/velocity/
        acceleration/camera-vs-object/masks/time base, diagnostics, and failure
        state. Evidence: motion-lock row.
  - [ ] I0.15.p Reconcile ControlNet pose conditioning with preprocessor/model/
        version, keypoint schema, strength/range, resolution, diagnostics,
        license, and audience. Evidence: ControlNet-pose row.
  - [ ] I0.15.q Reconcile ControlNet depth conditioning with estimator/model/
        version, units/normalization/alignment, strength/range, diagnostics, and
        audience. Evidence: ControlNet-depth row.
  - [ ] I0.15.r Reconcile ControlNet canny conditioning with threshold/aperture/
        resolution/inversion/strength/schedule, diagnostics, and typed preview.
        Evidence: ControlNet-canny row.
  - [ ] I0.15.s Reconcile ControlNet line-art conditioning with preprocessor/
        style/model/version/resolution/strength/schedule, preview, and
        diagnostics. Evidence: ControlNet-line-art row.
  - [ ] I0.15.t Reconcile ControlNet segmentation conditioning with vocabulary/
        class map/palette/model/version/unknown class/strength, semantic
        preview, and diagnostics. Evidence: ControlNet-segmentation row.
  - [ ] I0.15.u Reconcile ControlNet normal conditioning with coordinate system/
        range/encoding/camera alignment/model/version/strength, 3D preview, and
        diagnostics. Evidence: ControlNet-normal row.
  - [ ] I0.15.v Reconcile IP-Adapter style conditioning with reference rights/
        preprocessing/embedding/model/version/weight/schedule/masking, style
        policy, and diagnostics. Evidence: IP-Adapter-style row.
  - [ ] I0.15.w Reconcile IP-Adapter face conditioning with identity consent/
        face detection/embedding/model/version/weight/masking, impersonation/
        biometric gates, and diagnostics. Evidence: IP-Adapter-face row.
  - [ ] I0.15.x Reconcile InstantID with identity image/landmarks/embedding/
        adapter/control versions/strength, consent/impersonation/biometric
        policy, and identity drift metrics. Evidence: InstantID row.
  - [ ] I0.15.y Reconcile open-pose sequences with keypoint/skeleton schema,
        frame rate/time base/interpolation/confidence/person tracking, sequence
        preview, and temporal diagnostics. Evidence: open-pose row.
  - [ ] I0.15.z Reconcile camera paths with coordinate system/keyframes/
        interpolation/lens/time base/collision/constraints, 3D and timeline
        preview, and downstream export. Evidence: camera-path row.
  - [ ] I0.15.aa Reconcile motion vectors with encoding/coordinate space/units/
        scale/direction/time base/resolution/occlusion, visualization, and
        validation. Evidence: motion-vector row.
  - [ ] I0.15.ab Reconcile masks with source/semantic role/format/alpha/
        resolution/alignment/feather/invert/animation/lifecycle, visual preview,
        and privacy/safety constraints. Evidence: mask row.
  - [ ] I0.15.ac Reconcile reference ensembles with ordered refs/roles/weights/
        conflicts/rights/consent/compatibility, per-reference influence/
        exclusion diagnostics, and lineage. Evidence: ensemble row.
  - [ ] I0.15.ad Reconcile multi-control blends with typed control set/weights/
        schedules/masks/precedence/conflict/capacity, component/aggregate
        diagnostics, and unsupported combinations. Evidence: multi-control row.
  - [ ] I0.15.ae Validate every mode independently for audience/host exposure,
        typed inputs/units/version, provider/workflow compatibility, preview/
        diagnostics, raw values/gates, provenance/rights/consent, and explicit
        unsupported/not-configured/degraded behavior. Evidence: zero incomplete
        mode rows.
- [ ] I0.16 Reconcile specialized 3D capabilities in `libs/isis/*`: scene from
      image, part-level and semantic/text mesh editing, AI texturing, PBR
      relighting, mesh transformers, quad meshing, model fine-tuning,
      video-to-mesh, score distillation, advanced/diffusion-edited Gaussian
      splats, gigascale scenes, asset browsing/marketplace, and universal
      rigging. Evidence: each capability is explicitly exposed, operationally
      projected, retained, retired, or excluded.
      <!-- THE RECONCILIATION IS BUILT AND TEN OF THE FOURTEEN CAPABILITIES ARE
      NONE OF THE FIVE THINGS THE EVIDENCE CLAUSE ALLOWS; THE PARENT AND ALL
      FIFTEEN CHILDREN STAY `[ ]`.
      evidence/v1-workbenches/isis-3d-capability-reach.json —
      generate-isis-3d-capability-reach.mjs + .test.mjs (12 tests, 3 ratchets,
      3 controls), wired into inventory + verify. The capability list is read
      from THIS document; `.o` is separated from the fourteen by its own wording
      ("Validate", not "Reconcile").

      THE PARENT'S FIVE VERBS ARE ALL ABOUT REACH, SO REACH IS WHAT IS
      MEASURED, at four altitudes that do not nest: BUILT (the package holds
      exported source and tests), IMPORTED (something outside it imports it),
      APP-REACHABLE (an application arrives, directly or through a chain), and
      PROJECTED (that arrival is an API route or a UI surface).

      ELEVEN OF FOURTEEN HAVE NO APP-REACHABLE PACKAGE AND THEREFORE NO
      PROJECTION: .a scene-from-image, .b part-level editing, .c semantic/text
      mesh editing, .d AI texturing, .e PBR relighting, .f mesh transformers,
      .g quad meshing, .i video-to-mesh, .j score distillation, .k advanced/
      diffusion-edited splats, .l gigascale scenes. These are not empty shells —
      `scene-from-image-composer` holds 107 tracked files and 40 test files,
      `video-to-mesh` 62, `ai-texturing` 494 export statements across 40 source
      files and 35 test files. They are built, exported, tested, documented in
      the architecture deep-dive, and no application can arrive.

      35 OF THE 79 ISIS PACKAGES ARE NOT REACHABLE FROM ANY APPLICATION, and 14
      of the 18 packages these capabilities are built from are among them. THAT
      NUMBER IS A FLOOR, NOT AN ESTIMATE: every non-Isis library importing an
      Isis package seeds the frontier without checking whether an app reaches
      that library, and dynamic `import()` and `require()` count as edges, so
      both choices can only ADD reach.

      A CONSUMER THAT NOTHING REACHES IS NOT REACH. Two capabilities have
      importers and still cannot be arrived at. `ai-texturing`'s only importers
      are two files in `3d-comfyui-nodes`, and `3d-comfyui-nodes` is imported by
      nothing at all. `3d-asset-library` (705 export statements) is imported
      only from `3d-semantic-editing`, itself unreferenced. One hop of apparent
      use, terminating in silence — which a one-hop consumer count reports as
      healthy.

      AND A SHIPPED PAGE WITH A SIMILAR NAME IS NOT THE CAPABILITY.
      `apps/yemaya/studio-web/src/pages/RelightStudioPage` imports
      `@isis/relight-studio`, a 2,365-line module of IC-Light/GenLit FRAME
      relighting — presets, EXR passes, NLE interchange — which is the image
      relighting I0.17.c asks about, not the PBR relighting of I0.16.e. Both
      were bound to this cell in a first pass here and separated on reading the
      source; the LumiGauss relightable-Gaussian library I0.16.e does name has
      zero consumers, and a test pins `relight-studio` out of every I0.16 cell
      so the conflation cannot come back.

      THE GUARD AGAINST THE AUDIT CITING ITSELF IS LIVE AND CURRENTLY DECIDES
      NOTHING, and both halves are published. `scripts/v1-workbenches/**` and
      `tools/conventions/**` import three Isis packages — entitlements,
      curated-cards, output-gallery — to measure them; all three also have
      product consumers, so excluding audit paths removes nobody's only caller
      today. It is recorded because the hazard is real the moment a package's
      product consumers go away, and an audit that counted itself would report
      exactly the wrong verdict at exactly that moment.

      NOT ONE ROW CARRIES AN APPROVED DISPOSITION, AND THE FIELD NAME IS WHY.
      The estate holds one disposition per package and the field is called
      `proposedDisposition`, so `reuse` is a proposed reuse exactly as
      `retire-candidate` is a proposed retirement — the value does not upgrade
      the field. I0.6 produced proposals and labelled them honestly. Measured
      from the field NAMES the package inventory carries, so this stops being
      true the moment an approved one appears. I0.16.o wants zero incomplete 3D
      rows and there are fourteen; the three that fail on the disposition ALONE
      — .h model fine-tuning, .m asset browsing/marketplace, .n universal
      rigging — are the ones an application can reach and project, so the estate
      does ship some of this. -->
  - [ ] I0.16.a Reconcile scene-from-image generation independently: source/
        camera/depth/segmentation/scale assumptions, scene graph/geometry/
        material outputs, uncertainty, preview, provenance/rights, validation,
        target formats, audience/host, and disposition. Evidence: scene-from-
        image capability row.
  - [ ] I0.16.b Reconcile part-level mesh editing independently: stable part/
        topology/selection identity, local operation/mask/constraint, boundary/
        UV/skin/material preservation, preview/diff/undo, validation, and
        disposition. Evidence: part-edit row.
  - [ ] I0.16.c Reconcile semantic/text mesh editing independently: typed edit
        intent/target/strength/constraints, ambiguous target clarification,
        geometry/topology/material/rig impact, variants, provenance, quality,
        and disposition. Evidence: semantic mesh-edit row.
  - [ ] I0.16.d Reconcile AI texturing independently: mesh/UV/material inputs,
        prompt/reference/rights, texture set/channels/resolution/UDIM/color/
        tiling, seam consistency, preview, PBR gates, and disposition. Evidence:
        AI-texturing row.
  - [ ] I0.16.e Reconcile PBR relighting independently: geometry/material/
        normals/environment/light rig/camera/color inputs, relit maps/renders,
        energy/material consistency, preview, and disposition. Evidence:
        PBR-relighting row.
  - [ ] I0.16.f Reconcile mesh-transformer capabilities independently: supported
        model/task/version/input/output/topology/scale limits, resource/cost,
        quality/compatibility/rights/provenance, preview, and disposition.
        Evidence: mesh-transformer row.
  - [ ] I0.16.g Reconcile quad meshing independently: source mesh/point/splat,
        target density/flow/symmetry/boundaries/features, UV/material/rig
        preservation, topology metrics, preview, downstream DCC, and
        disposition. Evidence: quad-meshing row.
  - [ ] I0.16.h Reconcile 3D model fine-tuning independently: dataset/license/
        consent/provenance, architecture/base/version, training parameters/
        resource/cost, checkpoints/evaluation, promotion/quarantine, and
        disposition. Evidence: model fine-tuning row.
  - [ ] I0.16.i Reconcile video-to-mesh independently: video/camera/calibration/
        time/mask inputs, reconstruction scope, geometry/texture/animation/
        confidence outputs, identity/biometric/rights, quality, and disposition.
        Evidence: video-to-mesh row.
  - [ ] I0.16.j Reconcile score-distillation capabilities independently:
        representation/camera/render/diffusion/model/version/objective/optimizer/
        seeds/resource inputs, intermediate/final artifacts, reproducibility,
        safety/rights, evaluation, and disposition. Evidence: score-distillation
        row.
  - [ ] I0.16.k Reconcile advanced and diffusion-edited Gaussian splats
        independently: source/camera/splat/model/edit/mask inputs, coordinate/
        SH/compression outputs, temporal/spatial/artifact/privacy gates,
        preview/interchange, and disposition. Evidence: advanced-splat row.
  - [ ] I0.16.l Reconcile gigascale scene capabilities independently: tiling/
        streaming/LOD/spatial index/resolver/coordinate/precision/distributed
        build, resource/cost, preview, integrity/performance, export, and
        disposition. Evidence: gigascale scene row.
  - [ ] I0.16.m Reconcile asset browsing/marketplace independently: catalog/
        search/filter/preview/license/price/entitlement/provenance/import/
        dependency/update/revocation/attribution, provider trust, audience, and
        disposition. Evidence: asset-marketplace row.
  - [ ] I0.16.n Reconcile universal rigging independently: source mesh/
        skeleton/archetype/landmarks, auto joints/weights/controls/retarget/
        constraints, target rigs/formats, deformation metrics, preview, and
        disposition. Evidence: universal-rigging row.
  - [ ] I0.16.o Validate every 3D capability row names owner/package/consumer,
        audience/host, typed contract, provider/job/resource, persistence/
        lineage, UI/API/operational projection, rights/safety/privacy, quality/
        compatibility/interchange, tests, and one approved disposition.
        Evidence: zero incomplete 3D rows.
- [ ] I0.17 Reconcile specialized image/video/audio capabilities: portrait and
      face synthesis, relighting, fast generation, AnimateDiff Lightning,
      Florence-2 analysis, live preview, camera/motion control, AV narrative,
      video editing/object removal, NVIDIA RTX/RIFE/Topaz enhancement, visual
      dubbing, foley, TTS, text-to-audio, voice cloning, music, and
      conversational audio. Evidence: each capability has a host, consent/right
      policy, typed controls, preview, lineage, gates, and release path or
      explicit exclusion.
      <!-- THE RECONCILIATION IS BUILT AND ONE ROW OF TWENTY-ONE CARRIES THE
      PARENT'S SEVEN COLUMNS; THE PARENT AND ALL TWENTY-TWO CHILDREN STAY `[ ]`.
      evidence/v1-workbenches/isis-media-capability-matrix.json —
      generate-isis-media-capability-matrix.mjs + .test.mjs (10 tests, 3
      ratchets, 2 controls), wired into inventory + verify. The capability list
      is read from THIS document; `.v` is separated by its own wording.

      THE UNIT HAD TO BE A MODULE, NOT A PACKAGE. Ten of the twenty-one do not
      live wholly in a package of their own — AnimateDiff Lightning, Florence-2,
      live preview, RTX, RIFE, Topaz, TTS, text-to-audio and conversational
      audio are directories or single files inside `@isis/ai-providers` or
      `@isis/video-enhancement`, and music spans a package AND a providers
      directory. A package-level reachability answer would call each of them
      reachable the moment anything imported the parent barrel, so reach is read
      from IMPORTS AND THEIR BINDINGS: an application reaches a capability if it
      imports a path inside the module, or imports the module's package and
      NAMES a symbol the module exports. A star or default import of the parent
      names nothing and cannot decide either way, so it is recorded as
      indeterminate rather than counted or dropped; today there are none.
      Eleven of the twenty-one have no application import at all.

      A COLUMN IS A MEMBER, NOT AN OCCURRENCE. Exported interface, type-literal
      AND class bodies are brace-matched and their member names are what each
      column matches, with optionality recorded, because an optional consent
      field and a required one are different promises. Class members had to be
      read too: without them a class-based module measures as empty for a reason
      about its style rather than its contract, which is how TTS first read as
      having no typed controls at all.

      FIVE OF THE SEVEN BIOMETRIC CAPABILITIES DECLARE NO CONSENT MEMBER
      ANYWHERE IN THEIR TYPES OR CLASSES: .a portrait synthesis, .i AV
      narrative, .o visual dubbing, .q TTS, .u conversational audio. Visual
      dubbing is the loudest — 19 source files that put one person's speech in
      another person's face, 191 exported symbols, 776 type members, and the
      strings "consent", "rights", "licence", "watermark" and "disclosure" do
      not occur in the module at all. Conversational audio carries hundreds of
      licence occurrences for its voice marketplace and zero consent. TTS is the
      sharpest: `providers/tts` declares no object type of its own and its whole
      contract is a 1,403-line `ElevenLabsProvider` class exposing `cloneVoice`,
      `cloneVoiceInstant`, `cloneVoiceProfessional` and `designVoice` — 71 class
      members, none of them consent.

      AND WHERE CONSENT DOES EXIST IT IS A SEPARATE REQUEST NOBODY MAKES. Voice
      cloning has 606 type members, three of which mention consent, all on one
      `VoiceConsentRights*` family in one file; face synthesis has 824 members,
      two of them on a `FaceSynthesisEthicalSafeguardsRequest`. Neither is a
      field on the generation request, so consent is a check the caller may
      decline to run — and tracing the symbols those files export shows the ONLY
      non-test callers are the package's own barrel and, for face synthesis, the
      v1-workbenches script that measures it. THIS IS THE INVERSE OF THE
      ESTATE'S OWN BEST WORK: the I2.16 conditioning reference makes
      `consentRef`, `consentExpiresAt` and `consentScopes` non-nullable on the
      face-identity layer, so a layer without consent cannot be constructed.

      A RIGHTS POLICY IS THE WIDEST GAP: 16 of 21 declare no licence, rights,
      royalty, attribution or ownership member of any kind. Then lineage (11),
      preview (10), release path (10), gates (6), consent (5 of the 7 biometric)
      and typed controls (2).

      WHY EVERY CELL STAYS OPEN. Video object removal (.k) is the one row that
      carries all seven parent columns — it ships a C2PA credential type, a mask
      preview, a provenance package and a safety context. The artifact names
      that field `carriesAllParentColumns`, not `complete`, because each child
      asks for more than the parent's seven: .k also wants a protected-evidence
      and manipulation policy and a disposition, and the only disposition the
      estate records is the `proposedDisposition` I0.16 found. I0.17.v wants
      zero incomplete media rows and there are twenty. -->
  - [ ] I0.17.a Reconcile portrait synthesis independently: subject/context/
        style/pose/wardrobe/background references, consent/biometric/public-
        figure policy, identity/anatomy/quality, disclosure/provenance, host,
        and release/disposition. Evidence: portrait row.
  - [ ] I0.17.b Reconcile face synthesis independently: identity/attribute/
        expression/age/pose/lighting controls, detection/embedding, consent/
        impersonation/child safety, artifact/identity metrics, watermark/
        provenance, audience, and disposition. Evidence: face row.
  - [ ] I0.17.c Reconcile image relighting independently: image/depth/normal/
        mask/environment/light/camera/color inputs, shadow/specular/material/
        identity preservation, diagnostics, preview, and disposition. Evidence:
        relighting row.
  - [ ] I0.17.d Reconcile fast-generation paths independently:
        model/distillation/ quantization/step/scheduler/provider/version,
        quality/latency/cost trade- offs, unsupported controls, tier exposure,
        provenance, and disposition. Evidence: fast-generation row.
  - [ ] I0.17.e Reconcile AnimateDiff Lightning independently: base model/
        motion module/version/workflow/frame/rate/duration/seed/conditioning,
        temporal quality/flicker, license/provenance, preview, and disposition.
        Evidence: AnimateDiff row.
  - [ ] I0.17.f Reconcile Florence-2 analysis independently: supported caption/
        detection/grounding/OCR/segmentation tasks, model/version, input/output
        schema/confidence, privacy/safety, evaluator limits, host, and
        disposition. Evidence: Florence-2 row.
  - [ ] I0.17.g Reconcile live preview independently: progressive/intermediate/
        low-res/stream protocol, cancellation/backpressure/cost,
        unsafe/unscanned content containment, ephemeral retention, accessible
        alternative, and audience. Evidence: live-preview row.
  - [ ] I0.17.h Reconcile camera/motion control independently: camera path/lens/
        pose/depth/flow/vector/reference/time controls, model/workflow
        compatibility, diagnostics/preview, and disposition. Evidence:
        camera-motion row.
  - [ ] I0.17.i Reconcile audiovisual narrative generation independently:
        script/scene/shot/character/dialogue/camera/image/video/voice/music/SFX/
        captions/timecode dependencies, continuity, rights/safety, bundles, and
        disposition. Evidence: AV-narrative row.
  - [ ] I0.17.j Reconcile video editing independently: trim/split/reorder/
        extend/inpaint/replace/style/color/frame-rate/upscale/audio/caption
        operations, source preservation, timeline/preview, provenance, quality,
        and disposition. Evidence: video-editing row.
  - [ ] I0.17.k Reconcile video object removal independently: object/mask/track/
        occlusion/background/motion inputs, temporal fill consistency, protected
        evidence/manipulation policy, preview/diff, and disposition. Evidence:
        object-removal row.
  - [ ] I0.17.l Reconcile NVIDIA RTX processing independently: supported SDK/
        model/operation/GPU/driver/version, inputs/outputs, latency/quality/
        resource/cost, host licensing, diagnostics, and disposition. Evidence:
        RTX row.
  - [ ] I0.17.m Reconcile RIFE interpolation independently: implementation/
        model/version/input/output frame rates/time base/scene cuts/artifacts/
        audio sync, quality/latency, preview, and disposition. Evidence: RIFE
        row.
  - [ ] I0.17.n Reconcile Topaz enhancement independently:
        product/model/version/ license/host/provider,
        upscale/denoise/sharpen/frame operations, parameters/outputs,
        quality/artifact/cost, provenance, and disposition. Evidence: Topaz row.
  - [ ] I0.17.o Reconcile visual dubbing independently: source video/audio/
        transcript/translation/voice/face/timing, speaker identity/consent,
        lip-sync/translation/audio metrics, captions/disclosure/provenance, and
        disposition. Evidence: visual-dubbing row.
  - [ ] I0.17.p Reconcile foley generation independently: scene/action/material/
        event/time/perspective/variation prompts, audiovisual sync, source/
        training rights, audio technical/quality gates, package, and
        disposition. Evidence: foley row.
  - [ ] I0.17.q Reconcile TTS independently: text/SSML/language/voice/style/
        pronunciation/timing/output controls, voice license/consent/disclosure,
        safety/quality/accessibility, and disposition. Evidence: TTS row.
  - [ ] I0.17.r Reconcile text-to-audio independently: prompt/category/duration/
        scene/timing/variation/loop/sample/channel controls, safety/rights/
        originality, technical quality, provenance, and disposition. Evidence:
        text-to-audio row.
  - [ ] I0.17.s Reconcile voice cloning independently: enrollment samples/
        identity verification/consent/purpose/locale/quality, embedding/model/
        retention/revocation, impersonation/disclosure/watermark, access, and
        disposition. Evidence: voice-cloning row.
  - [ ] I0.17.t Reconcile music generation independently: brief/genre/style/
        artist-policy/key/tempo/meter/structure/instrument/duration/stems/lyrics/
        seed controls, originality/rights/quality, provenance, and disposition.
        Evidence: music row.
  - [ ] I0.17.u Reconcile conversational audio independently: participants/
        voices/roles/script/turns/interruptions/emotion/timing/room/SFX/music/
        captions, voice consent/safety, streaming/cancel, and disposition.
        Evidence: conversational-audio row.
  - [ ] I0.17.v Validate every media capability for owner/package,
        audience/host, typed controls/contracts, provider/workflow/job, live
        preview/semantic alternative, lineage/provenance,
        consent/rights/safety/privacy, raw quality diagnostics/gates,
        bundles/publish/recall, tests, and approved release or exclusion.
        Evidence: zero incomplete media rows.
- [ ] I0.18 Reconcile the existing operator-only parity monitors, feature flags,
      audit log, RunPod topology, model/workflow parity, API parity,
      entitlement, anomaly, chargeback, marketplace, monetization, resource
      recommendation, and collection surfaces. Evidence: route-by-route keep,
      merge, retire, and live-data disposition.
      <!-- THE RECONCILIATION IS BUILT AND NOT ONE OF THE FOURTEEN FAMILIES
      CARRIES A PAGE, AN API, A LIVE-DATA VERDICT AND A RECORDED DISPOSITION
      TOGETHER; THE PARENT AND ALL FIFTEEN CHILDREN STAY `[ ]`.
      evidence/v1-workbenches/isis-operator-surface-matrix.json —
      generate-isis-operator-surface-matrix.mjs + .test.mjs (9 tests, 4
      ratchets, 1 control), wired into inventory + verify. It joins committed
      evidence rather than re-deriving it: the I0.1.a register (now four
      surfaces), the I0.3.b page walkthrough's live-data verdicts, and the
      I0.1.e disposition ledger.

      THIS CELL IS WHY THE REGISTER GREW. Chasing "which routes serve the
      operator-only surfaces" found `apps/oshun/admin/src/app/isis/*` — eight
      operator pages in none of the register's three surfaces, and the only UI
      that calls the 77 admin-isis-* modules. See the I0.1.a correction; the
      register, the effective-route matrix, the tier rows, the boundary cases,
      the disposition ledger and the I0.1.g baseline were all repaired first,
      because a per-family answer built on a register missing a host would have
      been confidently wrong.

      FIVE FAMILIES HAVE AN OPERATOR API AND NO OPERATOR PAGE AT ALL:
      .h entitlement, .i anomaly, .j chargeback, .l monetization, .m resource
      recommendation. THREE ARE THE MIRROR IMAGE — .e feature flags and .f the
      audit log are pages in the operator SPA with no admin-isis-* module behind
      them, and .h has neither, so the surface the cell names does not exist as
      an operator surface at all.

      A NEAR-MISS THAT FILLS A COLUMN IS WORSE THAN AN EMPTY ONE. Three keywords
      were removed after reading what the modules actually serve:
      `admin-isis-integration-events` validates outbound integration events and
      is not the mutation/event/job/provider/release audit trail .f names;
      `admin-isis-account-protection` is abuse defence, not the
      actor/tenant/plan/licence rights of .h; and `artifact-detection` is a
      false friend, since in this estate an artifact is a visual defect in a
      generated image and not an anomaly signal. Removing them moved three
      families from "has an API" to "has none", which is the true answer, and a
      test pins them out so the coverage they manufactured cannot come back.

      AND WHERE THERE IS A PAGE, THE WALKTHROUGH FOUND IT NOT CONFIGURED. Six
      families — .a generation-type parity, .b workflow parity, .c model parity,
      .e feature flags, .f audit log, .g RunPod topology — have pages whose
      EVERY live-data verdict is not-configured. `/jobs/generation-parity`
      reaches 0 of its 27 client operations, the single largest unserved page in
      the estate; `/feature-flags` 0 of 3; `/audit-logs` 0 of 2. Only .d
      provider API/SDK parity has any page the walkthrough called live.

      THE UNCLAIMED SET IS PUBLISHED, BECAUSE IT IS THE OTHER HALF OF THE
      ANSWER: 48 of the 77 admin-isis-* modules serve surfaces this cell does
      not name at all, along with eight operator SPA routes and five admin
      console pages. Every one is an operator capability with no seat in the
      fourteen families I0.18 enumerates.

      WHY EVERY CELL STAYS OPEN. I0.18.o wants zero undispositioned operator
      routes. The I0.1.e ledger labels the disposition cell `owed` for every
      route in every one of these families — 112 of 200 routes are knowingly
      undecided and now recorded as such in the I0.1.g baseline — so no family
      can carry the keep/merge/retire half of its own evidence clause. -->
  - [ ] I0.18.a Reconcile generation-type parity monitors: authoritative type/
        capability sources, expected versus implemented provider/workflow/UI/
        contract coverage, divergence/freshness, owner, and disposition.
        Evidence: generation-type route row.
  - [ ] I0.18.b Reconcile workflow parity monitors: registry/graph/template/
        version/node/provider/host expected versus deployed coverage, mismatch/
        drift/unsupported state, and disposition. Evidence: workflow parity row.
  - [ ] I0.18.c Reconcile model/model-version parity monitors:
        registry/artifact/ provider/endpoint/evaluation/promotion/host expected
        versus actual versions/states and disposition. Evidence: model parity
        row.
  - [ ] I0.18.d Reconcile provider API/SDK/UI parity monitors: operations/
        request-response/error/capability/version/control exposure and exact
        missing producer/consumer surface. Evidence: API parity row.
  - [ ] I0.18.e Reconcile feature-flag surfaces: registry/runtime evaluation/
        owner/purpose/audience/default/expiry/rollout/rollback/removal and
        authorized operator control. Evidence: feature-flag row.
  - [ ] I0.18.f Reconcile audit-log surfaces: mutation/event/job/provider/
        release/emergency coverage, correlation/redaction/integrity/retention/
        export/access and live source. Evidence: audit row.
  - [ ] I0.18.g Reconcile RunPod topology surfaces: accounts/projects/regions/
        network volumes/templates/endpoints/pods/workers/GPUs/models/queues/
        health/capacity/cost/credentials with safe live probes. Evidence:
        topology row.
  - [ ] I0.18.h Reconcile entitlement surfaces:
        actor/tenant/project/plan/license/
        asset/workflow/model/provider/quota/territory/time rights, decision
        provenance, enforcement, and appeal. Evidence: entitlement row.
  - [ ] I0.18.i Reconcile anomaly surfaces: signal/model/rule/version/baseline/
        confidence/threshold/category, affected provider/job/cost/quality/
        security, triage/alert/ack/resolution, and false-positive handling.
        Evidence: anomaly row.
  - [ ] I0.18.j Reconcile chargeback surfaces: usage/rate-card/allocation/
        estimate/reservation/actual/variance/invoice/project/tenant/provider/
        resource attribution, correction, export, and access. Evidence:
        chargeback row.
  - [ ] I0.18.k Reconcile marketplace surfaces: provider/vendor/asset/model/
        workflow/license/price/entitlement/trust/security/rights/provenance/
        compatibility/install/update/revoke and disposition. Evidence:
        marketplace row.
  - [ ] I0.18.l Reconcile monetization surfaces: offer/plan/price/currency/tax/
        entitlement/quota/metering/billing/refund/revenue share/territory/
        disclosure and product-boundary approval. Evidence: monetization row.
  - [ ] I0.18.m Reconcile resource-recommendation surfaces: workload/model/
        quality/latency/budget/capacity inputs, candidates, raw estimates/
        confidence/reasons, policy/floors, reservation, actual comparison, and
        no auto-dispatch. Evidence: recommendation row.
  - [ ] I0.18.n Reconcile collection surfaces: collection/member/relation/order/
        taxonomy/eligibility/audience/owner/lifecycle/search/bulk/export/
        publisher and operator-only versus creator exposure. Evidence:
        collection row.
  - [ ] I0.18.o For each route assign canonical host/owner/audience, live API/
        store/provider, fixture/degraded states, keep/refactor/merge/retire/
        redirect decision, migration, tests, and evidence. Evidence: zero
        undispositioned operator routes.
- [ ] I0.19 Reconcile every output/interchange format and package, including
      PNG, JPEG, WebP, TIFF, EXR, MP4, MOV, WAV, FLAC, OBJ, FBX, glTF/GLB, USD,
      Alembic, Gaussian-splat/SPZ, manifests, EDL/shot packages, DCC bundles,
      and engine bundles. Evidence: each supported format maps contract,
      metadata, preview, deterministic naming, integrity, provenance, quality,
      downstream ingest, loss profile, and authorized host; unsupported formats
      are explicit.
      <!-- THE RECONCILIATION IS BUILT AND NOT ONE OF THE NINETEEN FORMATS
      CARRIES ALL TEN COLUMNS; THE PARENT AND ALL TWENTY CHILDREN STAY `[ ]`.
      evidence/v1-workbenches/isis-output-format-matrix.json —
      generate-isis-output-format-matrix.mjs + .test.mjs (9 tests, 3 ratchets,
      2 controls), wired into inventory + verify. The format list is read from
      THIS document.

      THE TEMPLATE COMES FROM THE ESTATE AND THE DERIVATION REFUSED THE RICHER
      GUESS. Reading the fullest family (Exr, 16 symbols) suggests a five-symbol
      spine — ArtifactRole, DependencyManifestReference, ManifestProvenance,
      ArtifactManifest, OutputPackage. Intersecting what the families ACTUALLY
      carry gives three: ArtifactRole and ManifestProvenance are not universal.
      The generator derives the spine rather than listing it, and a test pins
      that the derivation reacts to its input instead of returning a constant.

      SEVENTEEN OF NINETEEN FOLLOW IT, AND THE PART THEY SHARE IS GOOD. Every
      one points its `<Fmt>ArtifactManifest` at the shared
      `OutputArtifactManifest`, which carries `filename`,
      `checksumAlgorithm`/`checksum` and `provenance` — so deterministic naming,
      integrity and provenance are answered for all seventeen at once, from one
      contract, with a dependency-manifest reference back to the checklist item
      and ticket that produced them.

      TWO FORMATS HAVE NO OUTPUT PACKAGE AND THEY ARE NOT THE SAME FINDING.
      GAUSSIAN SPLAT / SPZ HAS NO SYMBOL AT ALL — no package, no manifest, no
      provenance — and the format is not hypothetical:
      `libs/isis/3d-generation/src/types/3d.types.ts` carries `'spz'` in its
      format union and `libs/isis/gaussian-splatting`'s optimizer writes and
      converts it. So a splat can be produced and has no deterministic name, no
      checksum and no provenance record. DCC BUNDLES ARE THE OTHER SHAPE: they
      have `DCCBundleFileEntry`, `DCCBundleManifest` and `DCCExportBundle`, so
      the template was started and stopped before the package. Neither is
      recorded as explicitly unsupported, which is what the parent asks for when
      a format is not supported.

      AND THREE COLUMNS ARE CARRIED BY NO FORMAT AT ALL: quality, downstream
      ingest, and authorized host. Preview is carried by exactly one — the
      engine bundle. A loss profile by four, and only because PNG, TIFF, EXR and
      FLAC name a compression mode that happens to imply one. The four members
      every package shares are `format`, `manifest`, `schemaVersion` and
      `sourceOutputClass`. THE MATCHER IS NOT SIMPLY FAILING: preview, loss
      profile and metadata are looked for the same way and all three come back
      present somewhere, which is what makes the three zeroes mean something.

      WHY EVERY CELL STAYS OPEN. Each child asks for its format's own technical
      limits AND the shared columns. Seventeen rows are missing quality,
      downstream ingest and authorized host; two are missing the contract
      itself. I0.19.t wants every supported format complete and every
      unsupported one explicit, and neither half holds. -->
  - [ ] I0.19.a Reconcile PNG support: color type/depth/profile/alpha/metadata/
        animation applicability, limits, preview, deterministic encoding/name,
        hash/provenance, rights/quality, ingest, and loss profile. Evidence: PNG
        row.
  - [ ] I0.19.b Reconcile JPEG support: profile/quality/subsampling/color/EXIF/
        orientation/metadata limits, preview, deterministic encoding/name,
        hash/provenance, rights/quality, ingest, and lossy behavior. Evidence:
        JPEG row.
  - [ ] I0.19.c Reconcile WebP support: lossless/lossy/quality/alpha/animation/
        color/metadata limits, preview, deterministic encoding/name, integrity/
        provenance, ingest, and loss profile. Evidence: WebP row.
  - [ ] I0.19.d Reconcile TIFF support: classic/BigTIFF, pages/layers, bit
        depth/ sample format/compression/color/alpha/metadata limits, preview,
        naming, integrity/provenance, ingest, and loss. Evidence: TIFF row.
  - [ ] I0.19.e Reconcile EXR support: version/compression/channels/layers/
        half-float/full-float/color/metadata/tile-scanline/deep applicability,
        preview/tonemap, naming, integrity/provenance, ingest, and loss.
        Evidence: EXR row.
  - [ ] I0.19.f Reconcile MP4 support: container/codec/profile/level/bitrate/
        resolution/frame rate/color/HDR/audio/subtitles/timecode/metadata,
        preview, deterministic transcode/name, integrity/provenance, ingest, and
        loss. Evidence: MP4 row.
  - [ ] I0.19.g Reconcile MOV support: QuickTime container/codecs/profiles/
        ProRes/alpha/timecode/tracks/color/HDR/audio/subtitles/metadata,
        preview, deterministic transcode/name, integrity/provenance, ingest, and
        loss. Evidence: MOV row.
  - [ ] I0.19.h Reconcile WAV support: RIFF/RF64, PCM/float, sample rate/depth/
        channels/layout/BWF/timecode/metadata, audition/waveform, deterministic
        encoding/name, integrity/provenance, ingest, and lossless behavior.
        Evidence: WAV row.
  - [ ] I0.19.i Reconcile FLAC support: version/level/sample
        rate/depth/channels/ compression/metadata/cues/pictures,
        audition/waveform, deterministic encoding/name, integrity/provenance,
        ingest, and lossless behavior. Evidence: FLAC row.
  - [ ] I0.19.j Reconcile OBJ support: vertices/normals/UVs/faces/groups/
        materials/MTL/textures/units/axes/naming/encoding/limits, preview,
        integrity/provenance, ingest/round-trip, and known loss. Evidence: OBJ
        row.
  - [ ] I0.19.k Reconcile FBX support: supported versions/binary-text, units/
        axes/hierarchy/geometry/materials/textures/skeleton/skin/animation/
        cameras/lights/custom props, preview, deterministic constraints,
        integrity/provenance, ingest, and loss. Evidence: FBX row.
  - [ ] I0.19.l Reconcile glTF/GLB support: version/extensions/buffers/images/
        geometry/materials/PBR/skins/animation/cameras/lights/units/axes/
        compression, preview, validation, naming, integrity/provenance, ingest,
        and loss. Evidence: glTF/GLB row.
  - [ ] I0.19.m Reconcile USD support: profile/version/file formats/layers/
        composition/variants/payloads/refs/resolver/schemas/units/axes/materials/
        skeleton/animation, preview, `usdchecker`, deterministic packaging,
        integrity/provenance, target ingest, and loss. Evidence: USD row.
  - [ ] I0.19.n Reconcile Alembic support: version/archive type/units/axes/
        hierarchy/geometry/topology/UV/normals/attributes/cameras/animation/time
        sampling/compression, preview, integrity/provenance, ingest, and known
        loss. Evidence: Alembic row.
  - [ ] I0.19.o Reconcile Gaussian-splat/SPZ support: version/coordinate/
        positions/scales/rotations/opacity/SH/cameras/compression/count/limits/
        privacy, spatial preview, validation, naming, integrity/provenance,
        target ingest, and loss. Evidence: splat/SPZ row.
  - [ ] I0.19.p Reconcile manifest support: schema/version/profile/IDs/refs/
        hashes/media/metadata/dependencies/rights/consent/gates/provenance/
        signatures/compatibility, deterministic serialization/name, validation,
        import, and losslessness. Evidence: manifest row.
  - [ ] I0.19.q Reconcile EDL/shot packages: EDL/XML/AAF/OTIO or supported
        profile, sequence/shot/clip IDs, source refs, timecode/rate, handles/
        transitions/tracks/audio/metadata/assets/manifests, validation,
        round-trip, and known loss. Evidence: editorial-package row.
  - [ ] I0.19.r Reconcile DCC bundles: target app/version/plugins, project/
        scene/assets/refs/resolver/cache/textures/fonts/scripts policy,
        manifest/lock/SBOM/provenance, deterministic layout, sandbox validation,
        import/round-trip, and loss. Evidence: DCC-bundle row.
  - [ ] I0.19.s Reconcile engine bundles: target engine/version/platform/
        renderer/plugins, assets/import settings/metadata/LODs/collision/
        materials/rig/animation/audio/code policy, manifest/dependencies/
        provenance, deterministic layout, validation, ingest, and loss.
        Evidence: engine-bundle row.
  - [ ] I0.19.t For every format/profile assign supported import/export/
        preview/publish direction, asset kinds/audiences/hosts, authoritative
        contract/schema/validator, limits/security, naming/integrity/provenance/
        quality, dependency packaging, round-trip/loss policy, version/
        deprecation, and explicit unsupported behavior. Evidence: zero
        incomplete format rows.
- [ ] I0.20 Crosswalk Phase I against
      `ISIS_CHROMA_RUNPOD_MVP_TODOS_2026-09-11.md` before any I6–I9 cell is
      started. Added 2026-09-18 by the board specification audit: that tracker
      has built, since 2026-09-11 and with no reference to this ledger, the
      RunPod endpoints, spend governor, model library and cache, Civitai intake
      and licence register, job/output pages in `apps/isis/web`, and a 3D
      workspace plan (T.15) — the same ground as I6.6, I7.1, I7.3, I7.5, I7.13,
      I7.18 and I7.19 here. Evidence: a committed crosswalk table under
      `evidence/v1-workbenches/isis/` with one row per I6–I9 parent, in the
      merged / superseded / independent / split vocabulary of Isis ADR-0008.
  - [ ] I0.20.a List every checked item of the Isis tracker's C, V, A, L, E, H
        and T sections that ships code in `apps/isis/web`,
        `apps/isis/generation-api` or `libs/isis/*`, with the path it shipped.
        Evidence: shipped-surface rows keyed by tracker id.
  - [ ] I0.20.b For each I6–I9 parent, record whether an Isis-tracker item
        already delivers it in whole (superseded), in part (split, naming the
        remainder), under a different design (merged, naming which design
        wins and why), or not at all (independent). Evidence: zero unmapped
        I6–I9 parents.
  - [ ] I0.20.c Re-run the I0.3 page map over the `apps/isis/web` pages added
        after the BASE.1 inventory SHA, so keep/refactor/merge/retire covers
        them. Evidence: I0.3 rows cite the refreshed inventory SHA.
  - [ ] I0.20.d Record the reverse direction in the Isis tracker: every open T,
        E and F item that adds a page or an API to those paths names the I-cell
        it satisfies or states that none applies. Evidence: the Isis tracker's
        open dashboard items (T.15, T.22.06, E.01.02, E.02.04, F.20.03) carry
        the reference.

### I1 — Canonical generation-control model

- [ ] I1.1 Verify the canonical workflow-template state machine against current
      contracts. Evidence: code-derived state diagram and known-value tests.
  - [x] I1.1.a Inventory workflow/template status enums, transition commands,
        guards, APIs, persistence, events/jobs, operator UI, and tests from
        current code/contracts. Evidence: source mapping.
        <!-- isis-workflow-state-machine.json: source mapping of the CURRENT Isis
                                                                                                                                                                                                                                                                        workflow-template state machine (realized in apps/isis/workflow-registry/src/
                                                                                                                                                                                                                                                                        services/workflow.service.ts). Status enums: WorkflowStatus (DRAFT/PUBLISHED/
                                                                                                                                                                                                                                                                        DEPRECATED/ARCHIVED) + WorkflowVisibility (4); persistence: Workflow.status +
                                                                                                                                                                                                                                                                        WorkflowVersion.version/deprecated. Transition commands: 8/12 intended
                                                                                                                                                                                                                                                                        transitions have a code signal — the CORE lifecycle (create, edit=update,
                                                                                                                                                                                                                                                                        version=createVersion, activate, deprecate, archive, restore, publish) is
                                                                                                                                                                                                                                                                        implemented; the review sub-flow (submit/review/approve/revoke) is honestly
                                                                                                                                                                                                                                                                        reported ABSENT (workflow-templates have no submit→review→approve path).
                                                                                                                                                                                                                                                                        Guards: auth preHandlers present (routes/rbac.ts requireAuth+permission).
                                                                                                                                                                                                                                                                        62 workflow tests, 16 operator-UI components, 7 event sources. Verb-synonym
                                                                                                                                                                                                                                                                        detection (edit→update, version→createVersion) + the git-pathspec both-globs
                                                                                                                                                                                                                                                                        fix (routes/*.ts AND routes/**/*.ts) avoid false negatives.
                                                                                                                                                                                                                                                                        generate-isis-workflow-state-machine.mjs + 6 tests, wired into inventory/
                                                                                                                                                                                                                                                                        verify + CI. I1.1.b (diagram), I1.1.c (lifecycle reconciliation), I1.1.d-f
                                                                                                                                                                                                                                                                        (transition/guard/immutability verification with negative tests) build on
                                                                                                                                                                                                                                                                        this mapping and stay unchecked. -->
  - [x] I1.1.b Generate the implemented state/transition/guard/actor/side-effect
        diagram from authoritative definitions. Evidence: code-derived diagram.
        <!-- evidence: isis-workflow-state-diagram.json .diagramMermaid —
        code-derived state diagram (DRAFT→PUBLISHED→DEPRECATED→ARCHIVED w/
        create/edit/version/ publish/activate/deprecate/archive/restore) built
        on the I1.1.a census. generate-isis-workflow-state-diagram.mjs (--check
        gate), 9 tests. -->
  - [x] I1.1.c Reconcile draft/review/approved/active/deprecated/revoked/
        archived and any current states with the intended lifecycle. Evidence:
        state disposition table.
        <!-- evidence: .dispositionTable — 7 canonical states reconciled: 4 present
                                                                                                                                                                                                                                                    (draft=DRAFT, active=PUBLISHED, deprecated=DEPRECATED, archived=ARCHIVED),
                                                                                                                                                                                                                                                    3 ABSENT (review/approved/revoked — workflow-templates have no review sub-flow,
                                                                                                                                                                                                                                                    the I1.1.a finding). -->
  - [x] I1.1.d Verify
        create/edit/submit/review/approve/activate/deprecate/revoke/
        archive/restore/version transitions and exact authorization/guards.
        Evidence: transition matrix.
        <!-- evidence: .transitionMatrix — 12 canonical transitions, 8 implemented with
                                                                                                                                                                                                                                                    an auth-preHandler guard (requireAuth+permission), 4 absent (submit/review/
                                                                                                                                                                                                                                                    approve/revoke). Verb-synonyms resolved (edit=update(), version=createVersion()). -->
  - [ ] I1.1.e Test invalid source/target, missing evidence, stale revision,
        incompatible dependency, revoked model/node, in-use template, and
        unauthorized/system actor. Evidence: known-value negative tests.
        <!-- SUPERSEDED (2026-08-16). The old note said this was "runtime:
        writing known-value NEGATIVE tests that execute against the running
        workflow service". IT IS NOT RUNTIME, in the same way I0.1.c was not.
        `WorkflowServiceImpl` picks an in-memory backend from
        `ISIS_WORKFLOW_REGISTRY_STORAGE`, so the REAL service — the same class
        `apps/isis/workflow-registry` mounts — runs in-process with no database
        and no HTTP, and the negative tests execute against it.

        evidence/v1-workbenches/isis-workflow-transition-probe.json —
        generate-isis-workflow-transition-probe.mts + .test.mts (6 tests, 2
        ratchets, 1 control), wired into inventory + verify. The subject is
        created with `createFromTemplate` from the service's own seeded
        template, so the definition under test is the estate's.

        EVERY ONE OF THE TWELVE ORDERED STATE PAIRS IS ACCEPTED. draft →
        archived, archived → published, published → draft, draft → deprecated:
        the workflow-template state machine has NO transition guard at all. The
        terminal state is not terminal, and a template can be deprecated without
        ever having been published.

        THE GUARDS THAT DO EXIST ARE REAL, WHICH IS WHAT MAKES THE ABSENCE MEAN
        SOMETHING. A stale `expectedUpdatedAt` is refused with
        `WorkflowUpdateConflictError`; a `createVersion` at or below the current
        semver is refused; an empty definition is refused by
        `assertDefinitionValid`; a soft-deleted workflow cannot be updated. The
        write path is capable of refusing — it simply has nothing to say about
        the state machine.

        A NULL RETURN IS A REFUSAL, NOT AN ACCEPTANCE. The first cut read "it
        did not throw" as "the estate performed it", which put a false
        acceptance in the artifact for the soft-deleted case: `update` returns
        `null` there because `get()` filtered the row. The refusal MODE is now
        carried beside the outcome so a throw and a null stay distinguishable.

        THE CELL STAYS `[ ]` ON COVERAGE, NOT ON METHOD. Three of the seven
        cases it names are tested with known values — invalid source/target,
        stale revision, unauthorized actor. FOUR ARE NOT EXPRESSIBLE AT THIS
        LAYER and are recorded as such with reasons rather than as passes: the
        workflow-template model carries no evidence record to be missing; the
        definition names custom nodes and models as strings the service resolves
        against no registry; there is no link to a model or node lifecycle; and
        nothing records that a workflow came from a template, so "in use" is not
        a state the service holds. Whether another layer can express them is the
        open question. -->
  - [ ] I1.1.f Verify immutable version/history/audit/event/active-run/release
        effects and no direct status write bypass. Evidence: integration tests.
        <!-- THE BYPASS HALF IS ANSWERED AND THE ANSWER IS THAT THE BYPASS IS
        THE ONLY PATH (2026-08-16, same probe as I1.1.e). There is no
        `publishWorkflow`, `activateWorkflow`, `deprecateWorkflow` or
        `archiveWorkflow` method on the workflow at all: `update` takes `status`
        from the request and writes it, and `UpdateWorkflowRequestSchema`
        validates that the value is one of the four states and nothing about the
        move. The named methods the I1.1.a census counted —
        `activateVersion`, `deprecateVersion`, `restoreVersion` — belong to
        `WorkflowVersion`, a different entity from the `WorkflowStatus` the
        I1.1.b diagram draws, so the census's verb synonyms attributed a
        version-level transition to the template.

        The immutability half — version/history/audit/event/active-run/release
        effects — is not covered by this probe and stays open. -->
        <!-- runtime: integration tests require the running service + DB. -->
  - [x] I1.1.g Resolve contract/code/UI drift and fail on future
        state/transition additions without tests/diagram update. Evidence: drift
        gate. <!-- evidence: .driftGate — the generator reads the I1.1.a census,
        so a new WorkflowStatus value or implemented transition flows through
        and breaks --check + the coherence assertions (every implemented
        transition must have a matrix row; every code state must map to a
        canonical state) until the diagram is updated. -->

- [ ] I1.2 Verify the canonical model and model-version state machines.
      Evidence: invalid promotion, deprecation, and archive paths fail.
  - [x] I1.2.a Inventory model and model-version states/transitions/guards/
        contracts/stores/events/jobs/UI/tests independently. Evidence: source
        mapping.
        <!-- isis-model-state-machine.json: source mapping of the CURRENT Isis model/
                                                                                                                                                                                                                                                                        model-version state machine from apps/isis/generation-api/src/services/
                                                                                                                                                                                                                                                                        model.service.ts (read ONCE for perf — the workflow gen's per-transition git
                                                                                                                                                                                                                                                                        greps timed out verify). 26 service operations classified: create/edit/delete,
                                                                                                                                                                                                                                                                        intake-upload (generateUploadUrl/completeUpload/cleanupAbandonedUploads),
                                                                                                                                                                                                                                                                        verification (verifyUploadedContent/scanUploadedContentForMalware), delivery,
                                                                                                                                                                                                                                                                        audit/telemetry, ops/health. VERIFICATION FINDING: the model lifecycle is
                                                                                                                                                                                                                                                                        WEAKLY formalized vs the workflow machine — NO typed ModelStatus enum (contrast
                                                                                                                                                                                                                                                                        WorkflowStatus), the intended states (draft/review/approved/active/deprecated/
                                                                                                                                                                                                                                                                        revoked/archived) appear only as SCATTERED string literals across 28 files with
                                                                                                                                                                                                                                                                        no canonical definition, and status transitions ride on update() (no explicit
                                                                                                                                                                                                                                                                        approve/deprecate/revoke methods). 7 ModelType + 8 ModelFormat contracts, 39
                                                                                                                                                                                                                                                                        model UI components, guards/events/stores present, but only 2 model-service
                                                                                                                                                                                                                                                                        test files (thin coverage). generate-isis-model-state-machine.mjs + 6 tests,
                                                                                                                                                                                                                                                                        wired into inventory/verify + CI. I1.2.b (identity/version boundary) and I1.2.c
                                                                                                                                                                                                                                                                        (lifecycle verification) build on this and stay unchecked. -->
  - [x] I1.2.b Define model identity/ownership/license/risk/supported task/
        lifecycle separately from immutable version/checkpoint/artifact/
        evaluation/provider compatibility. Evidence: boundary review. <!--
        evidence: isis-model-identity-boundary.json .boundary — 9 dimensions
        partitioned: MODEL IDENTITY (mutable) =
        ownership/license/risk/supported-task/ lifecycle vs IMMUTABLE VERSION
        (frozen) = checkpoint/artifact/evaluation/ provider-compatibility, each
        modeled dim mapped to real ModelCard/ModelRegistry/ ModelVersion fields
        (validated = drift gate). HONEST GAPS: license/risk/provider-
        compatibility NOT modeled today; lifecycle weak (no ModelStatus enum,
        I1.2.a). generate-isis-model-identity-boundary.mjs (--check gate), 9
        tests. I1.2.c-g need executable tests (runtime) → parent I1.2 stays
        unchecked. -->
  - [ ] I1.2.c Verify model draft/review/approved/active/deprecated/revoked/
        archived lifecycle and model-level default/current version semantics.
        Evidence: model state tests.
        <!-- THE WRITE-PATH CENSUS IS BUILT AND IT ANSWERS PART OF .c, .e AND .g;
        ALL THREE STAY `[ ]`.
        evidence/v1-workbenches/isis-model-writepath.json —
        generate-isis-model-writepath.mjs + .test.mjs (8 tests, 3 ratchets, 2
        controls), wired into inventory + verify. It asks, for each of the 27
        columns `CREATE TABLE model_registry` declares, WHO CAN WRITE IT: the
        INSERT column list, the `if (updates.<field> !== undefined)` SET guards,
        and every literal `UPDATE model_registry SET …` in the module — then
        crosses that with who READS it, who FILTERS or SORTS on it, and whether
        `determineModelReleaseState` requires it.

        `is_verified` HAS A READER, A FILTER, AN API PARAMETER, A GATE AND NO
        WRITER. Declared `BOOLEAN NOT NULL DEFAULT false`; absent from the
        INSERT column list so `create` cannot set it; no `updates.isVerified`
        guard so `update` cannot set it; no direct statement writes it. It is
        read into `ModelRegistryEntry.isVerified`, filtered on through
        `verifiedOnly` which `model.schema.ts` accepts as a coerced boolean, and
        required by `determineModelReleaseState` for `eligible`. SO
        `GET /models?verifiedOnly=true` RETURNS NOTHING, PERMANENTLY, and every
        model created through this service lands on `review` and never on
        `eligible`.

        `star_count` IS THE SAME SHAPE ONE STEP QUIETER: declared `INTEGER NOT
        NULL DEFAULT 0`, read into `starCount`, offered as a `sortBy` value the
        list schema accepts, and written by nothing.

        THE CONTROL THAT KEEPS THIS HONEST. A first cut counted only the guarded
        `update()` clauses and reported `usage_count`, `download_count` and
        `star_count` as unwritable. Two of the three ARE written — by dedicated
        increment statements — and the soft delete writes `deleted_at` and
        `is_active`. A census that misses a writer manufactures exactly the
        finding it exists to detect, so the scan now reads every literal UPDATE
        and a test pins that it sees those writers.

        WHY THESE CELLS STAY OPEN. .c asks for model STATE tests and there is no
        typed status enum to test (the I1.2.a finding); this census establishes
        what the fields can do, not that a lifecycle behaves. .e asks to prevent
        promotion without evidence — the evidence flag has no writer, so the
        negative test cannot even be set up on the positive side. .g asks about
        immutable artifacts and lineage as well as the bypass; only the bypass
        half is answered here, and the answer is that `is_active` rides the
        generic `update()` with no transition command. The model service is
        Postgres-only, so unlike I1.1.e there is no in-memory backend to execute
        the transitions against. -->
  - [ ] I1.2.d Verify version intake/quarantine/scan/evaluate/approve/promote/
        active/deprecate/revoke/archive transitions and actor/guard
        requirements. Evidence: model-version state tests.
        <!-- SIX OF THE TEN STAGES HAVE NO COMMAND, NOT ONE OF THE FOUR THAT
        EXIST REQUIRES AN ACTOR, AND THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-model-version-lifecycle.json —
        generate-isis-model-version-lifecycle.mts + .test.mts (16 tests, 5
        ratchets, 5 controls), `--check` and the tests wired into verify. The
        ten stages are parsed out of THIS cell's own slash-list, so rewording
        the cell stops the build rather than quietly leaving a stage unmeasured.

        THIS ONE IS EXECUTED, WHICH I1.2.c COULD NOT BE. The note there said the
        model service is Postgres-only and so had no backend to run against; the
        local dev stack has a Postgres AND a MinIO, so the probe drives the real
        `ModelService` — the class `apps/isis/generation-api` mounts — with its
        real S3 signing client, its real hash verification and its real malware
        scanner, and records what each stage did. 16 cases, 10 findings, zero
        refusals classified `infrastructure` (see the control below). A version
        is a `model_registry` row keyed `(name, version)` and `getVersions(name)`
        selects the rows sharing a name, so the version lifecycle IS the row
        lifecycle and there is no second table to look for.

        THE SCAN RUNS, REPORTS `clean=false`, AND THE INTAKE TAKES THE FILE
        ANYWAY. `completeUpload` blocks on `threats.filter(t => t.type ===
        'malware')`, and `DEFAULT_MODEL_MALWARE_SCAN_ENGINE` is `'builtin'`.
        `BuiltinSecurityScanner` can emit fourteen threat types — api_key,
        command_injection, credential, encoded_payload, obfuscated_code,
        path_traversal, pii, private_key, sensitive_data, sql_injection, ssrf,
        suspicious_pattern, xss, xxe — and `malware` is not one of them; the
        only `type: 'malware'` in `scanner.ts` is inside `ClamAVScanner`.
        Executed: bytes the engine flagged came back `status="completed"
        clean=false` with 7 threats, 0 of them typed `malware`, and the upload
        completed with `malwareScan.status: 'clean'` written into the audit
        metadata. THE GATE IS NOT MISCONFIGURED, IT IS UNCLOSEABLE ON THE
        DEFAULT ENGINE, and `result.clean` — the scanner's own verdict — is
        never read.

        THE VERIFIED BYTES ARE NOT THE ISSUED BYTES. `POST /models/:id/upload-
        complete` takes `storagePath` from the request body; the zod schema
        checks only that it is a 1–500 character string, and nothing compares it
        to the `models/<id>/<file>` prefix `generateUploadUrl` minted. Every
        check that follows — HEAD size, download, sha256, scan — is then
        performed on whatever object the caller named, so they all pass.
        Executed: version 2.0.0's intake, naming version 1.0.0's object, was
        accepted and 2.0.0's row now points at 1.0.0's artifact. The same call
        on an already-completed version is accepted too, so the artifact binding
        is never sealed.

        NO STAGE REQUIRES AN ACTOR. `register(uploaderId)` is the only mutation
        whose actor has no default, and it accepts the empty string; `update`,
        `delete` and `completeUpload` all default to `'system'`, and every
        mutating route degrades an absent caller to the literal `'anonymous'`.
        Executed: a `completeUpload` with no actor recorded
        `model.upload.completed` by `system`. The 401 is real but it is in the
        chain, not the handler — `authMiddleware` on `/api/*`, mounted only when
        the app config carries `auth`, with `allowAnonymous` true under
        `NODE_ENV=development` — and `POST /models` is the one mutating route
        with no ownership comparison at all.

        THE SIX STAGES WITH NO COMMAND ARE quarantine, evaluate, approve,
        promote, deprecate AND revoke. Not weakly modelled — absent. Executed
        consequences: a version whose intake was refused stays live, active and
        unmarked with the rejected object still in the bucket (no quarantine);
        `update({isVerified:true})` is ACCEPTED and the flag reads back false,
        because `ModelUpdateRequestSchema` has no such field and the update is
        silently dropped (no approve); a hash-verified, malware-scanned version
        reaches release state `review` and stops there (no promote). The
        positive control separates the two possible explanations: with
        `is_verified` set by direct SQL the same predicate returns `eligible`,
        so the predicate is sound and its input has no writer — which is exactly
        what the I1.2.c census found from source and this run demonstrates at
        runtime.

        THE TWO STAGES THAT DO EXIST DO NOT EXIST AS COMMANDS. `active` rides
        the generic `update`, recording the audit action `model.updated`, so
        nothing distinguishes a deactivation from a description edit. `archive`
        is `delete()`, a soft delete setting `deleted_at` and `is_active`; the
        row survives and drops out of `get` and `getVersions`. And no column
        records which version is current: `getVersions` derives it as the first
        active row of an `ORDER BY created_at DESC`, so with 1.0.0 and 2.0.0
        both live it named 2.0.0, and after deactivating 2.0.0 it named 1.0.0.
        DEACTIVATION IS THE ONLY LEVER ON "CURRENT" — promoting an older version
        is not expressible.

        WHAT KEEPS THE PASSES HONEST. Four cases came back refused across three
        classes — `(name, version)` uniqueness, hash mismatch, size mismatch —
        and a BROKEN OBJECT STORE WOULD HAVE PRODUCED THE SAME REFUSALS. Every
        throw is therefore classified by message into a named refusal class, and
        the run aborts if any lands on `infrastructure`; all four classified as
        estate refusals. Two matchers were also narrowed after they lied: route
        handlers are bounded by the NEXT handler of any verb, because slicing
        each POST to the next POST swallowed the GET handlers between them and
        credited `POST /models` with an ownership check that belongs to
        `GET /models/:id`; and the scanner vocabulary is read per class, because
        reading the file whole puts ClamAV's `malware` in the builtin engine's
        list and silently turns the central finding into a pass.

        NOT WIRED INTO `inventory:v1-workbenches`. The write path needs a
        Postgres and an object store, and refuses outright without them rather
        than replacing a real recorded run with an empty one; `--check` without
        them verifies the static half and says so. Reproduce:
        `ISIS_MODEL_PROBE_DATABASE_URL=… ISIS_MODEL_PROBE_S3_ENDPOINT=…
        ISIS_MODEL_PROBE_S3_BUCKET=… ISIS_MODEL_PROBE_S3_ACCESS_KEY=…
        ISIS_MODEL_PROBE_S3_SECRET_KEY=… pnpm exec tsx
        scripts/v1-workbenches/generate-isis-model-version-lifecycle.mts`. -->
  - [ ] I1.2.e Prevent promotion without hash/signature/source/license/security/
        safety/quality/compatibility evidence or with stale/missing results.
        Evidence: promotion negative tests.
        <!-- ONE OF THE EIGHT EVIDENCE KINDS IS GATED AND THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-model-promotion-evidence.json —
        generate-isis-model-promotion-evidence.mts + .test.mts (13 tests, 5
        ratchets, 4 controls), wired into inventory + verify. The eight kinds
        are parsed out of THIS cell's slash-list and the parser also requires
        the "stale/missing results" clause, so half an answer to half a question
        cannot read as a whole one.

        A NEGATIVE TEST NEEDS SOMETHING TO WITHHOLD FROM A DECISION THAT
        CONSULTS IT. There are two promotion decisions, both exported pure
        functions, so the negatives run against the real implementations with
        the estate's own builders producing the subjects. `determineModelRelease
        State` reads exactly three fields — `hash`, `isActive`, `isVerified` —
        and `buildIsisReleaseReadiness` folds the result in as one requirement,
        `model_not_verified`. Of the eight kinds the cell names, ONLY `hash` is
        an input to either. Executed: no hash → `review`, unverified → `review`,
        inactive → `blocked`, everything present → `eligible`, and a named model
        in review makes the readiness gate `block`. Those four passes are what
        make the rest mean something.

        THE ESTATE HAS TWO MODEL-VERSION RECORDS AND THE GATE READS THE THIN
        ONE. `model_registry` — raw SQL, written by `apps/isis/generation-api` —
        carries a hash and one opaque boolean. `model_versions` and
        `model_cards` are Prisma models carrying `licenses`, `hashes`, `safety`,
        `ethics`, `governance`, `evaluation`, `telemetry`, `lineage` and
        `source`: exactly the evidence this cell asks for. NOTHING OUTSIDE THE
        GENERATED CLIENT READS OR WRITES THEM. The seven files that mention the
        names are six generated modules and one test asserting the migration
        adds the tables, each excluded by name and reason rather than silently.
        So license, safety and quality are declared on a record no decision can
        reach; source (`base_model`, `trained_on`) and compatibility
        (`compatible_engines`) are carried on the gated record and read by no
        promotion decision; signature is not modelled anywhere; and security is
        AUDIT-TRAIL-ONLY — the malware scan runs at intake and its summary is
        written into the audit-log `metadata` JSON, where no column carries it
        and no decision can query it.

        THE PREDICATE'S OTHER TWO INPUTS NAME NO EVIDENCE KIND AT ALL.
        `isActive` and `isVerified` match none of the cell's eight words, and
        `isVerified` is the opaque boolean standing in for all of them — the one
        the I1.2.c write-path census found has a reader, a filter, an API
        parameter, a gate and NO WRITER. Together those two facts say the whole
        thing: seven kinds of evidence cannot reach the decision, and the eighth
        channel that could carry them cannot be written.

        STALENESS IS NOT A STATE THIS ESTATE CAN BE IN. Neither decision takes a
        timestamp. Executed: a record last touched in 2019 returns `eligible`,
        the same as a fresh one. Nor could a freshness bound be bolted on to
        what exists — `model_registry` has only `created_at` and `updated_at`,
        and `update()` sets `updated_at = NOW()` on any edit including a
        description change, so the row's only clock is not evidence-scoped.
        `model_versions`, which could carry per-evidence timestamps, is the
        record nothing reaches.

        AND THE MODEL REQUIREMENT PASSES WHEN NO MODEL IS NAMED.
        `const modelVerified = input.model ? input.model.releaseState ===
        'eligible' : true` — absence is permission. `modelId` is optional on
        `IsisControlledGenerationRequest` and `resolveGenerationContext` resolves
        it to `null` when omitted, so the omission path is reachable from the
        public request type. Executed: with no model the gate reports
        `modelVerified=true` and an empty `missingRequirements`. Combined with
        the unwritable `is_verified`, THE MODEL GATE IS UNPASSABLE WHEN A MODEL
        IS NAMED AND VACUOUS WHEN ONE IS NOT.

        Compatibility has one more executed wrinkle: a version with no declared
        compatible engine reaches `eligible` carrying the warning "Model has no
        declared compatible engines." A WARNING IS NOT A REFUSAL.

        CONTROL THAT MATTERED. An unanchored `model_versions` matches
        `athena_cad_model_versions` — a different table in a different domain —
        and would have reported a consumer for the record whose central finding
        is that it has none. The sweep is a fast `git grep -F` narrowed by
        word-anchored patterns in-process, and a test pins both that the
        collision source still exists and that the anchored pattern rejects
        it. -->
  - [ ] I1.2.f Prevent deprecation/archive/revoke that strands active workflows/
        endpoints/runs/releases without replacement/impact/recall/migration.
        Evidence: dependency guard tests.
        <!-- A DEPENDENCY GUARD NEEDS A DEPENDENCY EDGE, AND ALL FOUR EDGES ARE
        STRINGS NOTHING RESOLVES. THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-model-retirement-guard.json —
        generate-isis-model-retirement-guard.mts + .test.mts (13 tests, 5
        ratchets, 5 controls), `--check` and the tests wired into verify. All
        three of the cell's lists — the transitions, the dependents and the
        remedies — are parsed out of the cell separately, and a cell that stops
        naming any one of them stops the build.

        TWO OF THE THREE RETIRING TRANSITIONS HAVE NO COMMAND. There is no
        `deprecate` and no `revoke` on `ModelService`; `archive` resolves to the
        soft `delete()`, whose entire body is `UPDATE model_registry SET
        deleted_at = NOW(), is_active = false WHERE id = $1 AND deleted_at IS
        NULL`. It reads no dependent table, so the count of transitions that
        consult a dependent is 0 of 3 — and two of those three could not consult
        anything because they do not exist.

        THE FOUR DEPENDENTS HOLD FOUR DIFFERENT SHAPES OF STRING AND NOT ONE IS
        A FOREIGN KEY. A staging recipe pins models as `model://name/variant@
        version` URIs in a JSON record (`modelAliases`); an endpoint declares
        model CLASSES (`modelClasses`), validated only for uniqueness; a run
        carries `jobs.input_model TEXT`, whatever the submitter passed; and a
        release carries `outputs.model JSONB`, a snapshot of the name, version
        and hash taken at output time. Two of the four cannot name a version at
        all. NO CODE RESOLVES ANY OF THEM back to a `model_registry` row, and no
        SQL statement anywhere in the repository names `model_registry`
        alongside `jobs`, `outputs` or a workflow table — though all of them
        share one `ISIS_DATABASE_URL`, so the join is physically available and
        simply never written.

        NONE OF THE FOUR REMEDIES IS A CONCEPT THE MODEL SURFACE CARRIES. No
        replacement or `replacedBy` pointer, no impact or blast-radius record,
        no recall or rollback command, no migration plan — across the service,
        its routes and its request schemas, with comments stripped so a header
        naming a remedy cannot stand in for one.

        EXECUTED, AGAINST THE REAL SERVICES. A run was submitted through the
        real `JobService` naming a real model registered through the real
        `ModelService`, and the model was then retired. `delete()` was accepted
        with a `pending` run naming the model; the run row still names it; and
        because a second version shares the name, the reference now denotes
        version 2.0.0 while the run was submitted against 1.0.0. The retirement
        audit entry is `model.deleted` with NO metadata and a `beforeState` of
        the model row alone — nothing anywhere records what the retirement
        affected. THE POSITIVE CONTROL SEPARATES ABSENCE FROM INCAPACITY: a
        second `delete()` of the same version WAS refused, so the write path can
        say no; it just has nothing to say about dependents.

        TWO MATCHERS LIED BEFORE THEY WERE FIXED, AND BOTH WOULD HAVE SOFTENED
        THE FINDING. Searching for the releases reference by its column name
        `model` returned seventy-odd files and reported the reference as
        resolved; the tokens are a conjunction now (`row.model` AND `outputs`),
        with a positive control proving the search still finds a real resolver.
        And the SQL-join sweep matched a backtick literal spanning two unrelated
        templates in `calliope-operations.ts`, where `model_registry` is the
        name of a SECRET PROVIDER, not a table; a statement must now also carry
        a SQL verb and stay under a length no real query exceeds.

        THE PER-REFERENCE MEASUREMENT IS A FLOOR and says so in the artifact: a
        file resolving one of these references under a different alias would not
        be counted. The join sweep is independent of aliasing and agrees.

        OUT OF CELL, FOUND WHILE BUILDING THIS. `JobService.initialize()` throws
        on the default configuration: `ISIS_QUEUE_PRODUCER_ENABLED` defaults to
        true and `DEFAULT_QUEUE_NAME` is `'isis:jobs:submit'`, which BullMQ
        rejects outright with "Queue name cannot contain :". Production is not
        affected — `resolveQueueProducerConfig` requires `ISIS_QUEUE_NAME` there
        — but every non-production boot that does not set it fails to start the
        job service. The probe sets `ISIS_QUEUE_PRODUCER_ENABLED=false` because
        the queue is not this cell's subject; the defect belongs to the job
        dispatch cells and is recorded here so it is not lost. -->
  - [ ] I1.2.g Verify immutable version artifacts/lineage/audit/events and no
        direct status or current-version bypass. Evidence: real integration
        tests.
        <!-- ONE OF THE FOUR SUBJECTS IS IMMUTABLE, NEITHER BYPASS IS GUARDED,
        AND THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-model-immutability-ledger.json —
        generate-isis-model-immutability-ledger.mts + .test.mts (12 tests, 5
        ratchets, 3 controls), `--check` and the tests wired into verify. The
        four subjects and both bypasses are parsed out of the cell.

        ABSENCE IS NOT IMMUTABILITY, AND THE LEDGER REFUSES TO MERGE THEM. A
        thing with no carrier cannot be mutated; scoring that as immutable would
        turn a missing record into a pass. Each subject is `immutable`,
        `mutable` or `absent`. ARTIFACTS: mutable. LINEAGE: mutable. AUDIT:
        immutable — the one real pass. EVENTS: absent.

        THE ARTIFACT COLUMNS ARE HELD AGAINST THE GENERIC UPDATE AND REWRITTEN
        BY THE INTAKE PATH. Executed: `update({storagePath, sizeBytes, hash})`
        left all three columns unchanged, because `ModelUpdateRequestSchema`
        has no such fields and the update silently drops them. The only writer
        is `completeUpload`, which the I1.2.d run showed accepts a second call
        on an already-completed version AND a storage path belonging to a
        different version. Those two cases are CITED BY ID out of
        `isis-model-version-lifecycle.json` and re-read at build time, so a fix
        over there changes this artifact and breaks this cell's ratchet rather
        than leaving a stale sentence behind.

        THE ONLY ANCESTRY A VERSION CARRIES IS TWO FREE-TEXT FIELDS THE GENERIC
        UPDATE REWRITES. Executed: `update({baseModel, trainedOn})` rewrote both
        to arbitrary strings. Neither is resolved against a registered model
        (I1.2.f measured that for every model reference in the estate), so a
        version's recorded parentage is an editable label.

        THE AUDIT TABLE IS APPEND-ONLY, EVERYWHERE. No `UPDATE` and no `DELETE
        FROM model_registry_audit_log` exists in any tracked `.ts`, `.tsx` or
        `.sql` file; the service only inserts and selects; and entries carry a
        before and an after state. That is a genuine pass and it is what makes
        the rest of the ledger mean something. BUT NOT EVERY WRITE IS AUDITED:
        executed, `recordUsage()` incremented `usage_count` with the audit count
        unchanged and `updated_at` NOT moving, so a column changes with no entry
        and no clock. (A first cut compared `updated_at` against the row as it
        stood at registration, when two earlier `update()` calls had already
        moved it, and reported a clock that `recordUsage` does not touch; the
        snapshot is now taken immediately before the write under test.)

        NO DOMAIN EVENT IS PUBLISHED ON THE MODEL PATH AT ALL. Four audit
        actions go into a table and nothing goes onto a bus. The sibling
        `JobService` in the same app holds an `eventPublisher`, so the silence
        is a choice rather than a platform limit, and the ledger records the
        contrast.

        NEITHER BYPASS IS GUARDED, FOR TWO DIFFERENT REASONS, AND THE VERDICTS
        KEEP THEM APART. `status`: the only status a version has is `is_active`,
        it is a field of the generic update request, and no method on the
        service names activate, deprecate, revoke or transition — so THE BYPASS
        IS THE ONLY PATH. `current-version`: the live table declares zero
        columns naming current or default, so there is NOTHING TO BYPASS —
        which is not the same as guarded, and is the other face of I1.2.d's
        finding that the current version is derived as the first active row of
        an `ORDER BY created_at DESC`. Executed against `information_schema`
        rather than the DDL text, so the claim is about the table that exists.

        The one thing the trail does get right: when `is_active` flips, the
        entry is the generic `model.updated`, but its `changedFields` names
        `isActive` and its before and after states do record the flip. The
        transition is unnamed, not unrecorded.

        Reproduce: `ISIS_MODEL_PROBE_DATABASE_URL=… pnpm exec tsx
        scripts/v1-workbenches/generate-isis-model-immutability-ledger.mts`. The
        write path refuses without a database rather than replacing a real
        recorded run with an empty one; `--check` verifies the static half and
        says so, which is why the writer is not in `inventory:v1-workbenches`. -->
- [ ] I1.3 Verify provider-endpoint lifecycle and health semantics. Evidence:
      state/health combinations have deterministic dispatch eligibility.
  - [x] I1.3.a Inventory endpoint configuration/lifecycle/health/circuit/
        maintenance/capability/capacity states, probes, routing, APIs/stores/UI,
        and tests. Evidence: source mapping.
        <!-- isis-endpoint-state-machine.json: source mapping of the CURRENT Isis
                                                                                                                                                                                                                                                                        provider-endpoint state machine (libs/isis/ai-providers/src/provider-endpoints;
                                                                                                                                                                                                                                                                        registry/health-monitor/router/dead-letter/queue-metrics read ONCE each for
                                                                                                                                                                                                                                                                        perf). 4 typed health states (ISIS_ENDPOINT_HEALTH_STATES: healthy/degraded/
                                                                                                                                                                                                                                                                        unhealthy/unknown), 9 provider kinds (ISIS_PROVIDER_KINDS), 7 regions, 6 typed
                                                                                                                                                                                                                                                                        config interfaces (IsisProviderEndpoint/CostControls/ConcurrencyControls/
                                                                                                                                                                                                                                                                        RetryPolicy/SecretRotation/Registry). All 9/9 I1.3.a dimensions map to typed
                                                                                                                                                                                                                                                                        evidence: configuration→IsisProviderEndpoint, lifecycle→health-states,
                                                                                                                                                                                                                                                                        health→health-monitor probes, circuit→dead-letter, maintenance→SecretRotation,
                                                                                                                                                                                                                                                                        capability→provider-kinds, capacity→concurrency/cost controls, probes→
                                                                                                                                                                                                                                                                        health-monitor, routing→router. POSITIVE FINDING: unlike the weakly-formalized
                                                                                                                                                                                                                                                                        model machine (I1.2.a), the endpoint machine is WELL-TYPED. 16 endpoint/provider
                                                                                                                                                                                                                                                                        UI components, 3 provider-endpoints test files (both-globs fix caught top-level
                                                                                                                                                                                                                                                                        specs the ** glob misses). generate-isis-endpoint-state-machine.mjs + 6 tests,
                                                                                                                                                                                                                                                                        wired into inventory/verify + CI. I1.3.b+ (config/lifecycle/circuit verification)
                                                                                                                                                                                                                                                                        build on this and stay unchecked. -->
  - [x] I1.3.b Define configured/unconfigured/validating/ready/draining/
        maintenance/disabled/retired lifecycle and authorized transitions.
        Evidence: lifecycle state machine. <!-- evidence:
        isis-endpoint-dispatch-model.json .lifecycle — 8 states
        (unconfigured/configured/validating/ready/draining/maintenance/disabled/
        retired) + 11 authorized transitions with actor (admin/operator/system);
        only `ready` is dispatch-eligible.
        generate-isis-endpoint-dispatch-model.mjs (--check gate), 14 tests. -->
  - [x] I1.3.c Define probe unknown/healthy/degraded/unhealthy/unavailable/
        incompatible/stale states with checked time, latency/error/capacity/
        supported operation/model detail. Evidence: health contract.
        <!-- evidence: .healthContract — 7 probe states (4 realized in code per I1.3.a:
                                                                                                                                                                                                                                            healthy/degraded/unhealthy/unknown; 3 contract additions: unavailable/
                                                                                                                                                                                                                                            incompatible/stale) each with 6 detail fields (checkedAt/latencyMs/errorRate/
                                                                                                                                                                                                                                            capacity/supportedOperations/model). 4 code states ⊆ 7 contract (validated). -->
  - [x] I1.3.d Define circuit closed/open/half-open and throttle/quota/capacity/
        regional/license/dependency state separately from health. Evidence:
        dispatch input schema.
        <!-- evidence: .dispatchInputSchema — circuit (closed/open/half-open) +
                                                                                                                                                                                                                                            throttle/quota/capacity/regional/license/dependency, defined SEPARATELY from
                                                                                                                                                                                                                                            the health contract (health is not a field of the dispatch schema). -->
  - [x] I1.3.e Produce a deterministic eligibility/routing/refusal reason table
        for every lifecycle×health×circuit×maintenance×capability×capacity
        combination. Evidence: truth table tests.
        <!-- evidence: .eligibility — a DETERMINISTIC, TOTAL decision function over all
                                                                                                                                                                                                                                            1344 combinations (8×7×3×2×2×2): 4 eligible, the rest refused with 1 of 10
                                                                                                                                                                                                                                            priority-ordered reasons (lifecycle→maintenance→circuit→health→capability→
                                                                                                                                                                                                                                            capacity), ALL reachable (checked by full cross-product enumeration). The
                                                                                                                                                                                                                                            "truth table tests" are pure-function known-value tests (no running service). -->

  - [ ] I1.3.f Recheck endpoint state/health/capacity/model compatibility/
        entitlement/budget at dispatch reservation and handle races. Evidence:
        dispatch integration tests.
        <!-- NOT ONE OF THE SIX DIMENSIONS IS RECHECKED AT DISPATCH; THE CELL
        STAYS `[ ]`.
        evidence/v1-workbenches/isis-dispatch-recheck.json —
        generate-isis-dispatch-recheck.mjs + .test.mjs (7 tests, 2 ratchets, 2
        controls), wired into inventory + verify. The six dimensions are read
        from THIS cell's own words and the build stops if the cell stops naming
        one of them.

        THE DISPATCH GUARD IS REAL, FAIL-CLOSED AND WIRED — AND IT GUARDS THE
        OUTPUT, NOT THE ENDPOINT. `evaluateIsisDispatch` denies on a block, a
        review, a gate error or any denied admission, and
        `apps/oshun/bff/src/generation/release-gate.ts` calls it. But every
        field of the `CanonicalReleaseGateMeasurement` it receives is about the
        artifact — safety-scan score, provenance manifest, watermark coverage,
        synthetic-voice disclosure, quality aggregate, policy compliance, rights
        and licence, output shape. Endpoint state, health, capacity, model
        compatibility, entitlement and budget are not among them, and the BFF
        caller names none of them either. The guard is a pure function of its
        argument, so a dimension it cannot see cannot be re-read however
        carefully it was computed at plan time.

        SO THE ELIGIBILITY TABLE I1.3.e BUILT IS COMPUTED ONCE AND NEVER
        RECHECKED. The plan records `providerEndpointId`, `canaryEndpointId`,
        `modelVersion` and `estimatedCostUsd`; `dispatchGuardedGeneration` then
        calls the guard and, if permitted, calls `runGeneration`. Between those
        two moments an endpoint can drain, a circuit can open, capacity can fill
        and a budget can be spent, and nothing looks again.

        TWO OF THE SIX ARE ABSENT FROM BOTH SIDES. Health and capacity are not
        fields of the plan either, so they are not merely un-rechecked — the
        control plane never carries them into the decision at all, though I1.3.c
        and I1.3.d defined probe states and concurrency controls for them.

        AND THE RACE HALF IS UNADDRESSED: no reservation, no lock, no
        compare-and-set, no idempotency key anywhere on the dispatch path. The
        estate does know how — `WorkflowServiceImpl.update` refuses a stale
        `expectedUpdatedAt` — so this is a gap, not a limitation.

        TWO MATCHERS HAD TO BE NARROWED BEFORE ANY OF THIS WAS TRUE. An
        unanchored `queue` matched `enqueueGenerationJob` in a header comment
        and credited the guard with a capacity recheck it does not make; an
        unanchored `lock` matched `blockedReasons`, which is on the dispatch
        path in quantity, and reported a lock where there is none. Comments are
        stripped before matching, the race vocabulary is word-anchored, and a
        control test pins that both patterns still match what they are for. -->
  - [ ] I1.3.g Verify drain/disable/maintenance/failover/recovery behavior for
        queued/in-flight/retry work and unknown external outcomes. Evidence:
        fault tests.
        <!-- THREE OF THE FIVE EVENTS ARE NOT STATES AN ENDPOINT CAN ENTER, AND
        THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-endpoint-fault-behaviour.json —
        generate-isis-endpoint-fault-behaviour.mts + .test.mts (12 tests, 5
        ratchets, 4 controls), wired into inventory + verify. Every endpoint
        module is pure, so the FAULT TESTS the cell asks for execute the real
        exported functions with no service and no database, and the endpoint
        fixtures are checked by the registry's own `validateIsisProviderEndpoint`
        before use.

        DRAIN, DISABLE AND MAINTENANCE ARE NOT IN THE CODE AT ALL. Not weakly
        modelled — absent: no field of `IsisProviderEndpoint`, no member of
        `ISIS_ENDPOINT_HEALTH_STATES`, and no identifier in any of the five
        modules names them. I1.3.b defined an eight-state lifecycle
        (unconfigured/configured/validating/ready/draining/maintenance/disabled/
        retired) as a CONTRACT; the endpoint the router reads carries `health`
        and nothing else. Executed: routing to a `degraded` endpoint is GRANTED,
        so the only lever that stops new work is `unhealthy`, which is a
        statement about how the endpoint is behaving and not a decision anyone
        made. There is no way to say "stop sending, finish what you have".

        WHAT DOES WORK, WORKS, AND THAT IS WHAT MAKES THE REST MEAN SOMETHING.
        Executed: an `unhealthy` endpoint is refused with
        `considered=[primary:unhealthy]`; in-flight at the concurrency ceiling
        is refused; and a failed primary routes through to its declared
        companion with reason `failover-companion-match`.

        QUEUE DEPTH IS MEASURED AND NEVER CONSULTED. `IsisQueueMetrics` reports
        depth, lag percentiles, throughput and success ratio per endpoint;
        `IsisRouteRequest` has no field for any of it. Executed: with a depth of
        2 recorded for the endpoint, the router granted the route.

        A TIMEOUT IS AN UNKNOWN OUTCOME AND THE ESTATE TREATS IT AS A FAILURE.
        `decideRetry` retries a timeout — executed, `kind="retry"` after 100ms —
        and neither the retry decision nor the route request carries an
        idempotency key, which matches I1.3.f's finding that there is none
        anywhere on the dispatch path. So a provider call that succeeded and
        whose answer was lost is retried, and the second attempt is
        indistinguishable from the first.

        AND THE VOCABULARY FOR "UNKNOWN" EXISTS WHERE NOTHING IS AT STAKE.
        `HEALTH_ERROR_KINDS` has seven words INCLUDING `unknown`, so a probe can
        report one. `RETRY_ERROR_KINDS` has four and `unknown` is not among
        them: executed, `decideRetry({errorKind:'unknown'})` throws
        `failure.errorKind "unknown" is not canonical`. THE ONE PLACE THAT CAN
        SAY IT IS THE ONE PLACE WHERE NO WORK IS PENDING.

        A LIVE DEFECT IN THE RECOVERY PATH. `evaluateEndpointHealth`'s doc
        comment says "Newest outcomes first"; its body is
        `.slice(-window.samples)`, which takes the LAST entries. Executed: the
        same eight probes — three failures then five successes — give
        `healthy` when passed oldest-first and `unhealthy` when passed
        newest-first. The one in-repo caller, `createHealthMonitorController`,
        appends to the tail and is therefore correct; ANY CALLER OBEYING THE
        DOCUMENTED CONTRACT IS JUDGED ON THE STALEST PROBES. Recovery also has
        no hysteresis and no minimum time in state: the window flips as soon as
        the ratio crosses, recorded as an observation rather than a finding
        because the cell asks what recovery does, not what it should be.

        AND ALMOST NOTHING CALLS ANY OF IT. Of seven entry points —
        `planIsisRoute`, `evaluateEndpointHealth`,
        `evaluateEndpointHealthBatch`, `createHealthMonitorController`,
        `decideRetry`, `buildDeadLetterEntry`, `IsisQueueMetrics` — ONE has a
        caller outside its own module and its tests, and that caller is
        `libs/isis/study-adapter/src/adapter.ts`, whose package
        `@isis/study-adapter` is imported by nothing. The second hop is
        MEASURED, not asserted, and an unmeasurable hop is flagged rather than
        reported as empty: `@isis/ai-providers` has only subpath mappings in
        `tsconfig.base.json` and no root one, so a caller living there would
        resolve to null, and the artifact carries a field that says so.
        `decideRetry` is also a name three unrelated libraries export, so a
        caller only counts if it imports the Isis package — a control pins that
        Euterpe's own `decideRetry` is not miscounted.

        I1.3.a called this machine WELL-TYPED and it is. The question it did not
        ask is whether anything runs it. -->

- [x] I1.4 Define a generation brief with intent, audience, asset kind, locale,
      references, rights assertions, safety context, quality targets, budget,
      and delivery target. Evidence: schema invariants. <!-- evidence: COMPILING
      reference evidence/v1-workbenches/generation-brief/\*.ts (schema.ts 11
      facet interfaces a-k + validate.ts cross-field suite l + fixtures.ts 1
      valid/8 invalid + check.ts runnable), strict tsc --noEmit + invariant
      suite run via tsx (both gated in verify:inventory:v1-workbenches).
      a=BriefIdentity, b=BriefIntent(+intent fixture),
      c=BriefAudience(+validateAudience Entitlement),
      d=BriefAssetKind(+validateAssetKind, grounds to GenerationType I0.5.a),
      e=BriefLocale, f=BriefReference(+reference fixture), g=RightsClaim as
      evidence-backed claims (verified rejected without evidenceRef — NOT client
      truth), h=BriefSafety(+ validateSafety: minor-present⇒reviewer+consent),
      i=QualityTarget, j=BriefBudget (no fabricated cost — nullable ceiling),
      k=BriefDelivery, l=validateBrief cross-field suite.
      generate-generation-brief-schema.mjs (--check gate), 9 tests. -->
  - [x] I1.4.a Define stable brief ID/version, tenant/project/parent context,
        author/delegate, lifecycle, base revision, title/summary, and
        created/updated metadata. Evidence: brief identity schema.
  - [x] I1.4.b Define structured creative/production intent, goal/non-goals,
        subject/content/action/context/style/tone, required/excluded elements,
        and domain extension blocks. Evidence: intent fixtures.
  - [x] I1.4.c Define audience/tier/end user/age/region/channel/purpose/
        sensitivity and verify it against actor/host entitlement. Evidence:
        audience tests.
  - [x] I1.4.d Define asset-kind/profile/operation/count/variant/group/
        composite relationship and kind-specific typed control selection.
        Evidence: asset-kind invariant tests.
  - [x] I1.4.e Define content/UI/source/target locale/language/script/direction/
        cultural context and localizable versus stable identifiers. Evidence:
        locale tests.
  - [x] I1.4.f Define ordered reference roles/weights/scopes/hashes/versions/
        rights/consent/provenance/availability and conditioning bindings.
        Evidence: reference fixtures.
  - [x] I1.4.g Define rights/license/ownership/attribution/territory/term/
        derivative/training/publication assertions as claims requiring evidence,
        not client truth. Evidence: rights validation tests.
  - [x] I1.4.h Define safety context for people/identity/age/biometric/voice/
        sensitive content/policy/risk/reviewer/consent and required
        restrictions. Evidence: safety fixtures.
  - [x] I1.4.i Define asset/delivery-specific raw quality targets/units/
        thresholds/tolerance/priorities/evaluator/profile versions and
        unavailable behavior. Evidence: quality target tests.
  - [x] I1.4.j Define currency/rate-card/estimate ceiling/tenant-project budget/
        latency/resource/carbon or other approved constraints without fabricated
        cost. Evidence: budget tests.
  - [x] I1.4.k Define delivery target/host/channel/territory/environment/format/
        profile/dimensions/time/audio/3D package/naming/deadline/embargo/
        publisher and compatibility. Evidence: delivery target tests.
  - [x] I1.4.l Enforce cross-field authorization/compatibility/safety/rights/
        budget/quality/delivery invariants and retain valid/invalid/full
        fixtures. Evidence: schema invariant suite.
- [x] I1.5 Define a resolved generation plan distinct from the user brief.
      Evidence: plan records workflow/model/provider choices and every reason.
      <!-- evidence: COMPILING reference
      evidence/v1-workbenches/generation-plan/\*.ts (plan-schema.ts +
      resolve.ts + fixtures.ts + check.ts), imports the I1.4 brief; strict tsc
      --noEmit + plan suite via tsx (both gated in verify). a=PlanIdentity
      (immutable: planHash+version+exact briefRevision), b=resolveBriefToPlan +
      NormalizedIntent w/ ResolvedField.source
      (user/default/derived/constrained/ unsupported — over-limit count
      constrained to 8), c/d/e=Decision<Workflow/Model/ Provider Choice>
      recording chosen + rejected alternatives WITH reasons (every reason),
      f=ExecutionGraph, g=PlanBudget (per-stage estimate/reservation/uncertainty
      +stopConditions), h=validatePlan + 6 invalid fixtures, i=createReplan +
      ReplanLink (original kept IMMUTABLE, replanPreservesOriginal).
      generate-generation-plan-schema.mjs (--check gate), 8 tests. -->
  - [x] I1.5.a Define immutable plan ID/version, originating exact brief
        revision, resolver/policy/catalog versions, actor/job/time, status,
        expiry, and plan hash. Evidence: plan identity schema.
  - [x] I1.5.b Record normalized intent and every default/derived/unsupported/
        constrained field separately from user-authored brief values. Evidence:
        brief-to-plan mapping tests.
  - [x] I1.5.c Record chosen workflow/template/version/graph/class/node/
        parameter profile and compatible alternatives/rejections with reasons.
        Evidence: workflow decision fixtures.
  - [x] I1.5.d Record chosen model/version/checkpoint/adapter/LoRA/scheduler/
        preprocessor/evaluator and alternatives/rejections with compatibility/
        policy reasons. Evidence: model decision fixtures.
  - [x] I1.5.e Record chosen provider/endpoint/region/resource/queue/failover/
        priority/capacity reservation and alternatives/rejections with health/
        entitlement/latency/cost reasons. Evidence: provider decision fixtures.
  - [x] I1.5.f Record transformed/resolved inputs/references/conditioning,
        output kinds/counts/formats/bundles, post-processing, gates/review/
        provenance/watermark/publisher steps. Evidence: execution graph.
  - [x] I1.5.g Record per-stage estimate/reservation, resource/time/cost ranges,
        uncertainty, budget checks, and stop conditions. Evidence: plan budget
        tests.
  - [x] I1.5.h Validate plan against current actor/tier/host,
        contracts/versions, compatibility, config/health, rights/consent/safety,
        capacity/budget, and delivery before approval/dispatch. Evidence:
        invalid plan tests.
  - [x] I1.5.i Keep plans immutable and create linked replan versions when any
        brief/dependency/policy/provider/capacity/target input changes.
        Evidence: replan lineage tests.
- [x] I1.6 Define immutable generation execution records. Evidence: seed,
      parameters, inputs, policy, provider response, costs, outputs, and timings
      are retained. <!-- evidence: COMPILING reference
      evidence/v1-workbenches/execution-record/\*.ts (execution-schema.ts 9
      facets + record.ts validation/append-only + fixtures.ts 1 valid/7
      invalid + check.ts), strict tsc --noEmit + record suite via tsx (both
      gated in verify). a=ExecutionIdentity (pins exact plan/brief revisions),
      b=Runtime Provenance, c=ParameterRecord (seed/subseeds/latents/typed
      params/parameterHash), d=InputArtifact (ordered+hash+role),
      e=PolicyProvenance (decisions+dispatchEligible; failed⇒not-eligible),
      f=ProviderRecord (secure REFS not inlined secrets), g=CostRecord (actual
      NULLABLE — no fabricated cost; variance rejected without settlement),
      h=OutputRecord (noOutputReason), i=TimingRecord (clock/unit),
      j=appendCorrection + CorrectionLink (NEW linked record, original
      IMMUTABLE + legal hold, correctionPreserves Original).
      generate-execution-record-schema.mjs (--check gate), 9 tests. -->
  - [x] I1.6.a Define execution/run/attempt/stage IDs, exact plan/brief
        revisions, actor/tenant/project/host/tier, job/correlation/trace,
        lifecycle, and timestamps. Evidence: execution identity schema.
  - [x] I1.6.b Record resolved workflow/graph/template/node/model/checkpoint/
        adapter/LoRA/scheduler/provider/endpoint/resource/environment/container/
        dependency versions. Evidence: runtime provenance tests.
  - [x] I1.6.c Record seed/subseeds/latent refs and exact typed stage
        parameters/ defaults/derived values with units and immutable hashes.
        Evidence: parameter tests.
  - [x] I1.6.d Record ordered immutable input/reference/conditioning/mask/
        source artifact refs, hashes, roles, transformations, rights/consent,
        and availability. Evidence: input tests.
  - [x] I1.6.e Record safety/rights/consent/quality/release policy, threshold,
        gate/evidence/waiver versions, decisions, and dispatch eligibility.
        Evidence: policy provenance tests.
  - [x] I1.6.f Securely retain or reference raw provider request/response/
        stream/tool events with IDs/hashes/status/finish/safety/usage/error/
        cancellation and normalized adapter version. Evidence: provider record
        tests.
  - [x] I1.6.g Record estimate/reservation/actual/refund/variance cost and
        token/ GPU/CPU/memory/storage/egress/provider/resource usage per
        stage/attempt. Evidence: cost tests.
  - [x] I1.6.h Record intermediate/partial/quarantined/rejected/final output
        refs/hashes/metadata/state/provenance and no-output reason. Evidence:
        output tests.
  - [x] I1.6.i Record queued/leased/started/first output/stage/checkpoint/
        completed/cancelled/failed/retry/total timing with clock/unit semantics.
        Evidence: timing tests.
  - [x] I1.6.j Enforce append-only/immutable attempts with access/retention/
        deletion/legal hold and new linked record for corrections/replay.
        Evidence: immutability/lifecycle tests.
- [ ] I1.7 Define output and ingredient lineage. Evidence: composites retain
      ordered relationships to every source and generated ingredient.
  - [x] I1.7.a Define lineage node types for source/reference/input,
        intermediate, generated output, edited derivative, composite, bundle,
        provenance manifest, and release with exact revision/hash. Evidence:
        node schema. <!-- evidence (I1.7.a/b/d/e/f/g): COMPILING reference
        evidence/v1-workbenches/ lineage/\*.ts (lineage-schema.ts 10 NodeType +
        9 EdgeType w/ role/weight/mask/ time/range/part + EdgeProvenance;
        graph.ts traverse/computeImpact/computeRecall/
        validateIntegrity/ingredientUses; fixtures.ts composite graph;
        check.ts). a=LineageNode, b=LineageEdge, d=ingredientUses (src used 2x
        at distinct layers, equal hash NOT collapsed), e=EdgeProvenance
        (run/stage/actor/rights per edge), f=cycle-safe bounded traversal
        (impact/recall), g=validateIntegrity (dangling/
        cross-tenant/cross-brief/hash-mismatch rejected; inaccessible reported
        honestly). strict tsc --noEmit + lineage suite via tsx gated in verify.
        generate-lineage- schema.mjs (--check gate), 10 tests. -->
  - [x] I1.7.b Define typed directional ordered edges for uses/conditions/
        derives/transforms/composites/mixes/layers/supersedes/publishes with
        role/weight/mask/time/range/part metadata. Evidence: edge schema.
  - [ ] I1.7.c Create edges transactionally for ingest, planning, execution
        stages, post-process, manual edit, variant merge, bundle, promotion, and
        publication. Evidence: mutation integration tests.
        <!-- SUPERSEDED (2026-08-16). The old note said "runtime: requires the
        running service + DB". WHETHER A WRITE IS INSIDE A TRANSACTION IS A
        PROPERTY OF THE SOURCE.
        evidence/v1-workbenches/isis-lineage-write-transactionality.json —
        generate-isis-lineage-write-transactionality.mjs + .test.mjs (7 tests,
        1 ratchet, 2 controls), wired into inventory + verify. Every statement
        in `apps/isis/output-registry` that writes a lineage-bearing column is
        located and scoped against the `withTransaction` callback bodies,
        BRACE-MATCHED rather than by proximity, because a write a few lines
        below a transaction that has already closed is not inside it.

        THE LINEAGE GRAPH HAS TWO WRITERS AND ONLY ONE OF THEM IS
        TRANSACTIONAL. `addLineageEdge` wraps its `INSERT INTO lineage_edges`
        and the accompanying audit record in one `withTransaction`, so an
        explicit edge and its audit trail land together or not at all.
        `register` — the path that creates an output WITH parents — does not: it
        inserts the output row carrying `parent_ids`, then issues a separate
        `UPDATE outputs SET child_ids = array_append(…)` against the parents,
        then writes the audit record, as THREE UN-TRANSACTED STATEMENTS.

        SO A FAILURE BETWEEN THEM LEAVES THE GRAPH ASYMMETRIC: a child that
        names its parents while the parents do not name the child. Both halves
        are read back — `getLineage` walks `parentIds` for ancestors and
        `childIds` for descendants — so the two directions can disagree about
        the same link, and the direction a caller asks for decides which answer
        they get.

        A SINGLE UN-TRANSACTED STATEMENT IS NOT THE DEFECT; A GROUP IS. The
        census reports per METHOD, and a method with one write is atomic by
        itself. `register` has three that must all land or none.

        AND THE CELL NAMES NINE STAGES WHILE THE ESTATE HAS TWO WRITERS.
        Ingest, planning, execution, post-process, manual edit, variant merge,
        bundle, promotion and publication are the moments an edge must be
        created transactionally; the output registry offers `register` and
        `addLineageEdge`, and nothing in it distinguishes which stage a caller
        is in. The cell stays `[ ]` on both counts. -->
  - [x] I1.7.d Preserve each duplicate use and ordering/layer/track/shot/part
        relationship where semantically meaningful without collapsing equal
        hashes. Evidence: composite fixtures.
  - [x] I1.7.e Link edge to run/stage/action/tool/model/policy/actor/time and
        rights/consent/license/attribution/provenance evidence. Evidence:
        provenance tests.
  - [x] I1.7.f Traverse exact upstream/downstream paths and compute impact/
        recall with cycle/fan-out/depth/pagination and redaction. Evidence:
        graph tests.
  - [x] I1.7.g Validate no missing/dangling/cross-tenant/cross-brief-invalid/
        hash-mismatched ingredient and represent inaccessible/deleted/unknown
        honestly. Evidence: integrity tests.
- [ ] I1.8 Define variant groups and comparison membership. Evidence: variants
      cannot cross tenant, brief, or incompatible asset kinds.
  - [x] I1.8.a Define group ID/version, tenant/project, exact brief revision,
        purpose/strategy, asset kind/profile, base/source, owner, lifecycle, and
        limits. Evidence: group schema. <!-- evidence (I1.8.a-f): COMPILING
        reference evidence/v1-workbenches/variant-group/\*.ts (variant-schema.ts
        VariantGroup+VariantMember + append-only history; variant-ops.ts
        validateMembership/validateCompatibility/addMember/removeMember/freezeGroup/
        freezeComparison/opPreservesInput; fixtures.ts + check.ts).
        a=VariantGroup (tenant/ project/exact-brief scoped), b=VariantMember
        (source provenance), c=membership rejects cross-tenant/cross-brief,
        d=compatibility rejects incompatible kind w/o adapter, e=IMMUTABLE
        lifecycle (new version + append-only history + optimistic
        expectedVersion, opPreserves Input), f=freezeComparison pins
        revision/hash + represents unavailable/stale/redacted honestly. strict
        tsc + variant suite via tsx gated. generate-variant-group-schema.mjs
        (--check), 9 tests. -->
  - [x] I1.8.b Define member ID/order/label/source
        manual-or-run/plan/run/output/ revision/hash, state, provenance, created
        actor/time, and archive. Evidence: member schema.
  - [x] I1.8.c Validate same tenant/project/authorized scope and exact brief or
        explicitly compatible fork lineage. Evidence: tenancy/brief negative
        tests.
  - [x] I1.8.d Validate asset-kind/profile/semantic/time-base/dimension/channel/
        3D coordinate compatibility or require typed cross-kind adapter.
        Evidence: compatibility tests.
  - [x] I1.8.e Support
        add/remove/reorder/rename/archive/restore/freeze/duplicate/
        merge-derived member with expected revision and immutable history.
        Evidence: lifecycle tests.
  - [x] I1.8.f Freeze comparison input revisions/hashes/adapters/score/gate
        versions and represent unavailable/stale/redacted member. Evidence:
        stable compare tests.
  - [ ] I1.8.g Link selection/rejection/decision/rationale/evidence and preserve
        all considered members. Evidence: decision integration tests.
        <!-- SUPERSEDED (2026-08-16). The old note said this needs "the running
        comparison service" and that the freeze contract "already preserves all
        considered members". The variant-group module is PURE, so the decision
        questions are function calls; and the preserve claim holds for the LIVE
        set, which is not the same thing.

        NONE OF THE FIVE ELEMENTS THE CELL NAMES EXISTS, AND THE CELL STAYS
        `[ ]`.

        evidence/v1-workbenches/isis-variant-decision-record.json —
        generate-isis-variant-decision-record.mts + .test.mts (9 tests, 3
        ratchets, 3 controls), wired into inventory + verify. Selection and
        rejection are asked for as STATES, decision as an EVENT, rationale as a
        FIELD on that event and evidence as a LINK from it — separately,
        because a schema can have one without the others and collapsing them
        would let one present element stand in for four absent ones.

        THE NINE OPS ARE add, archive, duplicate, freeze, merge-derived, remove,
        rename, reorder, restore. THE FIVE MEMBER STATES ARE active, archived,
        redacted, stale, unavailable. Not one names a selection or a rejection.
        A history entry carries exactly `[actorId, atVersion, memberId, op]` —
        THERE IS NOWHERE TO PUT A REASON. A frozen member carries `[adapter,
        availability, gateVersion, hash, memberId, revision, scoreVersion]`: the
        score and gate VERSIONS are pinned and no score, metric or evaluation
        reference is among them. `scoreVersion` is recorded as a NEAR-MISS
        rather than a carrier, because it is the version of a scorer and not a
        score, and "absent" should not read as "nothing resembling it exists".

        THE PRESERVE CLAIM, EXECUTED, IS HALF TRUE. A stale member DOES survive
        a freeze with its availability represented honestly — three members in,
        three out, `[available, available, stale]`. But `removeMember` drops the
        member from the live set and `freezeComparison` maps over the live set,
        so A MEMBER CONSIDERED AND THEN REMOVED IS ABSENT FROM THE COMPARISON:
        three members became two, and the removal survives only as a history
        entry carrying no revision, no hash and no state. "All considered
        members" and "all current members" are different sets and the freeze
        pins the second.

        THE ESTATE KNOWS HOW TO RECORD A DECISION. 175 non-test files carry a
        `decisionRationale` or a `rejectionReason` — persona releases, output
        spot-checks, research integrity, avatar packs. The variant comparison
        having none is a choice, not a platform limit.

        The positive control keeps the silences meaningful: `addMember` with a
        stale `expectedVersion` was refused with "stale group version 0 (current
        1)", so the module does say no. -->
        <!-- AND THE CONTRACT HALF IS A SPECIFICATION. I1.8.a-f cite the
        compiling reference under evidence/v1-workbenches/variant-group/, and
        the artifact measures its importers: ZERO files under apps/ or libs/
        reference it. Whatever decision record this cell wants would have to be
        built where the product can reach it. -->
- [ ] I1.9 Define quality profiles per asset kind and delivery target. Evidence:
      thresholds are versioned configuration with code-owned floors.
  - [x] I1.9.a Define profile ID/version/owner, asset kind/operation, delivery
        target/profile/environment/audience, applicability, lifecycle, and
        effective period. Evidence: quality profile schema. <!-- evidence
        (I1.9.a-f): COMPILING reference
        evidence/v1-workbenches/quality-profile/\*.ts (quality-schema.ts
        QualityProfile/QualityMeasure/Threshold/CodeFloor; policy.ts
        applyFloor/mergePolicies/versionPolicy; fixtures.ts + check.ts).
        a=QualityProfile, b=QualityMeasure registry, c=Threshold fixtures,
        d=CODE-OWNED floors — applyFloor lets config only TIGHTEN (loosening
        clamped to floor + flagged, never accepted), e=merge global→…→project
        under TOTAL LAYER_PRECEDENCE + provenance (loosening layer overridden by
        code-floor), f=versionPolicy (bump + invalidate changed + retain raw).
        strict tsc + quality suite via tsx gated.
        generate-quality-profile-schema.mjs (--check), 9 tests. -->
  - [x] I1.9.b Register raw technical/semantic/aesthetic/temporal/audio/3D/
        accessibility/safety/rights/provenance measures with units/direction/
        evaluator and nonvalue states. Evidence: measure registry.
  - [x] I1.9.c Define target-specific thresholds/tolerance/severity/composition/
        required evidence/freshness/remediation and human-review bands.
        Evidence: policy fixtures.
  - [x] I1.9.d Define immutable code-owned minimum/maximum/fail-closed floors
        for safety/integrity/compatibility and only allow configuration to
        tighten. Evidence: loosening negative tests.
  - [x] I1.9.e Merge global/asset-kind/target/territory/channel/tenant/project
        policies with field provenance and no ambiguous precedence. Evidence:
        merge truth tables.
  - [x] I1.9.f Version changes, invalidate old results where required, retain
        raw values, and define compatibility/deprecation/migration. Evidence:
        policy-change tests.
  - [ ] I1.9.g Calibrate/validate profiles on representative known-value/
        boundary/adversarial corpora and obtain domain/target approvals.
        Evidence: calibration report.
        <!-- THE HUMAN HALF IS REAL AND IT IS NOT THE BLOCKER. THE CELL STAYS
        `[ ]`.

        evidence/v1-workbenches/isis-quality-calibration.json —
        generate-isis-quality-calibration.mts + .test.mts (9 tests, 3 ratchets,
        2 controls), wired into inventory + verify.

        AN APPROVAL THAT IS UNOBTAINED AND ONE THAT IS UNRECORDABLE ARE
        DIFFERENT FINDINGS. Nobody signs off in a script, so the old note's
        "human" is fair — but `QualityProfile` declares thirteen fields
        (applicability, assetKind, audience, deliveryProfile, deliveryTarget,
        effectiveFrom, effectiveTo, environment, lifecycle, operation, ownerId,
        profileId, version) and NOT ONE of them could hold an approver, a
        signoff, a reviewer or a date. A domain or target approval obtained
        tomorrow would leave no trace. That is a schema gap, not a scheduling
        one.

        CALIBRATION MEANS RUNNING A MEASURE'S EVALUATOR, AND THE NAMES DO NOT
        RESOLVE. Every `QualityMeasure` names its evaluator with a bare string.
        The three declared are `laion`, `safety-classifier` and
        `tox-classifier`. `tox-classifier` appears NOWHERE ELSE IN THE
        REPOSITORY. The other two are MENTIONED — `laion` in ComfyUI checkpoint
        filenames and another reference's fixtures, `safety-classifier` in a
        Lilith test's `classifierId` and an Oshun BFF module name — and in none
        of those is the string a registry key, a `case` label or a registration
        call. A mention is not a dispatch, and the artifact records the two
        classes apart.

        THE CORPORA ARE REAL AND THEY ARE SOMEWHERE ELSE. `apps/isis/
        generation-api/src/routes` carries 32 regression golden datasets, one
        per asset family, each with thresholds and a quality gate. ALL 32 CARRY
        KNOWN-VALUE EXPECTATIONS. NONE CARRIES A BOUNDARY OR AN ADVERSARIAL
        CASE — two of the three kinds this cell names. And they are not attached
        to the `QualityProfile` model at all: nothing under apps/ or libs/
        imports the reference.

        THE PROFILE MODEL ITSELF IS SOUND, which is what makes the gaps mean
        something. Executed: `applyFloor` clamps a loosening config to the floor
        and flags `loosened=true`, while a tightening config passes through
        unflagged; `mergePolicies` returns effective thresholds each carrying
        `[floorEnforced, sourceLayer, threshold]`, so the winning layer is
        recorded.

        A CONTROL THAT MATTERED. Matching corpus kinds as bare words found
        "transient attack" in the audio golden set and "phrase boundaries" in
        the voice one — creative direction inside string literals — and reported
        an adversarial and a boundary corpus that do not exist. A kind now
        counts only where the term appears as an identifier before `:`/`=`/`(`
        or as a quoted value in its own right, and a test pins both prose
        strings as non-matches.

        AND ONE THE ARTIFACT DID TO ITSELF. The evaluator mention count sweeps
        `*.json` as well as `*.ts`, so once the artifact was committed it
        counted its OWN copy of every evaluator name and the count grew by one
        each run — `--check` could never be satisfied. The lane's own outputs
        (`evidence/v1-workbenches/<name>.json`) are excluded; the reference
        DIRECTORIES beside them are sources and stay in. -->
        <!-- Still human: the approvals themselves. Still provider-bound:
        nothing — calibration needs evaluators that resolve, which is a code
        gap, not an infrastructure one. -->
- [ ] I1.10 Define release-gate policy composition. Evidence: safety,
      provenance, watermark, quality, policy, rights, output shape, and review
      requirements have deterministic precedence.
  - [x] I1.10.a Define safety gates for content/person/identity/child/biometric/
        impersonation/provider policy with nonoverrideable classes and fail-
        closed unavailable behavior. Evidence: safety gate policy. <!-- evidence
        (I1.10.a-i): COMPILING reference
        evidence/v1-workbenches/release-gates/\*.ts (gate-schema.ts 8 GateClass
        policies + 9 GateOutcome states + OUTCOME_PRECEDENCE +
        NON_OVERRIDABLE_CLASSES; compose.ts composeRelease/enumerateTruthTable;
        fixtures.ts one policy per class + rule fixtures; check.ts).
        a=SafetyGatePolicy (nonOverridable + failClosedWhenUnavailable),
        b=ProvenanceGatePolicy, c=WatermarkGatePolicy, d=Quality GatePolicy
        (missingCountsAsPass:FALSE — no missing-to-pass), e=PolicyGateRow
        (version+ owner), f=RightsGatePolicy (requiresEvidence — never client
        truth), g=ShapeGatePolicy, h=ReviewGatePolicy (automationProhibited),
        i=composeRelease = DETERMINISTIC TOTAL aggregation over all 36
        truth-table rows (9 outcomes x required x class) → release/
        block/review-required; FAIL-CLOSED on required non-passing;
        safety+review never waivable; waiver w/o waiverRef blocks; optional
        failures warn only. strict tsc + release suite via tsx gated.
        generate-release-gate-composition.mjs (--check), 10 tests. -->
  - [x] I1.10.b Define provenance gates for artifact hash, ingredient/action/
        tool/actor completeness, manifest binding/signature/trust/revocation/
        recovery and target requirement. Evidence: provenance policy.
  - [x] I1.10.c Define watermark/disclosure gates for method/profile/presence/
        robustness/detectability/accessibility/target/channel and stripped-state
        fallback. Evidence: watermark policy.
  - [x] I1.10.d Define asset/target quality gates from raw measures/evaluator/
        profile/threshold/freshness and no missing-to-pass. Evidence: quality
        policy.
  - [x] I1.10.e Define platform/product/brand/editorial/marketplace/provider/
        territory/channel policy gates with exact version and owner. Evidence:
        policy gate rows.
  - [x] I1.10.f Define rights/consent/license/attribution/training/derivative/
        identity/territory/term/revocation gates with evidence and expiry.
        Evidence: rights policy.
  - [x] I1.10.g Define output-shape/format/schema/hash/size/media/technical/
        bundle/dependency/target-compatibility gates. Evidence: shape policy.
  - [x] I1.10.h Define human review/signoff roles/quorum/sequence/COI/step-up/
        expiry/staleness and automation prohibition. Evidence: review policy.
  - [x] I1.10.i Define deterministic precedence/aggregation for blocker/warning/
        unavailable/stale/waived/overridden/unsupported/partial across required/
        optional and target-specific gates. Evidence: exhaustive truth tables.
  - [ ] I1.10.j Bind eligibility to exact output/dependency/evaluator/policy/
        evidence/waiver/signoff/target versions and audit all reasons. Evidence:
        composition integration tests.
        <!-- SUPERSEDED (2026-08-16). The old note said "runtime: requires the
        running service + audit store". IT DOES NOT: the binding is
        `evaluateProofRequirements` in `@oshun/contracts/common`, a pure
        function, and the `evaluateReleaseGate` that calls it is pure too. Both
        run in-process with crafted known values.
        evidence/v1-workbenches/isis-release-binding-probe.json —
        generate-isis-release-binding-probe.mts + .test.mts (7 tests, 3
        ratchets, 1 control), wired into inventory + verify.

        THE BINDING THE ESTATE DOES HAVE IS STRICT AND IT WORKS. A proof
        differing from the requirement in exactly ONE way — a different subject,
        a different policy, the wrong proof type, a failed status, a revoked
        status, an expiry already passed, a verification instant in the future —
        is refused in all seven cases, each with its own reason, while the
        control proof that differs in nothing is satisfied. That control is what
        makes the seven refusals evidence about the binding rather than about
        the fixture.

        BUT OF THE EIGHT BINDINGS THE CELL NAMES, THE REQUIREMENT CAN DEMAND
        TWO. `ProofRequirementSchema` is `.strict()` with four fields —
        requirementId, subjectRef, requiredProofType, policyRef — so OUTPUT and
        POLICY are demandable and nothing else is. SIGNOFF is demandable only
        indirectly, by requiring the `human_approval` proof type, which cannot
        name a particular signer. DEPENDENCY, WAIVER and TARGET are expressed by
        nothing at all: no requirement field, no proof field, no proof type.

        AND TWO ARE CARRIED BUT NEVER COMPARED, WHICH IS THE SHARPEST CATEGORY.
        Every proof records a `producerRef` — the estate knows WHICH EVALUATOR
        issued it — and the binding never looks: the probe varies exactly that
        field and the requirement is still satisfied. Every proof carries
        `evidenceRefs` and the verdict returns them, so evidence is AUDITED; but
        no requirement can ask for particular evidence, so it is not DEMANDED.

        THE POLICY BINDING IS BY NAME, NOT BY VERSION. The gate builds its
        requirement with `policyRef: `isis-release:${kind}``, a constant per gate
        kind, so every evaluation of a given gate binds to the same reference
        forever and changing the policy does not invalidate a proof issued under
        the old one — which is precisely the version binding this cell asks for.

        The cell stays `[ ]`: two of eight demandable, one indirect, two carried
        but uncompared, three inexpressible, and no version anywhere. -->
        <!-- runtime: binding eligibility to exact live output/dependency/evaluator/
                                                                                                                                                                                                            policy/evidence/waiver/signoff/target VERSIONS and auditing every reason requires the
                                                                                                                                                                                                            running service + audit store (integration tests). The composition contract (i)
                                                                                                                                                                                                            already retains every reason. Parent I1.10 stays unchecked. -->
- [x] I1.11 Define model, adapter, LoRA, checkpoint, node, and workflow
      compatibility constraints. Evidence: incompatible combinations fail
      planning before dispatch. <!-- evidence: COMPILING reference
      evidence/v1-workbenches/compatibility/\*.ts (constraints-schema.ts 6
      component surfaces + IssueKind; solver.ts solveCompatibility; fixtures.ts
      12-row MATRIX; check.ts), strict tsc --noEmit + compat suite via tsx (both
      gated in verify). a=ModelConstraints, b=AdapterConstraints,
      c=LoRAConstraints, d=CheckpointConstraints, e=NodeConstraints (socket
      typing), f=WorkflowConstraints, g=solveCompatibility returning EXACT
      conflict/unsupported/missing-metadata/version-skew w/
      components+field+alternatives (NO implicit conversion; absent metadata
      BLOCKS; unknown extension refused not ignored), h=12-row fixture matrix
      (pairwise/multi-lora overlap/adapter
      stacking/version-skew/missing-metadata/unknown-extension/provider
      capability-drift/node-socket/missing-node/precision/audience/post-plan-change).
      PARENT satisfied: every incompatible plan has dispatchable=false —
      incompatible combinations FAIL PLANNING BEFORE DISPATCH.
      generate-compatibility-constraints.mjs (--check gate), 10 tests. -->
  - [x] I1.11.a Define model family/architecture/task/modality/input-output/
        tokenizer/encoder-decoder/precision/quantization/context/license/
        provider/runtime compatibility. Evidence: model constraints.
  - [x] I1.11.b Define adapter type/base architecture/model/version/target
        modules/dimensions/weights/format/precision/license and stacking/
        conflict compatibility. Evidence: adapter constraints.
  - [x] I1.11.c Define LoRA base model/version/architecture/target modules/rank/
        alpha/weight format/trigger/tags/precision/license/style/identity policy
        and multi-LoRA compatibility. Evidence: LoRA constraints.
  - [x] I1.11.d Define checkpoint architecture/base/version/format/hash/source/
        config/tokenizer/VAE/text encoder/scheduler/precision/license/security
        compatibility. Evidence: checkpoint constraints.
  - [x] I1.11.e Define node implementation/type/version/input-output socket/
        schema/data type/shape/unit/asset kind/runtime/provider/resource/
        determinism/security compatibility. Evidence: node constraints.
  - [x] I1.11.f Define workflow graph/template/version required/optional nodes/
        models/adapters/LoRAs/checkpoints/providers/resources/asset kinds/
        audiences/outputs and target constraints. Evidence: workflow
        constraints.
  - [x] I1.11.g Implement versioned constraint registry and planner solver that
        returns exact conflicts/alternatives/unsupported rather than dispatch or
        implicit conversion. Evidence: planning negative tests.
  - [x] I1.11.h Test pairwise and multi-component combinations, version skew,
        missing metadata, unknown extensions, provider capability drift, and
        post-plan change. Evidence: compatibility fixture matrix.
- [ ] I1.12 Define deterministic replay eligibility. Evidence: required
      artifacts, versions, seeds, and environment dependencies are explicit.
  - [x] I1.12.a Define replay class: byte-deterministic, semantically
        reproducible-with-tolerance, provider-best-effort, or nonreplayable with
        reason. Evidence: replay taxonomy. <!-- evidence (I1.12.a-g): COMPILING
        reference evidence/v1-workbenches/replay/\*.ts (replay-schema.ts
        ReplayClass 4-class + REQUIRED*VERSION_KEYS(15)/REQUIRED_ENVIRONMENT*
        KEYS(12) + DependencyProblemKind(6); eligibility.ts
        checkVersion/Artifact/Environment Completeness +
        classifyProviderCeiling + deriveReplayClass + checkPermission +
        buildReplayPreview; fixtures.ts + check.ts). a=class DERIVED from the
        weakest input, NEVER asserted upward (incomplete env caps below
        byte-deterministic even on a perfect provider; declared provider
        nondeterminism caps at best-effort; missing versions ⇒ nonreplayable
        WITH reason), b=null version is MISSING not "latest", c=artifact access/
        hash/lineage, d=environment manifest, e=provider support DECLARED not
        assumed, f=technical reproducibility is NOT permission
        (reproducible-but-forbidden proven), g=preview names every problem +
        unknown cost stays null. strict tsc + replay suite via tsx gated.
        generate-replay-eligibility.mjs (--check), 11 tests. -->
  - [x] I1.12.b Require immutable brief/plan/workflow/graph/node/model/
        checkpoint/adapter/LoRA/tool/template/policy/evaluator/corpus/provider
        versions or hashes. Evidence: version completeness tests.
  - [x] I1.12.c Require all source/reference/conditioning/mask/latent/
        intermediate/artifact bytes and ingredient lineage with hash/access/
        retention. Evidence: artifact completeness tests.
  - [x] I1.12.d Require exact seed/subseed/random
        generator/algorithm/parameters/
        precision/hardware/runtime/container/driver/environment/dependency/
        locale/time inputs where applicable. Evidence: environment manifest.
  - [x] I1.12.e Require provider operation/idempotency/replay support or
        classify provider nondeterminism/version unavailability honestly.
        Evidence: provider replay tests.
  - [x] I1.12.f Validate current authorization/rights/consent/policy/budget/
        safety and distinguish technical reproducibility from permitted replay.
        Evidence: eligibility tests.
  - [x] I1.12.g Produce missing/inaccessible/expired/revoked/incompatible/
        nondeterministic dependencies and expected tolerance/cost before replay.
        Evidence: replay preview.
  - [ ] I1.12.h Execute as a new linked run with same inputs/seed where eligible
        and compare bytes/raw metrics/semantic tolerances/provenance/cost.
        Evidence: replay known-value tests.
        <!-- EXECUTING A GENERATION DOES NEED A PROVIDER, AND THE TWO QUESTIONS
        BEFORE IT DO NOT. THE CELL STAYS `[ ]` ON BOTH.

        evidence/v1-workbenches/isis-replay-execution.json —
        generate-isis-replay-execution.mts + .test.mts (8 tests, 3 ratchets, 3
        controls), wired into inventory + verify.

        THERE IS NO REPLAY IN ISIS THAT PRODUCES A NEW RUN. The one operation
        Isis calls a replay is `replayDeadLetterEntry`, and its body is parsed
        rather than described: it UPDATEs the same outbox row and the same job
        row back to `pending`. No INSERT, no new id, no link. IT IS A
        RE-DISPATCH OF THE ORIGINAL, WHICH IS THE OPPOSITE OF A NEW LINKED RUN —
        and on the way it sets `attempts = 0`, `last_error = NULL` and
        `dead_lettered_at = NULL`, so the first attempt's terminal detail does
        not survive it. (`replay_count`, `last_replayed_at` and
        `last_replay_reason` do.) That is a defensible operational action; it is
        simply not what this cell asks for.

        A REAL LINKED REPLAY EXISTS IN A DIFFERENT DOMAIN.
        `@oshun/content-service`'s `replay(runId)` re-submits the brief and
        records `replayOf` on a separately persisted run. So the estate knows
        the shape. Two caveats measured from its own body: it re-submits with a
        MODIFIED brief id (`${briefId}__replay`), so "same inputs" is false by
        construction in one field, and it compares nothing — it returns the new
        record.

        THE ELIGIBILITY MODEL I1.12.a-g BUILT IS GOOD, AND IT WAS EXECUTED. A
        complete manifest on a deterministic provider classifies as
        `byte-deterministic`; a missing model version gives `nonreplayable`; a
        declared-nondeterministic provider caps at `provider-best-effort`. The
        class really is derived from the weakest input.

        BUT ITS PREVIEW SAYS WHETHER, NEVER WHAT. `buildReplayPreview` returns
        `[classification, eligible, estimatedCost, expectedTolerance, permitted,
        problems, technicallyReproducible]`. Two of those are THE CALLER'S OWN
        ARGUMENTS HANDED BACK — `tolerance` and `estimatedCost` are parameters —
        and none of them is a comparison plan or a list of the dimensions a
        replay would be checked against. The cell names five dimensions and the
        preview names none of them.

        AND A COMPARATOR IS ONLY ON THE REPLAY PATH IF THERE IS A REPLAY PATH.
        Scoped to `apps/isis`, `libs/isis` and the generation control plane,
        there are 32 exported comparator functions; matched against each
        dimension BY FUNCTION BODY, bytes has 3, raw metrics 1, provenance 1,
        cost 1 and semantic tolerances none. Not one of them can run on a replay
        of an Isis generation, because Isis produces no replay run for them to
        read.

        TWO NARROWINGS THE MEASUREMENT NEEDED. Matching a dimension against the
        whole FILE credited every comparator that happens to live beside the
        word "score" — 561 for one dimension, which measures nothing; the match
        is now against the function body. And `[Dd]iff` matched
        `adjustDifficulty`; it now requires the next character not to be
        lowercase. -->
        <!-- What remains genuinely provider-bound: executing a replay and
        comparing the actual bytes. That is one step, and it is the last one. -->
- [ ] I1.13 Define artifact promotion across development, test, staging, and
      production. Evidence: no environment can consume an inadmissible state.
  - [x] I1.13.a Define environment-specific artifact states, stores/regions/
        encryption/credentials, allowed producers/consumers, and immutable
        identity/hash. Evidence: environment policy. <!-- evidence
        (I1.13.a-e/g/h): COMPILING reference
        evidence/v1-workbenches/promotion/\*.ts (promotion-schema.ts
        ENVIRONMENTS(4)/ArtifactState(8)/CONSUMABLE_STATES/
        CONSUMPTION_CHANNELS(9)/TEST|STAGING|PRODUCTION_PREREQUISITES(11/9/9)/LifecycleOp(7);
        admission.ts canConsume/promote/applyLifecycleOp; fixtures.ts +
        check.ts). a=EnvironmentPolicy (per-env
        store/region/encryption/credentialScope, never shared), b=dev artifact
        refused by EVERY non-dev env x EVERY channel, c/d/e=test/staging/
        production gates w/ each unmet prerequisite NAMED (prod requires
        humanSignoff), g=all 7 lifecycle ops leave the artifact inadmissible on
        every channel, h=ONE admission door for all 9 channels incl. direct-id
        (no bypass) + concurrent source-mutation detection via observed-hash.
        Promotion is SEQUENTIAL, mints a NEW identity w/ lineage, and REFUSES
        in-place relabeling + env-skipping. strict tsc + promotion suite via tsx
        gated. generate-artifact-promotion.mjs (--check), 12 tests. -->
  - [x] I1.13.b Define development intake/iteration artifacts and explicit
        prohibition from test/staging/production consumption without promotion.
        Evidence: dev isolation tests.
  - [x] I1.13.c Define test promotion prerequisites for source/hash/schema/
        malware/security/license/rights/consent/provenance and reproducible
        fixture/evaluation. Evidence: test gate.
  - [x] I1.13.d Define staging promotion prerequisites for approved model/
        workflow, representative quality/safety/compatibility/cost/load, target
        bundle, and release candidate. Evidence: staging gate.
  - [x] I1.13.e Define production promotion prerequisites for fresh gates,
        human/signoff, provenance/signature, rights/consent, artifact lock,
        rollback/recall, and target readiness. Evidence: production gate.
  - [ ] I1.13.f Copy/replicate exact verified bytes using target-managed
        credentials, rehash/read-back, record source/destination identity and
        promotion lineage, and prohibit in-place status relabeling. Evidence:
        promotion integration tests.
        <!-- SUPERSEDED (2026-08-16). The old note said this needs "the running
        service + real object stores"; the dev stack HAS a real object store,
        and the run below used two buckets of it. It also said "the CONTRACT
        half is done: promote() records source/destination identity + lineage" —
        HALF OF THAT IS WRONG. `promote()` records a source artifact id and a
        target ENVIRONMENT. No destination store, bucket, key or region appears
        anywhere on the artifact it produces, and the artifact interface has no
        field that could hold one.

        ZERO OF THE SEVEN OBLIGATIONS ARE PERFORMED BY THE PRODUCT.

        evidence/v1-workbenches/isis-promotion-transfer.json —
        generate-isis-promotion-transfer.mts + .test.mts (12 tests, 4 ratchets,
        3 controls), `--check` and the tests wired into verify. Each obligation
        is anchored on a phrase that must still appear in the cell.

        THREE SUBJECTS, AND ONLY ONE OF THEM IS THE PRODUCT. The reference
        `evidence/v1-workbenches/promotion/*.ts` that I1.13.a–e/g/h cite; the
        estate's own `S3StorageClient`; and `apps/isis` + `libs/isis`. All three
        were measured and two were executed.

        THE REFERENCE IMPLEMENTS TWO OBLIGATIONS AND CANNOT EXPRESS FIVE.
        Executed: it mints a new identity carrying `sourceArtifactId`, and it
        REFUSES a promotion that reuses the same id with the message "in-place
        status relabeling is prohibited". Both real. But `promote(artifact,
        target, evidence, newArtifactId, atTime)` takes no bytes, no store and
        no credentials, so copying, rehashing, reading back and naming a
        destination are outside it by construction rather than merely undone.
        And it CARRIES THE SOURCE CONTENT HASH FORWARD UNCHANGED — the comment
        beside it reads "the SAME verified bytes", which is an assertion about
        bytes the function never sees.

        THE ESTATE'S CLIENT CAN CROSS BUCKETS AND DOES NOTHING ELSE THE CELL
        ASKS. Executed against MinIO with two buckets: `copy()` moved the object
        into the target bucket and returned an etag. It performs no download and
        computes no digest — the probe had to download and rehash itself to
        learn the destination bytes matched. And it issues ONE
        `CopyObjectCommand` from the destination client naming the source bucket
        in `CopySource`, so THE SAME CREDENTIAL PAIR READS THE SOURCE AND WRITES
        THE DESTINATION and a target-managed transfer is not expressible. The
        estate reads one `MODELS_S3_BUCKET` and one AWS key pair; the
        reference's `EnvironmentPolicy` declares a store and a credential scope
        per environment.

        NO ISIS PRODUCT FILE COPIES AN ARTIFACT BETWEEN STORES, and no product
        code imports the reference — so the two obligations that ARE implemented
        are implemented in a module nothing runs.

        A NEAR-MISS THAT FILLED FOUR COLUMNS. The first cut looked for `.copy(`
        and matched `buffer.copy(` across eight 3D-geometry modules, reporting
        four of the seven obligations as performed by the product — the opposite
        of the finding. A site now counts only if it issues `CopyObjectCommand`
        or calls `.copy(` in a module that holds a storage client, with a
        control pinning that the collision source is rejected and that a real
        storage copy is still found. -->
  - [x] I1.13.g Define reject/quarantine/rollback/demote/revoke/recall/expire
        and dependent workflow/run/release/cache behavior per environment.
        Evidence: lifecycle tests.
  - [x] I1.13.h Test environment credential, URL/ref, API/provider, storage,
        cache/search, job, and direct-ID bypass plus concurrent source mutation.
        Evidence: inadmissible consumption negative tests.
- [ ] I1.14 Define provenance aggregation for multi-step workflows. Evidence:
      nested executions produce one verifiable release lineage.
  - [x] I1.14.a Define workflow/parent-run/child-run/stage/action/artifact/
        ingredient/transform relationships and stable ordering/branch/fan-in/
        retry identity. Evidence: aggregation graph schema. <!-- evidence
        (I1.14.a-g): COMPILING reference evidence/v1-workbenches/provenance-
        aggregation/\*.ts (aggregation-schema.ts
        RunNodeKind(5)/IngredientDisposition(9)/
        AggregationIssueKind(4)/TrustStatus(4); aggregate.ts
        validateStageProvenance/
        aggregateIngredients/composeAssertions/bindManifest/composeTrust/queryLineage;
        fixtures.ts + check.ts). a=RunNode w/ order+branch+ATTEMPT (retry is a
        distinct node), b=stage provenance validated BEFORE aggregation (unknown
        artifact refs, hash mismatch, missing
        provider/tool/model/template/policy/actor, out-of-graph run all
        rejected), c=NO FLATTENING (same artifact reused AND duplicated = 2
        rows; all 9 dispositions survive; selected vs considered
        distinguishable), d=field-level sourceRunId + CONFLICT names every
        source (no silent winner) + redacted RECORDED, e=manifest binds final
        bytes + child manifests BY HASH (not inlined), f=HEADLINE: composeTrust
        = worst child status, parent claim preserved + parentClaimOverridden
        flagged (a trusted parent CANNOT hide a child failure), g=cycle-safe
        traversal w/ unauthorized nodes REDACTED BY NAME. strict tsc +
        aggregation suite via tsx gated. generate-provenance-aggregation.mjs
        (--check), 13 tests. -->
  - [x] I1.14.b Ingest each child/stage raw provider/tool/model/template/policy/
        actor/input/output provenance and validate hashes/references before
        aggregation. Evidence: stage provenance tests.
  - [x] I1.14.c Preserve shared/reused/duplicate/optional/failed/retried/
        compensated/manual edited ingredients and exact selected outputs without
        flattening lineage. Evidence: complex workflow fixtures.
  - [x] I1.14.d Compose ordered action/ingredient/tool/actor/policy assertions
        with field-level source and identify conflicts/missing/unsupported/
        redacted data. Evidence: aggregation tests.
  - [x] I1.14.e Bind aggregate manifest/ledger record to final artifact/bundle
        bytes and include child manifest/hash refs rather than unbounded
        content. Evidence: binding tests.
  - [x] I1.14.f Sign/validate aggregate independently while preserving invalid/
        untrusted/revoked child status and preventing a trusted parent from
        hiding child failure. Evidence: trust composition tests.
  - [x] I1.14.g Traverse release→final→stages→ingredients and ingredient→all
        derivatives/release with authorization/redaction/cycle/scale handling.
        Evidence: lineage query tests.
  - [ ] I1.14.h Verify rebuild/round-trip/tamper/stripped recovery and exact
        release eligibility based on complete current lineage. Evidence:
        end-to-end provenance report.
        <!-- SUPERSEDED (2026-08-16). The old note said a rebuild needs "the
        running service + real bytes". The estate's provenance ledger is a PURE
        hash-chained module — `buildIsisProvenanceBundle`,
        `appendProvenanceLedger`, `verifyProvenanceLedger`,
        `buildProvenanceChainFingerprint` in
        libs/isis/ai-providers/src/provenance/bundle.ts — so all four verbs are
        pure-function questions and all four were executed against the real
        implementation.

        THREE OF THE FOUR VERBS PASS, AND THE CELL STAYS `[ ]` ON THE FOURTH AND
        ON ELIGIBILITY.

        evidence/v1-workbenches/isis-provenance-recovery.json —
        generate-isis-provenance-recovery.mts + .test.mts (9 tests, 4 ratchets,
        3 controls), wired into inventory + verify.

        THE TAMPER DETECTION IS REAL AND SHOULD BE SAID SO. Executed: a bundle's
        fingerprint is recomputed from its fields, and one edited field is
        refused with `fingerprint does not match bundle fields`. A JSON
        round-trip of a three-entry ledger still verifies. An EDITED entry
        breaks at index 1, a TRUNCATED ledger at index 2 and a REORDERED ledger
        at index 0 — each caught, each located — while the untouched ledger
        verifies. `verifyProvenanceLedger` also enforces tenant coherence,
        one-generation-once and monotonic emission time.

        STRIPPED RECOVERY IS THE VERB THAT FAILS, AND IT FAILS ON A KEY. The
        persisted `isis_provenance_bundles` table declares fifteen columns and
        NOT ONE names an artifact, an output, a content hash or a storage key;
        the store offers exactly two lookups, `loadLedger(tenantId)` and
        `verifyPersistedLedger(tenantId)`. An artifact that arrives with its
        embedded provenance stripped therefore has nothing to be matched
        against: the ledger is keyed by tenant and generation id, and the
        generation id is precisely what stripping removes.

        AND THE VERDICT GATES NOTHING. `buildIsisReleaseReadiness` decides its
        provenance requirement with `input.expectNewOutput ? true :
        Boolean(input.outputBundle?.attachedSignals.includes('provenance'))` —
        a flag set when a provenance RECORD WAS SUPPLIED, never from whether a
        chain verified. Executed: a bundle claiming the signal is release-ready
        with an empty `missingRequirements`, and no ledger, fingerprint or
        verification verdict appears anywhere in the expression. The positive
        control matters here: removing the signal DOES block with
        `provenance_missing`, so the gate is not inert — it is reading the wrong
        thing. THE CELL ASKS FOR ELIGIBILITY BASED ON COMPLETE CURRENT LINEAGE
        AND THE GATE READS A CLAIM THAT ONE EXISTS.

        The one consumer of `verifyProvenanceLedger` outside its own module is
        `apps/oshun/web/src/lib/server/isis-provenance-loader.ts`, and it does
        not branch on the verdict — it spreads it into a returned object for an
        operator page to display. That is MEASURED: the binding is found by
        name and the branch is looked for on that binding. A first cut called
        any file containing a `throw` a gate, which credited the loader with
        refusing something it does not refuse.

        ALSO WORTH KEEPING IN VIEW: two libraries export a function called
        `buildIsisProvenanceBundle`. The Isis one builds the hash-chained
        record; the generation-control one builds the presentation summary
        carrying `attachedSignals`. The release gate reads the second. -->
- [x] I1.15 Add canonical contract fixtures for every asset kind. Evidence:
      fixtures parse through public contracts with no `any` or payload gaps.
      <!-- evidence: COMPILING reference
      evidence/v1-workbenches/asset-contracts/\*.ts (contracts.ts 11-kind
      discriminated union + branded units Pixels/Millimeters/
      Seconds/Hertz/Lufs/Degrees/Ratio01 + typed AssetRef/EvidenceRef; parse.ts
      public parseAsset/serializeAsset/roundTrips w/ per-kind field spec;
      fixtures.ts 11x5=55 fixtures; coverage.ts buildCoverageReport = the
      I1.15.l gate; check.ts). a=image b=portrait c=texture d=video e=audio
      f=foley g=voice h=mesh i=rig j=splat k=scene, each w/
      minimal/full/boundary (parse + byte-stable round-trip) + invalid (refused,
      specific field named) + version-skew (refused as skew, NEVER migrated).
      PARENT BAR MET: no `any` and no index-signature payload bag
      (comment-stripped source scan); an unexpected field is REFUSED ("no
      unknown-payload bag") not absorbed; a missing required field is refused BY
      NAME not defaulted. strict tsc + fixture suite via tsx gated.
      generate-asset-contract-fixtures.mjs (--check), 12 tests. -->
  - [x] I1.15.a Add minimal/full/invalid/boundary/version-skew image brief/plan/
        run/output/lineage/gate/release fixtures. Evidence: public contract
        parse/round-trip tests.
  - [x] I1.15.b Add portrait fixtures covering identity/reference/consent/
        biometric/impersonation and missing/revoked evidence states. Evidence:
        portrait contract tests.
  - [x] I1.15.c Add texture fixtures covering PBR channels/UDIM/tiling/color/
        material target and unsupported channel/profile. Evidence: texture
        tests.
  - [x] I1.15.d Add video fixtures covering frame/time/audio/caption/motion/
        codec/delivery and partial/truncated/variable-rate states. Evidence:
        video tests.
  - [x] I1.15.e Add audio fixtures covering sample/channel/duration/loudness/
        transcript/codec/delivery and nonvalue/error states. Evidence: audio
        tests.
  - [x] I1.15.f Add foley fixtures covering event/time/variation/perspective/
        sync/loop/package and rights/quality states. Evidence: foley tests.
  - [x] I1.15.g Add voice fixtures covering speaker/consent/language/SSML/
        pronunciation/timing/disclosure/caption and revoke states. Evidence:
        voice tests.
  - [x] I1.15.h Add mesh fixtures covering units/axes/geometry/topology/UV/
        materials/LOD/collision/formats and invalid/unsupported cases. Evidence:
        mesh tests.
  - [x] I1.15.i Add rig fixtures covering hierarchy/joints/skin/controls/
        constraints/retarget/target formats and deformation failure. Evidence:
        rig tests.
  - [x] I1.15.j Add Gaussian-splat fixtures covering coordinates/cameras/
        positions/scales/rotations/opacity/SH/compression/point count/privacy
        and version failures. Evidence: splat tests.
  - [x] I1.15.k Add composite-scene fixtures covering hierarchy/refs/variants/
        cameras/lights/materials/animation/audio/dependencies/bundles and
        partial component states. Evidence: scene tests.
  - [x] I1.15.l Assert every fixture uses branded typed fields, exact units/
        discriminators/versions/refs, no `any`/unknown payload bag, public
        parse/ serialize/round-trip, and expected invalid refusal. Evidence:
        asset-kind fixture coverage gate.

### I2 — Brief, prompt, workflow, and variant authoring

- [x] I2.1 Implement deterministic brief-to-prompt-spec compilation. Evidence:
      known briefs compile to exact structured specs. <!-- evidence: COMPILING
      reference evidence/v1-workbenches/prompt-spec/\*.ts (prompt-spec.ts
      versioned PromptSpec — system/policy separated, NO free-form
      prompt/instruction field, fenced references; compile.ts
      validateInput/compileBrief/
      serializeSpec/recompilesIdentically/leaksRestrictedDetail; fixtures.ts 6
      canonical + 10 negative briefs; goldens.ts FROZEN golden specs; check.ts).
      a=schema, b=validate+canonicalize (gapless ref order, 0..1 weights,
      approved vocab/locale/ extensions), c=approved-only resolution + full
      mapping trace (brief/default/derived/ omitted/unsupported — a default is
      visible AS a default), d=host-independent serialization (sorted keys, NFC,
      NO Date/Math.random/localeCompare), e=untrusted text stays DATA + tier
      restriction — the TRACE ITSELF redacts restricted identities (5/6 goldens
      unauthorized-tier carry ZERO template/model/provider strings; the 1
      authorized golden legitimately does), f=3 structured error kinds, NEVER a
      concatenation fallback, g=6 canonical briefs → EXACT frozen goldens +
      byte-identical recompile (the parent's bar). strict tsc + compiler suite
      via tsx gated. generate-prompt-spec-compiler.mjs (--check), 12 tests. -->
  - [x] I2.1.a Define versioned `PromptSpec` schema with separated
        system/policy, positive intent, exclusions, references/conditioning,
        spatial/temporal/ audio controls, output/delivery constraints, locale,
        and extensions. Evidence: schema fixtures.
  - [x] I2.1.b Validate and canonicalize exact brief revision, asset/workflow/
        audience profile, locale, units, ordered refs, typed controls, and
        policy/code floors before compile. Evidence: compiler input tests.
  - [x] I2.1.c Resolve only versioned approved templates/vocabularies/mappings
        and record every source field/default/derived/omitted/unsupported
        decision. Evidence: mapping trace.
  - [x] I2.1.d Serialize normalized spec in stable field/list/number/string/
        Unicode order independent of host locale/time/random/process ordering.
        Evidence: determinism tests.
  - [x] I2.1.e Keep untrusted user/reference content fenced as data and prohibit
        compiled spec from granting workflows/models/tools/provider detail
        outside audience. Evidence: injection/tier tests.
  - [x] I2.1.f Return structured validation/unsupported/policy errors without
        falling back to generic prompt concatenation. Evidence: negative tests.
  - [x] I2.1.g Compile canonical minimal/full/multilingual/multireference/
        multimodal briefs to exact golden specs and recompile byte-identically.
        Evidence: golden compiler suite.
- [x] I2.2 Keep positive intent, exclusions, reference controls, spatial
      controls, temporal controls, audio controls, and delivery constraints as
      typed fields. Evidence: no required control is hidden in free-form text.
      <!-- evidence: COMPILING reference
      evidence/v1-workbenches/typed-controls/\*.ts (controls.ts 7 typed blocks +
      LINTED_SURFACES(5) + HiddenControlViolation; lint.ts
      lintControlSet/lintFreeText/hiddenOnly/duplicatesOnly w/ 9 detectors;
      fixtures.ts FULLY_TYPED + AV_TYPED + 7 HIDDEN_CASES + DUPLICATE_CASE;
      check.ts). a=PositiveIntent (stable vocabularyVersion + namespaced
      domainBlocks), b=Exclusion (category/scope/SEVERITY/SOURCE — code-floor vs
      user distinguishable), c=ReferenceControl
      (order/version/hash/regionMask/influenceSchedule/preprocessing/
      rights/consent/conflictBehavior), d/e/f/g=Spatial/Temporal/Audio/Delivery.
      PARENT BAR: prose only in `notes` declared NON-AUTHORITATIVE; the lint
      covers ALL 5 surfaces (compiler/ui/contract/import/export + notes itself)
      and separates a genuinely HIDDEN control (typed field EMPTY ⇒ lint FAILS)
      from a DUPLICATE (typed ⇒ reported, not fatal), quoting the offending
      text. strict tsc + controls suite via tsx gated.
      generate-typed-controls.mjs (--check), 11 tests. -->
  - [x] I2.2.a Define typed positive intent for subject/content/action/context/
        style/tone/composition/required elements and domain blocks with stable
        vocabularies/extensions. Evidence: intent field tests.
  - [x] I2.2.b Define typed exclusions for content/objects/styles/attributes/
        safety/brand/rights/technical constraints with scope/severity/source.
        Evidence: exclusion tests.
  - [x] I2.2.c Define typed ordered reference roles, weights, strengths,
        regions/masks, influence schedule, preprocessing, versions/hashes,
        rights/consent, and conflict behavior. Evidence: reference control
        tests.
  - [x] I2.2.d Define typed canvas/coordinate/units/aspect/resolution/crop/
        layout/position/depth/pose/camera/lens/light/scene spatial controls.
        Evidence: spatial tests.
  - [x] I2.2.e Define typed frame/sample/beat/timecode units, duration/rate/
        sequence/shot/timing/motion/transition/loop/sync temporal controls.
        Evidence: temporal tests.
  - [x] I2.2.f Define typed sample/channel/codec/loudness/peak/dialogue/voice/
        pronunciation/music/SFX/foley/stem/mix/sync audio controls. Evidence:
        audio tests.
  - [x] I2.2.g Define typed target/host/channel/environment/territory/locale/
        format/profile/dimensions/quality/naming/package/deadline/embargo/
        publisher constraints. Evidence: delivery tests.
  - [x] I2.2.h Prevent required control names/values from appearing only in
        free-form prompt fields; lint compiler/UI/contracts/import/export.
        Evidence: deliberate hidden-control failures.
- [x] I2.3 Validate reference media kind, rights, checksum, dimensions,
      duration, codec, and moderation before planning. Evidence: invalid inputs
      never reach a provider. <!-- evidence: COMPILING reference
      evidence/v1-workbenches/reference-validation/\*.ts (reference-schema.ts
      StoredReference w/ NO url field + IssueKind(5) + ScanKind(8) +
      call-counted ProviderSeam; validate.ts
      resolveReference/checkKind/checkRights/
      checkChecksum/checkDimensions/checkTiming/checkCodec/checkScans/planWithReferences;
      fixtures.ts 17 bad refs + fnv1a hasher + makeProvider; check.ts).
      a=access/state/ retention + no mutable-URL path, b=SNIFFED kind
      authoritative over declared, c=rights/consent freshness+revocation, d=REAL
      recompute-and-compare (a flipped byte quarantines), e=dimensions,
      f=timing/sync, g=codec/bit-depth, h=scans must exist+PASS +be fresh+be
      BOUND TO THE SAME HASH. PARENT BAR (i): provider reachable ONLY via the
      counted seam; 17 bad refs x (alone + mixed w/ a valid ref) = 34
      ZERO-PROVIDER-CALL assertions; every refusal names ref+check+kind. strict
      tsc + validation suite via tsx gated. generate-reference-validation.mjs
      (--check), 11 tests. -->
  - [x] I2.3.a Resolve immutable authorized reference bytes/metadata and verify
        tenant/project/actor/property access, state, retention, and no mutable
        URL-only dependency. Evidence: reference resolver tests.
  - [x] I2.3.b Sniff actual media/container/asset kind and verify it is
        supported by declared role, asset kind, workflow class, model/provider,
        and target. Evidence: kind mismatch tests.
  - [x] I2.3.c Verify license/ownership/attribution/derivative/training/
        territory/term and identity/voice/biometric consent evidence/freshness/
        revocation. Evidence: rights/consent tests.
  - [x] I2.3.d Rehash stored bytes with approved algorithm and compare metadata/
        provenance/manifest; quarantine corruption. Evidence: checksum spoof
        tests.
  - [x] I2.3.e Validate image/texture/3D dimensions/units/axes/resolution/point/
        polygon/texture/channel limits and role alignment. Evidence: dimension
        boundary tests.
  - [x] I2.3.f Validate video/audio/animation duration/frame/sample/beat/time
        base/rate/track/channel/synchronization limits. Evidence: duration
        tests.
  - [x] I2.3.g Validate codec/container/profile/level/bit depth/color/HDR/
        compression/3D format/extensions and decode/parser resource limits.
        Evidence: codec/format tests.
  - [x] I2.3.h Require current malware/content/child/identity/safety/moderation/
        archive/domain scans bound to the same hash. Evidence: scan freshness
        tests.
  - [x] I2.3.i Stop compilation/planning/provider calls and return exact
        invalid/unsupported/unauthorized/stale/quarantined reason. Evidence:
        zero-provider-call negative assertions.
- [ ] I2.4 Implement workflow-template selection by capability and audience.
      Evidence: tier-forbidden workflows never appear or dispatch.
  - [x] I2.4.a Query approved active workflow versions by asset kind/operation/
        required capabilities/input-output/profile/target and current dependency
        compatibility. Evidence: candidate query tests. <!-- evidence
        (I2.4.a-f): COMPILING reference evidence/v1-workbenches/workflow-
        selection/\*.ts (selection-schema.ts WorkflowCandidate +
        CandidateSummary/Detail + RejectionCode; select.ts
        queryCandidates/filterByTier/filterEligibility/
        rankCandidates/selectWorkflow/projectFor/dispatchWorkflow; fixtures.ts
        19 deliberate rejections; check.ts).
        a=capability/IO/profile/target/dependency query, b=tier/host/
        entitlement/tenant-policy/classification exposure,
        c=lifecycle+provider+resource+ budget+rights+safety
        (deprecated-in-window stays eligible), d=deterministic rank w/ STABLE
        id/version tie-break (order-independent), e=curated tiers get a Summary
        type that STRUCTURALLY lacks graph/nodes/model/provider (a leak = TYPE
        ERROR) + serialized -view secret scan, f=explicit choice runs the FULL
        chain so a forbidden id is REJECTED, and dispatch RE-VALIDATES
        (revoked-after-planning RACE refused); operator tier still allowed.
        strict tsc + selection suite via tsx gated.
        generate-workflow-selection.mjs (--check), 11 tests. -->
  - [x] I2.4.b Filter by canonical actor tier, audience/host allowlist,
        entitlement, tenant/project policy, classification, and workflow
        exposure. Evidence: tier filter tests.
  - [x] I2.4.c Filter revoked/deprecated-expired/quarantined/incompatible/
        unavailable/not-configured/provider/resource/budget/rights/safety
        candidates with exact reasons. Evidence: eligibility tests.
  - [x] I2.4.d Rank/select deterministically by policy, compatibility, quality,
        latency, cost, capacity, target, and explicit user choice with stable
        tie-break and reason. Evidence: selection fixtures.
  - [x] I2.4.e Return allowed candidate summaries/details appropriate to
        audience and redact graph/node/model/provider internals from curated
        tiers. Evidence: property leak tests.
  - [x] I2.4.f Revalidate exact workflow/version/audience/host/dependencies at
        planning and dispatch; reject direct forbidden IDs. Evidence: bypass/
        race tests.
  - [ ] I2.4.g Emit capability/ineligibility state to UI/API and audit selected/
        rejected workflow decisions. Evidence: end-to-end tests.
        <!-- PRODUCING A DECISION AND EMITTING IT ARE DIFFERENT CLAIMS. THE
        CELL STAYS `[ ]` ON BOTH HALVES.

        evidence/v1-workbenches/isis-workflow-selection-emission.json —
        generate-isis-workflow-selection-emission.mts + .test.mts (9 tests, 3
        ratchets, 3 controls), wired into inventory + verify.

        THE PRIOR NOTE WAS RIGHT ABOUT THE CHAIN AND THAT IS WHAT MAKES THE REST
        A FINDING. Executed: selecting for a curated actor over the reference's
        25 candidates chose one workflow and produced 20 rejections across 19
        distinct codes, and `projectFor` gave the curated actor a summary
        carrying `[assetKind, capabilities, operation, profile, version,
        workflowId]` while the operator's carries the graph, the nodes, the
        model and the provider. The state exists.

        NOT ONE OF THE TWENTY-ONE REJECTION CODES IS EMITTED AS A WORKFLOW
        DECISION. Three of the strings do appear on the Isis surface for other
        subjects — `unavailable` as a status across three dozen stores and
        pages, `quarantined` in the 3D quality gates, `revoked` in a compliance
        report — and none of those files carries a workflow selection anywhere.
        The other eighteen, including `tier-not-exposed`, `entitlement-missing`,
        `classification-too-high` and `rights-uncleared`, appear on the Isis
        surface not at all. No API returns a candidate list with reasons and no
        UI renders one, because there is no vocabulary in the product to render.

        AND THE AUDIT NAMES NO DECISION. The workflow registry writes twelve
        actions into `workflow_audit_logs`: created, deleted, updated,
        workflow.created, workflow.deleted, workflow.restored,
        workflow.template.used, workflow.updated, workflow.version.created,
        workflow.version.deleted, workflow.version.deprecated,
        workflow.version.restored. EVERY ONE IS A MUTATION OF THE WORKFLOW
        RECORD. `workflow.template.used` records that one was used, never that
        others were considered and refused — which is exactly the pair this cell
        asks to be audited.

        TWO NARROWINGS THE MEASUREMENT NEEDED, AND BOTH CHANGED THE ANSWER.
        Counting any file carrying the string reported TWELVE of twenty-one
        codes as emitted, on the strength of `'revoked'` in an Asase API-auth
        module and `'over-budget'` in a localisation check; a carrier must be on
        the Isis surface. Then file-level co-occurrence with selection
        vocabulary still counted `candidateVersion` in a 3D rollback policy and
        `searchCandidates` in a Civitai intake store; the workflow and the
        candidate must now appear on the SAME LINE. Both collision sources are
        pinned by tests, so the narrowing cannot silently drift back. -->
        <!-- The reference is a specification: nothing under apps/ or libs/
        imports evidence/v1-workbenches/workflow-selection/, which the artifact
        measures. -->
- [x] I2.5 Implement model selection through the governed registry. Evidence:
      quarantined, incompatible, unlicensed, or environment-inadmissible models
      are unavailable with reasons.
      <!-- evidence: evidence/v1-workbenches/model-selection/ (compiling reference,
                                                                                                                                                   strict tsc + runnable suite) and model-selection.json via
                                                                                                                                                   `node scripts/v1-workbenches/generate-model-selection.mjs`. All four
                                                                                                                                                   states the parent names are distinct typed reason codes with deliberate
                                                                                                                                                   fixtures (19 refusals); every declared code is reachable. -->
  - [x] I2.5.a Query models/versions/checkpoints/adapters by required workflow/
        node/task/modality/asset/profile/input-output/provider/runtime/hardware
        capabilities. Evidence: candidate query tests.
  - [x] I2.5.b Require approved active lifecycle, verified artifact hash/source/
        signature/security scans, current evaluation, and nonrevoked state.
        Evidence: lifecycle tests.
  - [x] I2.5.c Require compatible architecture/version/precision/format/context/
        adapter/LoRA/node/workflow/provider/endpoint/resource and target.
        Evidence: compatibility tests.
  - [x] I2.5.d Require license/usage/training/derivative/attribution/territory/
        commercial rights and identity/content policy for actor/audience/use.
        Evidence: license tests.
  - [x] I2.5.e Require environment promotion/admission, regional availability,
        entitlement/quota/capacity/budget, and target data residency. Evidence:
        environment tests.
  - [x] I2.5.f Rank allowed candidates deterministically by policy/quality/
        latency/cost/capacity/reproducibility/target and record alternatives/
        rejection reasons. Evidence: selection fixtures.
  - [x] I2.5.g Expose audience-safe metadata and typed reasons for quarantined/
        incompatible/unlicensed/inadmissible/not-configured/unavailable without
        leaking private model/provider detail. Evidence: UI/API tests.
  - [x] I2.5.h Revalidate model/version/hash/licensing/evaluation/promotion/
        endpoint at dispatch and block stale direct references. Evidence: race
        tests.
- [x] I2.6 Implement LoRA/modifier selection only in authorized AAA/operator
      hosts. Evidence: curated payload leak audit passes.
      <!-- evidence: evidence/v1-workbenches/modifier-selection/ (compiling
                                                                                                                                               reference, strict tsc + runnable suite) and modifier-selection.json via
                                                                                                                                               `node scripts/v1-workbenches/generate-modifier-selection.mjs`. The leak
                                                                                                                                               audit is type-level (curated contracts declare no modifier field) plus a
                                                                                                                                               scan with a negative control, and no curated probe can distinguish a real
                                                                                                                                               modifier id from a fictional one (zero registry calls). -->
  - [x] I2.6.a Register LoRA/adapter/modifier ID/version/hash/source/base-model/
        target-module/rank/trigger/strength range/license/safety/identity/style/
        asset/workflow compatibility and lifecycle. Evidence: modifier schema.
  - [x] I2.6.b Resolve operator and AAA actor/host/tier/entitlement/tenant/
        project/use authorization at discovery and mutation. Evidence:
        authorization tests.
  - [x] I2.6.c Filter approved/nonquarantined/compatible/licensed/environment-
        admitted/provider-supported modifiers and multi-modifier combinations.
        Evidence: eligibility tests.
  - [x] I2.6.d Expose typed search/select/order/weight/schedule/mask/region/
        enable/preset controls with ranges and effective influence diagnostics.
        Evidence: AAA/operator UI tests.
  - [x] I2.6.e Revalidate exact modifier versions/hashes/combination/audience at
        planning/dispatch and persist effective provenance/parameters. Evidence:
        dispatch tests.
  - [x] I2.6.f Omit modifier IDs/names/hashes/triggers/weights/schedules/base-
        model linkage and unsupported hints entirely from curated contracts/
        routes/bundles/telemetry. Evidence: curated leak scan.
  - [x] I2.6.g Reject direct API/query/body/deep-link/import attempts from
        curated/contemplative users without revealing existence. Evidence:
        negative tests.
- [x] I2.7 Implement sampler and advanced node controls only in authorized
      hosts. Evidence: route and property authorization tests.
      <!-- evidence: evidence/v1-workbenches/advanced-controls/ (compiling
                                                                                                                                           reference, strict tsc + adversarial suite) and advanced-controls.json via
                                                                                                                                           `node scripts/v1-workbenches/generate-advanced-controls.mjs`. Route
                                                                                                                                           authorization is the 2-host x 4-verb matrix; property authorization is
                                                                                                                                           the allow-list guard, which closes direct/alias/nested/unknown-field/
                                                                                                                                           mass-assignment routes including a real own __proto__ key. -->
  - [x] I2.7.a Register sampler/scheduler/step/CFG/seed/denoise/precision/batch/
        node/port/parameter/subgraph/custom-node controls with versions/types/
        units/ranges/compatibility/risk/resource/cost. Evidence: control schema.
  - [x] I2.7.b Assign operator versus AAA view/edit/import/execute rights per
        workflow class/node/control and prohibit curated/contemplative exposure.
        Evidence: permission matrix.
  - [x] I2.7.c Build typed controls with validation/default provenance/
        dependency constraints/effective values/cost-quality-latency impact and
        no arbitrary JSON/code. Evidence: UI/API tests.
  - [x] I2.7.d Enforce node catalog/signature/license/security/sandbox/network/
        filesystem/resource policy and graph compatibility. Evidence: custom
        node security tests.
  - [x] I2.7.e Revalidate actor/host/workflow/version/node/parameter/policy/
        capacity at plan/dispatch and persist effective graph/controls.
        Evidence: dispatch tests.
  - [x] I2.7.f Remove/redact controls and raw values from lower-tier responses,
        errors, URLs, imports/exports, provenance summaries, logs, and
        capability hints. Evidence: leak tests.
  - [x] I2.7.g Test route, object/property/function authorization and direct/
        nested/alias/unknown-field/mass-assignment bypass. Evidence: adversarial
        authorization suite.
- [x] I2.8 Implement best-of-N variant requests with estimated cost and resource
      reservation. Evidence: dispatch requires explicit confirmation above the
      configured threshold.
      <!-- evidence: evidence/v1-workbenches/best-of-n/ (compiling reference,
                                                                                                                                       strict tsc + runnable suite) and best-of-n.json via
                                                                                                                                       `node scripts/v1-workbenches/generate-best-of-n.mjs`. The confirmation
                                                                                                                                       carries a digest of the estimate it approved, so a token issued for one
                                                                                                                                       estimate cannot authorize a changed one (count grew, or a unit price
                                                                                                                                       moved); all four threshold kinds are proven reachable. -->
  - [x] I2.8.a Define requested count, candidate count, diversity strategy,
        selection strategy, seed policy, per-variant controls, and hard maximums
        as typed fields. Evidence: best-of-N contract tests.
  - [x] I2.8.b Expand one request into deterministic candidate plans with unique
        run/output identities and shared request/brief lineage. Evidence:
        expansion fixtures.
  - [x] I2.8.c Estimate compute, provider, storage, egress, evaluation, and
        post-processing cost for the complete candidate set before reservation.
        Evidence: estimate tests.
  - [x] I2.8.d Reserve quota, budget, queue, GPU/CPU/VRAM, and storage for the
        confirmed set atomically or return an exact shortfall. Evidence:
        reservation contention tests.
  - [x] I2.8.e Require typed confirmation when configured count/cost/resource/
        latency thresholds are crossed and bind it to the unchanged estimate.
        Evidence: stale-confirmation tests.
  - [x] I2.8.f Dispatch only confirmed/reserved candidates with bounded fan-out,
        cancellation, partial-failure, retry, and reservation-release behavior.
        Evidence: execution race tests.
  - [x] I2.8.g Reconcile actual per-variant and aggregate usage/cost, preserving
        failed/cancelled candidates and selection state. Evidence:
        reconciliation fixtures.
- [x] I2.9 Implement manual and generated variant grouping. Evidence: generated
      scores remain provider/gate values and manual variants show score absence.
      <!-- evidence: evidence/v1-workbenches/variant-grouping/ (compiling
                                                                                                                                   reference, strict tsc + runnable suite) and variant-grouping.json via
                                                                                                                                   `node scripts/v1-workbenches/generate-variant-grouping.mjs`. ManualVariant
                                                                                                                                   declares no generation-provenance or score field, so a fabricated run or
                                                                                                                                   score is a compile error; measurements keep provider/gate attribution and
                                                                                                                                   absence survives aggregation. -->
  - [x] I2.9.a Define a stable variant-set identity, ordering, labels, roles,
        parent/revision links, source discriminator, and membership rules.
        Evidence: grouping schema tests.
  - [x] I2.9.b Add authorized manual assets without inventing generation run,
        model, seed, provider, prompt, or automated score provenance. Evidence:
        manual variant fixtures.
  - [x] I2.9.c Add generated assets only through immutable run/output lineage
        and retain provider measurements and gate results as attributed values.
        Evidence: generated variant fixtures.
  - [x] I2.9.d Prevent manual edits, regrouping, imports, or API payloads from
        rewriting source identity or converting missing scores into zero/pass.
        Evidence: provenance mutation tests.
  - [x] I2.9.e Enforce asset-kind/profile/comparison compatibility while
        preserving mixed-source groups explicitly allowed by policy. Evidence:
        compatibility tests.
  - [x] I2.9.f Record membership, reorder, remove, merge, split, and archive
        decisions without deleting historical group state. Evidence: audit
        history tests.
- [ ] I2.10 Implement side-by-side and A/B comparison with synchronized
      image/video/audio/3D controls. Evidence: selection and playback state are
      stable and accessible.
      <!-- evidence (a-e, h): evidence/v1-workbenches/comparison/ (compiling
                                                                                                                               reference, strict tsc + runnable suite) and comparison.json via
                                                                                                                               `node scripts/v1-workbenches/generate-comparison.mjs`.
                                                                                                                               runtime: f and g name an ACCESSIBLE audit and keyboard-focus /
                                                                                                                               screen-reader behaviour on a rendered surface, which no pure model can
                                                                                                                               establish; their data models (NonvisualPanel, RestorableState) are built
                                                                                                                               and asserted here so the runtime audit has something to run against. The
                                                                                                                               parent stays unchecked because its bar ends "and accessible". -->
  - [x] I2.10.a Define comparison session, participant, source revision, layout,
        synchronized state, annotation, and decision contracts. Evidence:
        comparison schema tests.
  - [x] I2.10.b Implement image pan/zoom/fit/pixel/alpha/difference controls
        synchronized in common coordinates with deliberate unlinking. Evidence:
        image interaction tests.
  - [x] I2.10.c Implement video play/pause/scrub/frame/timecode/rate/loop/audio
        controls with duration/rate mismatch policies. Evidence: video sync
        tests.
  - [x] I2.10.d Implement audio
        play/pause/scrub/loop/gain/mute/channel/waveform/ loudness controls
        without auto-normalizing away measured differences. Evidence: audio
        comparison tests.
  - [x] I2.10.e Implement 3D camera/orbit/pan/zoom/light/background/material/
        animation/LOD/wireframe controls with unit/axis/bounds reconciliation.
        Evidence: 3D comparison tests.
  - [ ] I2.10.f Present nonvisual metadata, provenance, raw gate values,
        thresholds, uncertainty, rights, and target compatibility alongside the
        media. Evidence: accessible comparison audit.
        <!-- WHAT IS BUILT IS THE PANEL, NOT THE PRESENTATION. THE CELL STAYS
        `[ ]`.

        evidence/v1-workbenches/isis-comparison-nonvisual.json —
        generate-isis-comparison-nonvisual.mts + .test.mts (7 tests, 3 ratchets,
        2 controls), wired into inventory + verify. The parent's note said no
        pure model can establish behaviour on a rendered surface. That is true
        of ACCESSIBILITY and not of PRESENCE: a page that never names
        provenance, a threshold or a licence does not render them, however it is
        rendered.

        THE MODEL IS GOOD AND WAS EXECUTED. The reference `NonvisualPanel`
        carries a provenance summary, gate readings, a rights summary and both
        the compatible AND the incompatible targets. Each `GateReading` carries
        the raw value, the threshold it was judged against and the uncertainty
        of the measurement together — a bare pass/fail would hide all three —
        and a missing measurement is a `null`, not a zero.

        FIVE OF THE SEVEN ITEMS ARE NAMED BY NO ISIS COMPARISON SURFACE. There
        are three: `OutputComparisonWorkspacePage`,
        `MeshTransformerAbComparisonPage` and
        `StudioIsisModelComparisonWorkspace`. Only two items match at all, and
        both loosely — `nonvisual metadata` on the generic words label, metadata
        and summary, and `target compatibility` on the model-comparison
        workspace. PROVENANCE, RAW GATE VALUES, THRESHOLDS, UNCERTAINTY AND
        RIGHTS APPEAR IN NONE OF THEM: the 4,969-line output comparison
        workspace mentions provenance, a threshold, uncertainty, rights and a
        licence exactly zero times each, which the test asserts word by word.
        And nothing under apps/ or libs/ imports the reference.

        A CONTROL THAT CHANGED THE ANSWER. Comparison pages exist all over the
        estate — Demeter compares gardens, Lilith compares research synopses,
        Hathor compares options — and auditing all 22 of them reported FIVE of
        the seven items as presented, by surfaces with nothing to do with
        generation output. The audit is scoped to the Isis surfaces, and story
        files and `__fixtures__` hosts are excluded because a fixture is not a
        surface a user reaches.

        NOT CLAIMED HERE: whether the panel, once rendered, is reachable by
        keyboard and announced by a screen reader. That is I2.10.g and it needs
        the browser harness. -->
  - [ ] I2.10.g Preserve keyboard focus, screen-reader labels, participant
        identity, synchronized state, and current selection across rerender,
        reconnect, and permitted deep link. Evidence: accessibility/state tests.
  - [x] I2.10.h Commit A/B preference or multi-candidate selection only against
        the exact compared revisions and reject stale/unauthorized choices.
        Evidence: selection concurrency tests.
- [x] I2.11 Implement decision records for select, reject, park, or regenerate.
      Evidence: chosen and rejected output IDs remain linked.
      <!-- evidence: evidence/v1-workbenches/decision-records/ (compiling
                                                                                                                           reference, strict tsc + runnable suite) and decision-records.json via
                                                                                                                           `node scripts/v1-workbenches/generate-decision-records.mjs`. Linkage is
                                                                                                                           enforced at write time (every compared candidate must appear as chosen or
                                                                                                                           nonselected) and as a reachability invariant asserted over a four-decision
                                                                                                                           select/reject/park/regenerate chain. -->
  - [x] I2.11.a Define immutable decision identity, actor/authority, timestamp,
        compared set/revisions, disposition, reasons, evidence, comments, and
        supersession linkage. Evidence: decision schema tests.
  - [x] I2.11.b Implement select with one or explicitly multiple chosen output
        revisions and retain all nonselected candidates. Evidence: selection
        tests.
  - [x] I2.11.c Implement reject with typed reason codes, optional annotations,
        remediation, and no asset deletion. Evidence: rejection tests.
  - [x] I2.11.d Implement park with owner, review trigger/date, hold reason, and
        restoration semantics. Evidence: parked-state tests.
  - [x] I2.11.e Implement regenerate as a new request/run linked to selected
        source inputs, requested changes, and preserved prior output. Evidence:
        regeneration lineage tests.
  - [x] I2.11.f Enforce actor/tier/project/state permissions and optimistic
        concurrency for each disposition. Evidence: authorization/race tests.
  - [x] I2.11.g Surface current and superseded decisions in UI/API/export/audit
        without allowing later outcomes to rewrite history. Evidence: history
        projection tests.
- [x] I2.12 Implement prompt/version diff. Evidence: differences distinguish
      user brief, compiled spec, workflow, model, seed, and policy.
      <!-- evidence: evidence/v1-workbenches/version-diff/ (compiling reference,
                                                                                                                       strict tsc + runnable suite) and version-diff.json via
                                                                                                                       `node scripts/v1-workbenches/generate-version-diff.mjs`. Layer attribution
                                                                                                                       is proven by single-layer fixtures asserting ONLY that layer moved; the
                                                                                                                       policy-only verdict is the one certain attribution and its negative case
                                                                                                                       deliberately flips a verdict AND changes the artifact. -->
  - [x] I2.12.a Resolve two authorized immutable run/output/brief revisions and
        reject incomparable or redacted targets. Evidence: diff input tests.
  - [x] I2.12.b Diff user-authored brief fields with semantic field/list/unit/
        reference changes distinct from prose formatting. Evidence: brief diff
        fixtures.
  - [x] I2.12.c Diff canonical compiled specs including derived/defaulted/
        omitted/unsupported fields and compiler/template versions. Evidence:
        spec diff fixtures.
  - [x] I2.12.d Diff workflow template/version/graph/control/dependency changes
        at the highest detail authorized for the audience. Evidence: workflow
        diff tests.
  - [x] I2.12.e Diff model/checkpoint/adapter/modifier/provider/runtime changes
        with audience-appropriate redaction. Evidence: model diff tests.
  - [x] I2.12.f Diff seed/randomness/candidate/conditioning and effective
        parameter changes without implying causation. Evidence: execution diff
        tests.
  - [x] I2.12.g Diff policy/code/evaluator/rights/safety/target-profile versions
        and distinguish policy-only verdict changes. Evidence: policy diff
        tests.
  - [x] I2.12.h Render added/removed/changed/unchanged/unknown/redacted states
        accessibly and export the same typed diff. Evidence: UI/API parity
        tests.
- [x] I2.13 Implement reusable brief presets with inheritance and code floors.
      Evidence: preset overrides cannot loosen safety or rights requirements.
      <!-- evidence: evidence/v1-workbenches/brief-presets/ (compiling reference,
                                                                                                           strict tsc + runnable suite) and brief-presets.json via
                                                                                                           `node scripts/v1-workbenches/generate-brief-presets.mjs`. Floors are
                                                                                                           re-applied after EVERY inheritance step and again after user overrides; a
                                                                                                           leaf that loosens all seven floor domains is clamped on every one, and
                                                                                                           each clamp records which step attempted it. -->
  - [x] I2.13.a Define preset identity, scope, owner, audience, asset/workflow
        compatibility, version, lifecycle, typed values, parent, and code-floor
        references. Evidence: preset schema tests.
  - [x] I2.13.b Resolve single-parent inheritance with cycle/depth detection and
        exact version pinning. Evidence: inheritance tests.
  - [x] I2.13.c Merge scalar/list/map/reference/constraint fields with explicit
        per-field replace/append/remove/lock rules. Evidence: merge fixtures.
  - [x] I2.13.d Enforce safety, rights, consent, privacy, retention, budget, and
        delivery code floors after every inheritance and user override step.
        Evidence: floor-bypass tests.
  - [x] I2.13.e Preview inherited/defaulted/overridden/locked/effective values
        and their provenance before apply. Evidence: preset preview tests.
  - [x] I2.13.f Version edits immutably, migrate or pin dependent briefs
        explicitly, and preserve reproducibility of historical runs. Evidence:
        version/migration tests.
  - [x] I2.13.g Authorize create/read/update/archive/share/import/export by
        personal/project/tenant/system scope and audience. Evidence: scope
        authorization tests.
- [x] I2.14 Implement import/export of portable brief specs. Evidence:
      round-trip is versioned, validated, and strips secrets.
      <!-- evidence: evidence/v1-workbenches/portable-brief/ (compiling reference,
                                                                                                       strict tsc + runnable suite) and portable-brief.json via
                                                                                                       `node scripts/v1-workbenches/generate-portable-brief.mjs`. The portable
                                                                                                       schema has no field for any secret category; round-trip is compared
                                                                                                       against a frozen golden canonical body, and an omitted field is asserted
                                                                                                       to remain omitted rather than becoming a default. -->
  - [x] I2.14.a Define a versioned portable envelope with manifest, canonical
        brief spec, referenced immutable identities, compatibility metadata,
        provenance summary, and integrity hash. Evidence: envelope schema.
  - [x] I2.14.b Export only audience-authorized portable fields and omit
        credentials, signed URLs, provider internals, private prompts, hidden
        policy, tenant metadata, and nonportable local paths. Evidence: secret/
        property leak tests.
  - [x] I2.14.c Represent unavailable, redacted, external, and intentionally
        omitted references explicitly rather than embedding mutable links.
        Evidence: reference portability tests.
  - [x] I2.14.d Parse imports with size/depth/count/format/unknown-field limits,
        version compatibility, signature/hash checks, and safe archive handling.
        Evidence: hostile import tests.
  - [x] I2.14.e Validate imported typed values, rights/consent, asset/workflow/
        model references, code floors, and target compatibility before preview.
        Evidence: import validation tests.
  - [x] I2.14.f Preview migrations, substitutions, redactions, missing
        dependencies, conflicts, and effective values before authorized commit.
        Evidence: import preview tests.
  - [x] I2.14.g Round-trip canonical supported specs byte-semantically while
        preserving versions and never converting omissions to defaults.
        Evidence: golden round-trip suite.
- [ ] I2.15 Implement continuity-profile authoring for characters, wardrobe,
      props, environments, lighting, palette, lens, camera, and motion across
      shots or asset sets. Evidence: profile revisions, drift thresholds,
      diagnostics, and authorized overrides remain linked to every output.
      <!-- evidence (a-m): evidence/v1-workbenches/continuity/ (compiling reference,
                                                                                                   strict tsc + runnable suite) and continuity.json via
                                                                                                   `node scripts/v1-workbenches/generate-continuity.mjs`. The parent's own
                                                                                                   bar is k+l+m and is proven as a linkage invariant: every consumer kind
                                                                                                   pins an exact revision, overrides change status only, and an unmeasured
                                                                                                   dimension reports unavailable rather than zero.
                                                                                                   runtime: n names an ACCESSIBLE end-to-end test of a rendered authoring/
                                                                                                   review surface, which no pure model can establish. -->
      <!-- SPEC 2026-09-11: parent re-opened. I2.15.n is `[ ]`, and §2.1 forbids
      a checked parent while any child is unchecked. Every other child stays as
      it was; the parent flips again only when I2.15.n does. -->
  - [x] I2.15.a Define versioned continuity-profile identity, scope, owners,
        target asset/sequence membership, reference sources, constraints,
        thresholds, overrides, and lifecycle. Evidence: profile schema.
  - [x] I2.15.b Author character identity, anatomy, age presentation, facial/
        body features, silhouette, scale, pose, expression, and consent-bound
        reference constraints. Evidence: character continuity tests.
  - [x] I2.15.c Author wardrobe silhouette, garment layers, materials, colors,
        patterns, accessories, state/damage, and shot-to-shot change rules.
        Evidence: wardrobe tests.
  - [x] I2.15.d Author prop identity, dimensions, materials, markings,
        handedness, ownership, placement, interaction, and state transitions.
        Evidence: prop tests.
  - [x] I2.15.e Author environment layout, scale, geography, architecture,
        set-dressing anchors, weather, time, and persistent state. Evidence:
        environment tests.
  - [x] I2.15.f Author lighting source/type/position/direction/color/intensity/
        exposure/shadow/atmosphere continuity and intended changes. Evidence:
        lighting tests.
  - [x] I2.15.g Author palette tokens, color-space values, allowed variance,
        semantic usage, accessibility constraints, and intentional exceptions.
        Evidence: palette tests.
  - [x] I2.15.h Author lens/focal/aperture/focus/distortion/sensor/aspect/crop
        continuity in explicit units. Evidence: lens tests.
  - [x] I2.15.i Author camera position/orientation/height/distance/framing/
        eyeline/screen direction/axis and transition rules. Evidence: camera
        tests.
  - [x] I2.15.j Author subject/object/camera motion paths, timing, velocity,
        acceleration, easing, cycles, and collision/contact constraints.
        Evidence: motion tests.
  - [x] I2.15.k Version profile edits immutably and record which exact revision
        each plan, run, shot, output, and evaluation consumed. Evidence:
        revision-lineage tests.
  - [x] I2.15.l Calculate typed per-dimension drift measurements, thresholds,
        uncertainty, unavailable status, and cross-shot/set diagnostics.
        Evidence: calibrated drift fixtures.
  - [x] I2.15.m Authorize bounded, reasoned, expiring dimension-level overrides
        without changing raw measurements or profile history. Evidence: override
        authorization tests.
  - [ ] I2.15.n Present effective constraints, conflicts, drift, overrides, and
        remediation in authoring/comparison/review views. Evidence: accessible
        end-to-end continuity tests.
        <!-- TWO OF THE FIVE ITEMS HAVE NO WORD IN THE MODEL AND ONE OF THE
        THREE VIEWS DOES NOT EXIST. THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-continuity-presentation.json —
        generate-isis-continuity-presentation.mts + .test.mts (8 tests, 3
        ratchets, 2 controls), wired into inventory + verify. The parent's note
        said an accessible end-to-end test of a rendered surface is beyond a
        pure model, which is true of ACCESSIBILITY and hides two other
        questions: whether the model can express the item at all, and whether a
        view NAMES it.

        NOTHING IN THE CONTINUITY REFERENCE NAMES A REMEDIATION — not directly,
        and not as a next-step or suggested-fix field. NOTHING RESOLVES OR
        MERGES CONSTRAINTS INTO AN EFFECTIVE SET either: the bare word
        `constraint` is deliberately refused as a synonym, because every
        constraint interface carries it and accepting it would report the item's
        INPUTS as the item. A view cannot present a concept the model has no
        word for.

        A NAME IS NOT THE THING IT NAMES, IN BOTH DIRECTIONS. `conflicts` IS
        modelled — the estate calls them `AuthoringIssue`s — and reporting it
        absent because the word differs would be as wrong as crediting a
        near-miss. The row records which term matched.

        THERE IS NO REVIEW VIEW. Of the Isis surfaces carrying continuity state,
        two are authoring (`JobSubmissionStudio`,
        `StudioIsisAutoRiggingCorrectionStage`), one is comparison
        (`OutputComparisonWorkspacePage`) and NONE is a review, approval or
        signoff surface. One of the three places the cell names has no candidate
        at all, so nothing can be presented in all three. Three further surfaces
        carry continuity and fit no kind (`JobsPage`, `OutputsPage`,
        `WorkflowDetailsPage`); they are recorded rather than dropped.

        AND THE THREE MODELLED ITEMS REACH ONE VIEW KIND. Conflicts, drift and
        overrides are named by the authoring surfaces and by NEITHER the
        comparison one nor a review one — so the comparison view, which does
        exist, shows continuity without showing what drifted.

        The model is sound, which is what makes the gaps mean something:
        executed, `reviseProfile` produced revision key `cp-1@3` from `cp-1@2`
        rather than editing in place, and an unmeasured dimension is a distinct
        fixture from a drifted one.

        STILL OUT OF REACH HERE: whether these panels, once rendered, are
        keyboard-reachable and announced. That is the "accessible" half and it
        needs the browser harness. -->
- [x] I2.16 Implement authorized conditioning stacks for pose, depth, edges,
      line art, segmentation, normals, style, face identity, masks, camera
      paths, motion vectors, and multi-control strength schedules. Evidence:
      typed UI/API parity and effective-control provenance.
      <!-- evidence: evidence/v1-workbenches/conditioning/ (compiling reference,
                                                                                               strict tsc + runnable suite) and conditioning.json via
                                                                                               `node scripts/v1-workbenches/generate-conditioning.mjs`. Every convention
                                                                                               is declared per layer; a mismatch is converted by a named transform that
                                                                                               is recorded, or refused. UI and API project one resolved stack, and the
                                                                                               parity fixture carries two layers of one kind so the check is not
                                                                                               vacuous. -->
  - [x] I2.16.a Define ordered conditioning-stack and layer contracts with kind,
        source/version/hash, preprocessing, spatial/temporal scope, strength
        schedule, blend, model/workflow compatibility, and provenance. Evidence:
        conditioning schema tests.
  - [x] I2.16.b Implement pose controls for skeleton format, keypoints,
        confidence, people/track association, occlusion, transforms, and frame
        range. Evidence: pose fixtures.
  - [x] I2.16.c Implement depth controls for units, range, inversion, clipping,
        normalization, confidence, camera, and resolution alignment. Evidence:
        depth fixtures.
  - [x] I2.16.d Implement edge and line-art controls for detector/version,
        threshold, scale, polarity, cleanup, weight, and resolution. Evidence:
        edge/line fixtures.
  - [x] I2.16.e Implement segmentation controls for label vocabulary/version,
        instance IDs, confidence, palette mapping, unknown regions, and masks.
        Evidence: segmentation fixtures.
  - [x] I2.16.f Implement normal controls for tangent/object/world space, axes,
        handedness, range, camera, confidence, and transform. Evidence: normal
        fixtures.
  - [x] I2.16.g Implement style controls with authorized reference roles,
        strength/schedule, region scope, protected attributes, and rights.
        Evidence: style conditioning tests.
  - [x] I2.16.h Implement face-identity controls with identity subject, consent,
        reference quality, region/tracking, strength limits, and
        audience/policy. Evidence: identity safety tests.
  - [x] I2.16.i Implement raster/vector/semantic masks with coordinate space,
        feather/dilate/erode/invert, frame range, version, and edit history.
        Evidence: mask tests.
  - [x] I2.16.j Implement camera-path controls with units, coordinate system,
        transforms, lens, interpolation, timestamps, collision, and bounds.
        Evidence: camera-path tests.
  - [x] I2.16.k Implement motion-vector controls with direction convention,
        units, scale, frame correspondence, confidence, occlusion, and bounds.
        Evidence: motion-vector tests.
  - [x] I2.16.l Validate multiple controls for ordering, compatible models/
        preprocessors, coordinate/time alignment, aggregate strength/resource
        limits, and conflicts. Evidence: multi-control tests.
  - [x] I2.16.m Keep UI and API typed controls/defaults/errors/effective values
        equivalent and revalidate the exact stack at dispatch. Evidence:
        UI/API/dispatch parity tests.
- [x] I2.17 Implement shot, storyboard, animatic, and sequence authoring with
      shot order, duration, lens, camera motion, continuity, patch rerender,
      editorial timing, and render-pass requirements. Evidence: sequence
      rerender preserves unaffected shot identities and lineage.
      <!-- evidence: evidence/v1-workbenches/sequence/ (compiling reference, strict
                                                                                           tsc + runnable suite) and sequence.json via
                                                                                           `node scripts/v1-workbenches/generate-sequence.mjs`. Invalidation
                                                                                           propagates only along declared edges, and the identity invariant compares
                                                                                           OBJECT identity so a structurally identical re-mint is still caught. -->
  - [x] I2.17.a Define versioned sequence, shot, board panel, animatic segment,
        track, transition, render requirement, and dependency contracts.
        Evidence: sequence schema tests.
  - [x] I2.17.b Author stable shot identity, order, grouping, branch,
        dependency, status, owner, and source revision with conflict-safe
        reordering. Evidence: shot-order tests.
  - [x] I2.17.c Author duration using frames/timecode/seconds with sequence
        rate, handles, source/record ranges, retime, and transition overlap.
        Evidence: timing tests.
  - [x] I2.17.d Author per-shot lens/sensor/aperture/focus/aspect/framing values
        in explicit units with continuity-profile linkage. Evidence: lens tests.
  - [x] I2.17.e Author camera
        pose/path/interpolation/constraints/shake/collision and preview it in
        shot context. Evidence: camera-motion tests.
  - [x] I2.17.f Author storyboard panels with frame/shot linkage, composition,
        action/dialogue/notes, references, annotations, and revision history.
        Evidence: storyboard tests.
  - [x] I2.17.g Assemble animatic picture/dialogue/SFX/music/caption tracks with
        sync, gain, transitions, placeholders, and missing-media states.
        Evidence: animatic playback tests.
  - [x] I2.17.h Apply character/wardrobe/prop/environment/light/palette/camera/
        motion continuity profiles and show shot-level conflicts/drift.
        Evidence: sequence continuity tests.
  - [x] I2.17.i Define
        beauty/matte/depth/normal/motion/object/cryptomatte/audio/ caption and
        target-specific render-pass requirements. Evidence: pass manifest tests.
  - [x] I2.17.j Plan a patch rerender from exact changed shots/frames/passes and
        downstream dependencies without silently widening scope. Evidence:
        invalidation graph tests.
  - [x] I2.17.k Preserve unaffected shot/output identities, regenerate affected
        identities, and link old/new plans/runs/assets/edits. Evidence: patch
        lineage tests.
  - [x] I2.17.l Export editorial interchange and review proxies with stable IDs,
        time bases, handles, media refs, captions, and round-trip validation.
        Evidence: editorial round-trip tests.
- [x] I2.18 Implement advanced 3D project authoring for reconstruction, scene
      assembly, part/semantic edits, texture/material generation, relighting,
      topology/LOD, rigging, splat editing, and engine/DCC packaging. Evidence:
      every stage is non-destructive, previewable, cancellable, and separately
      attributable.
      <!-- evidence: evidence/v1-workbenches/project3d/ (compiling reference, strict
                                                                           tsc + runnable suite) and project3d.json via
                                                                           `node scripts/v1-workbenches/generate-project3d.mjs`. A stage never
                                                                           mutates its input, so cancel/fail preserves the source and every prior
                                                                           stage; downstream invalidation follows declared edges only. -->
  - [x] I2.18.a Define versioned 3D project, scene, object, component, geometry,
        material, rig, animation, camera, light, edit layer, dependency, and
        package contracts. Evidence: 3D project schemas.
  - [x] I2.18.b Author reconstruction inputs, calibration, masks, scale, bounds,
        quality target, reconstruction method, uncertainty, and source linkage.
        Evidence: reconstruction tests.
  - [x] I2.18.c Assemble referenced or embedded scenes with stable hierarchy,
        instances, transforms, units, axes, variants, dependencies, and conflict
        handling. Evidence: assembly tests.
  - [x] I2.18.d Apply part-level and semantic select/mask/add/remove/replace/
        transform/deform edits as reversible versioned layers. Evidence:
        semantic edit tests.
  - [x] I2.18.e Generate/edit texture sets and materials with UV/UDIM mapping,
        channel semantics, color space, resolution, tiling, seams, baking, and
        source rights. Evidence: material tests.
  - [x] I2.18.f Author relighting with environment/source lights, units, color,
        exposure, shadows, reflections, baking, and original-light preservation.
        Evidence: relighting tests.
  - [x] I2.18.g Author topology repair/retopology/remesh/decimation/subdivision,
        normals, UV, manifold, and target polygon constraints non-destructively.
        Evidence: topology tests.
  - [x] I2.18.h Generate and validate ordered LODs, screen thresholds, reduction
        settings, material/UV preservation, collision, and target budgets.
        Evidence: LOD tests.
  - [x] I2.18.i Author skeleton/rig/control/skin/constraint/blend-shape and
        retargeting stages with bind/reference pose and deformation preview.
        Evidence: rigging tests.
  - [x] I2.18.j Edit gaussian-splat crop/mask/transform/opacity/SH/compression/
        density/cleanup/merge layers with renderer preview. Evidence: splat edit
        tests.
  - [x] I2.18.k Package glTF/GLB/USD/USDZ/FBX and governed DCC/engine targets
        with dependencies, manifests, hashes, versions, licenses, and import
        validation. Evidence: packaging round-trip tests.
  - [x] I2.18.l Give every stage its own plan/run/output/edit identity, preview,
        progress, cancellation, retry, measurements, cost, and attribution.
        Evidence: stage lifecycle tests.
  - [x] I2.18.m Preserve source and prior stage revisions on failure/cancel/
        replace and make downstream invalidation explicit. Evidence: destructive
        edit/race tests.
- [ ] I2.19 Implement audio/voice/music project authoring for SFX, foley,
      dialogue, dubbing, TTS, cloned voice, conversational tracks, music cues,
      stems, pronunciation, timing, loudness target, and mix package. Evidence:
      consent, rights, multi-track preview, and output-bundle tests.
      <!-- evidence (a-m, o): evidence/v1-workbenches/audio/ (compiling reference,
                                                                   strict tsc + runnable suite) and audio.json via
                                                                   `node scripts/v1-workbenches/generate-audio.mjs`. BS.1770 gated loudness
                                                                   is verified against the standard's own calibration; consent is required by
                                                                   the type and revocation beats a live term; the bundle cannot ship an
                                                                   unmeasured compliance.
                                                                   runtime: n names playback/accessibility tests of a rendered multitrack
                                                                   surface, which no pure model can establish. -->
      <!-- SPEC 2026-09-11: parent re-opened. I2.19.n is `[ ]`, and §2.1 forbids
      a checked parent while any child is unchecked. Every other child stays as
      it was; the parent flips again only when I2.19.n does. -->
  - [x] I2.19.a Define versioned audio project, timeline, track, clip, take,
        speaker/voice, cue, stem, automation, mix, transcript, caption, and
        delivery contracts. Evidence: audio project schemas.
  - [x] I2.19.b Author SFX cues with event, perspective, environment, intensity,
        duration, timing, variation, loop, source/generation, and rights.
        Evidence: SFX tests.
  - [x] I2.19.c Author foley cues with performer/object/surface/action/contact/
        perspective/timing layers and picture sync. Evidence: foley tests.
  - [x] I2.19.d Author dialogue takes with speaker identity, script revision,
        language, performance direction, pronunciation, timing, room tone,
        channel, and consent. Evidence: dialogue tests.
  - [x] I2.19.e Author dubbing with source segment/transcript/translation,
        speaker/voice mapping, lip/timing constraints, adaptation notes, and
        language review. Evidence: dubbing tests.
  - [x] I2.19.f Author TTS with approved voice/version, text/SSML subset,
        language, pronunciation lexicon, style/rate/pitch controls, and policy.
        Evidence: TTS tests.
  - [x] I2.19.g Author cloned/synthetic identity voice only with verified
        subject consent/scope/term/revocation, reference rights,
        watermark/disclosure, audience, and use policy. Evidence: cloned-voice
        safety tests.
  - [x] I2.19.h Author conversational multi-speaker turns with stable speaker/
        voice mapping, overlap, pauses, interruption, room, continuity, and
        disclosure. Evidence: conversation tests.
  - [x] I2.19.i Author music cues with brief, tempo/meter/key, form, duration,
        hit points, instrumentation, mood, references/rights, loop, and ending.
        Evidence: music cue tests.
  - [x] I2.19.j Define dialogue/music/SFX/foley/ambience/aux/master stems and
        clip/track/bus routing without destructive flattening. Evidence: stem
        routing tests.
  - [x] I2.19.k Maintain versioned language/speaker pronunciation lexicons with
        phonemes, stress, substitutions, context, approval, and provenance.
        Evidence: pronunciation tests.
  - [x] I2.19.l Author frame/timecode/sample/beat clip timing, trims, fades,
        crossfades, stretch, alignment, and synchronization in explicit units.
        Evidence: timeline edit tests.
  - [x] I2.19.m Author target loudness/true-peak/dialogue-gated/dynamic-range/
        channel/layout/sample/codec constraints and measure rather than assume
        compliance. Evidence: calibrated loudness tests.
  - [ ] I2.19.n Preview synchronized multitrack playback, mute/solo/gain/pan/
        automation/waveform/transcript/captions accessibly and consistently.
        Evidence: playback/accessibility tests.
        <!-- THERE IS NO MULTITRACK PREVIEW ON THE ISIS SURFACE TO TEST FOR
        ACCESSIBILITY. THE CELL STAYS `[ ]`.

        evidence/v1-workbenches/isis-multitrack-preview.json —
        generate-isis-multitrack-preview.mts + .test.mts (7 tests, 2 ratchets, 1
        control), wired into inventory + verify. A control a surface does not
        have cannot be tested for accessibility, so the two questions in front
        of the browser are whether the model carries each control and whether
        any Isis preview surface names it.

        THE MODEL CARRIES SEVEN OF THE EIGHT. Mute, solo, gain, pan, waveform,
        transcript and captions are all fields or checks of the audio reference.
        AUTOMATION IS NAMED NOWHERE IN IT — a mix with no automation curve
        cannot have one previewed, so that control is absent before any surface
        question arises.

        AND ONE OF THE EIGHT REACHES A SURFACE. Four Isis surfaces carry any
        playback vocabulary at all; exactly one names a waveform
        (`StudioIsisModelComparisonWorkspace`), and NONE names a mute, a solo, a
        gain, a pan, a transcript or a caption. A multitrack component DOES
        exist in the estate — `libs/yemaya/ui/src/components/MultiTrack` and the
        studio source timeline — so the absence here is a choice rather than a
        missing capability.

        THE AUDIO MODEL IS MEASURED, NOT ASSERTED, which is what makes the
        surface gap the finding. Executed: a BS.1770-calibrated block reads
        -3.01 LKFS, and three blocks including a silent one leave two after the
        absolute gate and one after the relative one — silence is gated out
        rather than averaged in.

        A CONTROL THAT CHANGED THE ANSWER. A stem rule matched `panel` and
        `panelStyle` and reported an audio pan control on an auto-rigging panel.
        Each control now declares the inflections it really has, and the test
        pins that `panel`, `console`, `against` and `commuter` match nothing. -->
        <!-- Still out of reach: whether a preview, once built, is
        keyboard-operable and announced. That needs the browser harness. -->
  - [x] I2.19.o Export versioned mix/stems/captions/transcript/cue sheet/
        attribution/consent/rights/measurements in a hashed output bundle.
        Evidence: bundle manifest/round-trip tests.
- [x] I2.20 Implement operator workflow graph authoring with node catalog, typed
      ports, subgraphs, custom-node/model dependencies, validation, version
      diff, dry run, benchmark, promotion, and rollback. Evidence: graph
      injection, incompatible node, and dependency-supply-chain tests.
      <!-- evidence: evidence/v1-workbenches/workflow-graph/ (compiling reference,
                                                               strict tsc + runnable suite) and workflow-graph.json via
                                                               `node scripts/v1-workbenches/generate-workflow-graph.mjs`. All three
                                                               named attacks are exercised: injection (the guard iterates the catalog,
                                                               not the payload), an incompatible node (ports carry their full type), and
                                                               a shallow dependency lock (the closure is walked to its leaves). -->
  - [x] I2.20.a Define immutable
        graph/version/node/port/edge/parameter/subgraph/
        dependency/validation/benchmark/promotion contracts and canonical
        serialization. Evidence: workflow graph schemas.
  - [x] I2.20.b Build an authorized searchable node catalog with signed source,
        version, input/output types, parameters, capabilities, compatibility,
        resource risk, license, lifecycle, and audience metadata. Evidence:
        catalog tests.
  - [x] I2.20.c Enforce typed port modality/shape/unit/time/coordinate/color/
        cardinality/optionality compatibility at connect and validate time.
        Evidence: port compatibility fixtures.
  - [x] I2.20.d Author reusable version-pinned subgraphs with declared typed
        interfaces, parameters, dependencies, scope, and cycle/depth limits.
        Evidence: subgraph tests.
  - [x] I2.20.e Resolve custom-node/model/adapter/runtime/package artifacts by
        immutable version/hash/signature/license/scan/promotion and complete
        transitive dependency lock. Evidence: supply-chain tests.
  - [x] I2.20.f Validate required ports, graph cycles, unreachable/dead outputs,
        parameter ranges, resource bounds, target compatibility, audience,
        policy, and output contracts. Evidence: graph validation suite.
  - [x] I2.20.g Diff canonical graph versions by node/edge/port/parameter/
        dependency/interface/policy/effective behavior with stable identities.
        Evidence: graph diff fixtures.
  - [x] I2.20.h Dry-run type resolution, dependency availability, authorization,
        resource/cost estimate, and execution plan without provider side
        effects. Evidence: zero-dispatch dry-run tests.
  - [x] I2.20.i Benchmark approved fixtures with pinned inputs/environment/
        dependencies and record quality/latency/cost/resource/reproducibility.
        Evidence: benchmark reproducibility tests.
  - [x] I2.20.j Promote exact signed graph versions through governed
        environments only after required validation/evaluation/approval
        evidence. Evidence: promotion gate tests.
  - [x] I2.20.k Roll back new selection to a prior admitted immutable graph
        without deleting runs or rewriting their consumed versions. Evidence:
        rollback/history tests.
  - [x] I2.20.l Reject code/expression/template/serialized-object/unknown-field/
        alias/mass-assignment graph injection and sandbox admitted execution.
        Evidence: adversarial graph tests.

### I3 — Provider execution, jobs, and resource governance

- [x] I3.1 Wire the V1 generation route to a real injected executor in an
      approved environment. Evidence: credentials remain external and the
      default stays `not_configured`.
      <!-- evidence: evidence/v1-workbenches/executor/ (compiling reference, strict
                                                           tsc + runnable suite) and executor.json via
                                                           `node scripts/v1-workbenches/generate-executor.mjs`. ExecutionRequest has
                                                           no credential field; the leak scan takes the actual secret VALUES; and a
                                                           call-counted executor plus a read-counted secret provider both record
                                                           ZERO in every unapproved environment, including against a smuggled
                                                           binding. -->
  - [x] I3.1.a Define the executor port and typed request/result/progress/error/
        cancellation contract independently of any provider SDK. Evidence:
        executor contract tests.
  - [x] I3.1.b Register real executor implementations only through approved
        environment composition and keep the default binding explicitly
        `not_configured`. Evidence: composition tests.
  - [x] I3.1.c Load credential references from the approved secret provider at
        execution time without accepting credentials in route payloads,
        configuration snapshots, logs, or stored plans. Evidence: secret leak
        tests.
  - [x] I3.1.d Revalidate actor, project, plan, workflow/model versions, policy,
        reservation, and idempotency immediately before invoking the executor.
        Evidence: stale-plan/bypass tests.
  - [x] I3.1.e Map executor progress, output, refusal, failure, cancellation,
        usage, and provenance to canonical domain events. Evidence: adapter
        integration tests.
  - [x] I3.1.f Prove local/test/unapproved environments make zero provider calls
        and return the stable not-configured result. Evidence: environment
        isolation tests.
- [x] I3.2 Implement RunPod/ComfyUI adapter capability negotiation. Evidence:
      unsupported nodes/models are rejected before queueing.
      <!-- evidence: evidence/v1-workbenches/adapter/ (compiling reference, strict
                                                       tsc + runnable suite) and adapter.json via
                                                       `node scripts/v1-workbenches/generate-adapter.mjs`. Identity is class
                                                       type + module + version and content hash, never a display name; every one
                                                       of the 15 refusal paths asserts zero submissions against a call-counted
                                                       queue seam. -->
  - [x] I3.2.a Define the adapter capability document for endpoint/version,
        queue API, node catalog, models, adapters, formats, limits, progress,
        cancellation, and result transport. Evidence: capability schema tests.
  - [x] I3.2.b Fetch and authenticate capability state with timeout, size,
        schema, freshness, cache, and last-known-state rules. Evidence:
        negotiation failure tests.
  - [x] I3.2.c Normalize RunPod endpoint/worker/GPU and ComfyUI node/object-info
        data into canonical capabilities without trusting display names.
        Evidence: normalization fixtures.
  - [x] I3.2.d Compare exact workflow graph, custom nodes, model hashes,
        adapters, controls, input/output formats, and resource requirements
        before queue submission. Evidence: compatibility tests.
  - [x] I3.2.e Reject missing, stale, mismatched, quarantined, or unverified
        capabilities with typed reasons and no queue side effect. Evidence:
        zero-queue negative tests.
  - [x] I3.2.f Detect capability changes between plan and dispatch and require
        replan rather than silently substituting dependencies. Evidence: race
        tests.
- [x] I3.3 Implement provider routing using health, capability, policy,
      residency, latency, cost, and capacity. Evidence: known routing fixtures.
      <!-- evidence: evidence/v1-workbenches/routing/ (compiling reference, strict
                                                   tsc + runnable suite) and routing.json via
                                                   `node scripts/v1-workbenches/generate-routing.mjs`. Endpoints differ in
                                                   exactly one ranking signal so each tier is proven independently; a stale
                                                   snapshot is unknown capacity, and dispatch may never substitute another
                                                   region or model. -->
  - [x] I3.3.a Build the candidate set from approved provider endpoints that
        match exact workflow/model/input/output capabilities. Evidence:
        candidate tests.
  - [x] I3.3.b Filter endpoint lifecycle, health/circuit state, policy, tenant/
        audience entitlement, regional/data-residency, rights, and security
        admission. Evidence: eligibility matrix.
  - [x] I3.3.c Filter live queue/GPU/VRAM/storage/egress capacity and
        reservation feasibility without overcommitting stale snapshots.
        Evidence: capacity contention tests.
  - [x] I3.3.d Rank eligible endpoints deterministically by policy priority,
        predicted latency, complete cost, capacity headroom, reliability, and
        stable tie-break. Evidence: routing fixtures.
  - [x] I3.3.e Record candidates, allowed rejection reasons, selected endpoint
        identity/version, inputs, estimates, and routing policy version.
        Evidence: routing decision audit.
  - [x] I3.3.f Revalidate the chosen endpoint and reservation at dispatch;
        fail/replan according to policy without unauthorized cross-region or
        model substitution. Evidence: dispatch race tests.
- [x] I3.4 Implement per-family circuit breakers and retry/backoff from the
      canonical policy. Evidence: clock-controlled state tests.
      <!-- evidence: evidence/v1-workbenches/breaker/ (compiling reference; every
                                          transition driven by an injected monotonic clock) + breaker.json via
                                          scripts/v1-workbenches/generate-breaker.mjs; 146 assertions, 25/25 mutants
                                          killed -->
  - [x] I3.4.a Define circuit scope keys for provider, endpoint, workflow/model
        family, operation, region, and failure class to prevent unrelated
        traffic from sharing state. Evidence: scope tests.
  - [x] I3.4.b Classify which failures count, do not count, or immediately open
        each circuit from the canonical versioned policy. Evidence: classifier
        fixtures.
  - [x] I3.4.c Implement closed/open/half-open transitions with rolling windows,
        thresholds, cooldown, bounded probes, concurrency safety, and monotonic
        time. Evidence: fake-clock state tests.
  - [x] I3.4.d Implement attempt limits, exponential backoff, jitter, deadline,
        Retry-After handling, idempotency, and retryable-stage constraints.
        Evidence: retry schedule tests.
  - [x] I3.4.e Ensure cancellation, policy denial, validation failure, budget
        exhaustion, and non-idempotent completed stages are never retried.
        Evidence: forbidden retry tests.
  - [x] I3.4.f Persist/restore sufficient breaker state across process restart
        and emit state/decision metrics without high-cardinality secrets.
        Evidence: restart/telemetry tests.
- [x] I3.5 Distinguish provider refusal, policy denial, capacity exhaustion,
      timeout, malformed response, worker loss, and internal failure. Evidence:
      stable failure taxonomy reaches the UI.
      <!-- evidence: evidence/v1-workbenches/failure-taxonomy/ (compiling
                                      reference; all 30 codes projected to a client shape with no prose field) +
                                      failure-taxonomy.json via scripts/v1-workbenches/generate-failure-taxonomy.mjs;
                                      134 assertions, 25/25 mutants killed -->
  - [x] I3.5.a Define stable machine codes, categories, retryability, ownership,
        user-safe message keys, operator detail, and terminal/nonterminal
        semantics for every failure class. Evidence: error schema tests.
  - [x] I3.5.b Map provider refusal/status/body signals without exposing
        provider-private prompts, safety internals, or credentials. Evidence:
        provider refusal fixtures.
  - [x] I3.5.c Distinguish canonical policy denial from provider refusal and
        retain policy version/rule/evidence appropriate to audience. Evidence:
        policy mapping tests.
  - [x] I3.5.d Distinguish queue/capacity/quota/reservation exhaustion with
        responsible scope, recoverability, and safe retry timing. Evidence:
        capacity error tests.
  - [x] I3.5.e Distinguish connect/read/overall/deadline timeouts and worker
        heartbeat/loss/eviction from explicit cancellation. Evidence: timeout/
        worker-loss tests.
  - [x] I3.5.f Validate provider response status, schema, content type, size,
        artifact manifests, hashes, and required outputs before acceptance.
        Evidence: malformed response tests.
  - [x] I3.5.g Preserve unexpected internal failure correlation for operators
        while UI/API receive stable redacted typed errors. Evidence: projection/
        leak tests.
- [x] I3.6 Implement durable generation jobs with stage checkpoints. Evidence:
      worker restart resumes safely or fails explicitly.
      <!-- evidence: evidence/v1-workbenches/durable-jobs/ (compiling reference;
                                  fenced leases, checkpoint provenance, replayable append-only log, real
                                  SHA-256 pinned to the FIPS vectors) + durable-jobs.json via
                                  scripts/v1-workbenches/generate-durable-jobs.mjs; 111 assertions, 28/28
                                  mutants killed -->
  - [x] I3.6.a Define immutable job identity, versioned plan snapshot, stage
        DAG, attempt, state, lease, checkpoint, output, error, usage, and event
        contracts. Evidence: job schemas.
  - [x] I3.6.b Create jobs idempotently with plan/input/hash/reservation binding
        in one durable transaction. Evidence: duplicate submission tests.
  - [x] I3.6.c Claim work with fenced leases, heartbeat, expiry, and
        compare-and- set transitions so stale workers cannot commit. Evidence:
        lease race tests.
  - [x] I3.6.d Checkpoint only validated stage outputs and exact consumed input/
        dependency/policy versions with integrity hashes. Evidence: checkpoint
        integrity tests.
  - [x] I3.6.e On restart, resume only explicitly resumable idempotent stages;
        otherwise create a new attempt or terminal explicit failure. Evidence:
        crash/restart tests.
  - [x] I3.6.f Handle duplicate/out-of-order progress/results and provider
        callbacks without regressing state or duplicating assets/cost. Evidence:
        event ordering tests.
  - [x] I3.6.g Preserve complete append-only job/stage/attempt history and make
        current state a reproducible projection. Evidence: replay tests.
- [x] I3.7 Implement cancellation propagation to queued and running providers.
      Evidence: race tests prevent late outputs from appearing successful.
      <!-- evidence: evidence/v1-workbenches/cancellation/ (compiling reference;
                              the claim/cancel race is run, and a late artifact is quarantined rather
                              than counted) + cancellation.json via
                              scripts/v1-workbenches/generate-cancellation.mjs; 100 assertions, 24/24
                              mutants killed -->
  - [x] I3.7.a Define authorized cancellation request, reason, scope, actor,
        timestamp, idempotency, and queued/running/terminal semantics. Evidence:
        cancellation contract tests.
  - [x] I3.7.b Atomically mark queued work cancelled and release queue/resource/
        budget reservations before any claim can dispatch. Evidence: queue race
        tests.
  - [x] I3.7.c Signal running workers/providers using supported cancel handles
        with timeout, retry, and explicit unsupported/unknown outcome. Evidence:
        provider cancellation tests.
  - [x] I3.7.d Fence stage/output commits after cancellation while retaining
        late provider artifacts only in quarantined operator-visible evidence.
        Evidence: late-result race tests.
  - [x] I3.7.e Reconcile actual usage/cost and release unused reservations even
        when provider cancellation is delayed or fails. Evidence: cancellation
        accounting tests.
  - [x] I3.7.f Project cancelled/cancelling/cancel-failed state consistently to
        UI/API/events and retain immutable history. Evidence: end-to-end tests.
- [x] I3.8 Implement deduplication and cache eligibility. Evidence: cache keys
      include every output-affecting input and policy version.
      <!-- evidence: evidence/v1-workbenches/cache-eligibility/ (compiling
                          reference; each of the 11 key components is changed one at a time and must
                          move the key, and the 4 non-affecting fields must not) +
                          cache-eligibility.json via
                          scripts/v1-workbenches/generate-cache-eligibility.mjs; 79 assertions, 25/25
                          mutants killed -->
  - [x] I3.8.a Define separately when request idempotency, in-flight coalescing,
        deterministic result reuse, and derived-artifact reuse are permitted.
        Evidence: eligibility policy tests.
  - [x] I3.8.b Canonicalize and hash exact input bytes/revisions, prompt spec,
        workflow graph, models/adapters, effective controls/seed, environment,
        target, code/policy/evaluator versions, and tenant scope. Evidence:
        cache-key golden tests.
  - [x] I3.8.c Exclude nondeterministic, private/identity-sensitive, consent-
        scoped, expired-rights, quarantined, mutable-dependency, or policy-
        forbidden runs from reuse. Evidence: exclusion tests.
  - [x] I3.8.d Coalesce concurrent eligible requests with fenced ownership,
        independent authorization, cancellation/reference counts, and fair cost
        attribution. Evidence: concurrency tests.
  - [x] I3.8.e Store cache metadata, artifact manifests, hashes, provenance,
        expiry, policy scope, and invalidation reasons without cross-tenant
        discovery. Evidence: isolation tests.
  - [x] I3.8.f Revalidate authorization, rights/consent, policy, target, and
        artifact integrity on every lookup before returning a hit. Evidence:
        stale-hit tests.
- [x] I3.9 Verify cache hits against stored artifact hashes. Evidence: corrupt
      cache entries are quarantined and recomputed.
      <!-- evidence: evidence/v1-workbenches/cache-integrity/ (compiling reference;
                          a corrupted stored object is quarantined by compare-and-set and replaced by a
                          new run that supersedes the old one without rewriting it) +
                          cache-integrity.json via scripts/v1-workbenches/generate-cache-integrity.mjs;
                          138 assertions, 47/47 mutants killed -->
  - [x] I3.9.a Resolve the immutable cached artifact manifest and authorized
        storage objects without trusting caller-supplied locations. Evidence:
        resolver tests.
  - [x] I3.9.b Stream-hash every required artifact and manifest using approved
        algorithms, enforcing size/count/time/resource limits. Evidence: hash
        verification tests.
  - [x] I3.9.c Verify manifest/object hash, length, media signature, dependency,
        provenance, and encryption/key-version consistency. Evidence: corrupted
        cache fixtures.
  - [x] I3.9.d Atomically quarantine the cache record and affected objects with
        reason/evidence, preventing races from serving the same entry. Evidence:
        quarantine contention tests.
  - [x] I3.9.e Recompute through a fresh run identity/reservation and never
        rewrite the historical corrupt run as successful. Evidence: recompute
        lineage tests.
  - [x] I3.9.f Emit bounded integrity/security telemetry and operator alerts
        without exposing asset content or cross-tenant keys. Evidence: telemetry
        leak tests.
- [x] I3.10 Implement queue priority with anti-starvation and tenant fairness.
      Evidence: load test demonstrates bounded wait.
      <!-- evidence: evidence/v1-workbenches/queue-priority/ (compiling reference) +
                          queue-priority.json via scripts/v1-workbenches/generate-queue-priority.mjs,
                          which RUNS the reference's load simulation and embeds the report it produced;
                          127 assertions, 32/32 mutants killed. Scope, honestly: the load run is a
                          deterministic simulation of the SCHEDULER (throughput, isolation, wait bounds,
                          starvation) — load testing live workers is a separate exercise and is not
                          claimed here. -->
  - [x] I3.10.a Define canonical base-priority classes, allowed actors/reasons,
        per-tenant/project/user/workflow/provider weights, and non-overridable
        safety/incident lanes. Evidence: priority policy tests.
  - [x] I3.10.b Enqueue with immutable submitted time, cost/resource estimate,
        deadline class, tenant scope, and policy version. Evidence: enqueue
        schema tests.
  - [x] I3.10.c Implement weighted fair scheduling with aging/anti-starvation,
        bounded priority boosts, per-scope concurrency, and deterministic
        tie-breaks. Evidence: scheduler model tests.
  - [x] I3.10.d Prevent job splitting, repeated cancel/resubmit, identity/tenant
        switching, or direct priority fields from gaming order. Evidence:
        adversarial fairness tests.
  - [x] I3.10.e Reconcile estimates against actual resource duration and feed
        bounded future scheduling signals without retroactive priority changes.
        Evidence: reconciliation tests.
  - [x] I3.10.f Load-test mixed tenants/priorities/job sizes/failures/cancels to
        demonstrate throughput, isolation, configured wait bounds, and no
        starvation. Evidence: queue load report.
- [x] I3.11 Implement GPU/CPU/VRAM/storage/egress reservations. Evidence:
      overcommit and release-on-failure tests.
      <!-- evidence: evidence/v1-workbenches/reservations/ (compiling reference; an
                          unauthorised expansion pauses the job instead of overcommitting, and all six
                          stop paths release idempotently) + reservations.json via
                          scripts/v1-workbenches/generate-reservations.mjs; 111 assertions, 33/33
                          mutants killed -->
  - [x] I3.11.a Define resource kinds, units, scopes, capacity sources, safety
        headroom, reservation states, expiry, ownership, and accounting
        contracts. Evidence: reservation schemas.
  - [x] I3.11.b Calculate per-stage and peak concurrent GPU count/type, CPU,
        RAM/VRAM, temporary/persistent storage, network, and egress needs from
        the exact plan. Evidence: requirement fixtures.
  - [x] I3.11.c Atomically reserve all required scoped resources or none, with
        fencing token, lease, queue linkage, and exact insufficiency reasons.
        Evidence: multi-resource contention tests.
  - [x] I3.11.d Revalidate and consume reservations at dispatch without allowing
        stale/expired/wrong-job/wrong-endpoint tokens. Evidence: dispatch race
        tests.
  - [x] I3.11.e Adjust within authorized bounds for measured usage, rejecting or
        pausing unapproved expansion instead of silent overcommit. Evidence:
        resize tests.
  - [x] I3.11.f Release unused resources idempotently on completion, failure,
        cancellation, timeout, lease loss, and abandoned job recovery. Evidence:
        release path tests.
  - [x] I3.11.g Reconcile reservation ledger to provider/storage/network
        measurements and alert on leaks/negative/over-capacity invariants.
        Evidence: reconciliation tests.
- [x] I3.12 Implement pre-dispatch estimate and post-run cost reconciliation.
      Evidence: variance and unbilled failures are visible.
      <!-- evidence: evidence/v1-workbenches/cost/ (compiling reference; an unknown or a
                          quarantined measurement marks the report INCOMPLETE rather than settling as
                          zero, and variance carries typed causes) + cost.json via
                          scripts/v1-workbenches/generate-cost.mjs; 109 assertions, 29/29 mutants
                          killed -->
  - [x] I3.12.a Define versioned price inputs and cost dimensions for provider
        compute/time, models/nodes, storage, egress, evaluation,
        post-processing, taxes/fees/credits, and internal allocation. Evidence:
        pricing schema.
  - [x] I3.12.b Compute low/expected/high estimates from exact plan, candidate
        count, resource reservation, retry policy, target, currency, and price
        version. Evidence: estimate fixtures.
  - [x] I3.12.c Bind the accepted estimate/threshold confirmation and budget
        reservation to the immutable plan before dispatch. Evidence: estimate
        binding tests.
  - [x] I3.12.d Ingest authenticated provider usage/billing plus canonical
        worker/storage/egress/evaluator measurements with source timestamps and
        confidence. Evidence: usage ingestion tests.
  - [x] I3.12.e Allocate actual cost by run/stage/variant/asset/project/tenant
        without double-counting retries, cache hits, shared jobs, or partial
        outputs. Evidence: allocation tests.
  - [x] I3.12.f Represent unbilled, estimated-only, pending, credited, refunded,
        disputed, and unknown costs explicitly rather than as zero. Evidence:
        accounting state tests.
  - [x] I3.12.g Calculate absolute/relative variance with typed causes and
        surface configured breach alerts and approval follow-up. Evidence:
        variance tests.
- [x] I3.13 Implement tenant, project, user, workflow, and provider quotas.
      Evidence: each limit is enforced server-side.
      <!-- evidence: evidence/v1-workbenches/quotas/ (compiling reference; all seven
                          declared entry paths are run against an exhausted quota and refused by the one
                          enforcement point) + quotas.json via scripts/v1-workbenches/generate-quotas.mjs;
                          98 assertions, 31/31 mutants killed -->
  - [x] I3.13.a Define versioned quota dimensions, units, rolling/fixed windows,
        scope hierarchy, soft/hard thresholds, burst, reservation, and reset
        semantics. Evidence: quota schemas.
  - [x] I3.13.b Resolve effective quotas from tenant/project/user/workflow/
        provider policy without allowing a child scope to loosen a parent hard
        limit. Evidence: hierarchy tests.
  - [x] I3.13.c Atomically check and reserve request/count/cost/compute/storage/
        egress/concurrency quota at plan confirmation and dispatch. Evidence:
        quota contention tests.
  - [x] I3.13.d Count completed, failed, cancelled, retried, cached, shared, and
        partial work according to explicit per-dimension rules. Evidence: quota
        accounting tests.
  - [x] I3.13.e Release/refund only permitted unused reservations and retain
        consumed usage through retries or scope changes. Evidence: refund tests.
  - [x] I3.13.f Return audience-safe current/limit/reset/shortfall/retry state
        and alert before soft/hard thresholds without exposing other tenants.
        Evidence: UI/API isolation tests.
  - [x] I3.13.g Test direct route, batch, replay, agent, import, provider
        callback, and concurrency bypass paths server-side. Evidence: quota
        bypass suite.
- [x] I3.14 Implement worker and endpoint draining for maintenance. Evidence: no
      new work lands and in-flight policy is honored.
      <!-- evidence: evidence/v1-workbenches/draining/ (compiling reference; all three
                          claim kinds are run against a draining target and refused by the one admission
                          point, and the order's in-flight policy is resolved against each running stage's
                          real capabilities) + draining.json via
                          scripts/v1-workbenches/generate-draining.mjs; 194 assertions, 43/43 mutants -->
  - [x] I3.14.a Define active/draining/drained/disabled state, reason, actor,
        effective/deadline times, in-flight policy, scope, and audit contract.
        Evidence: drain schema tests.
  - [x] I3.14.b Authorize drain/restore separately for worker, pool, endpoint,
        provider, model/workflow family, region, and environment. Evidence:
        operation authorization tests.
  - [x] I3.14.c Remove draining targets from new routing/reservation/queue
        claims atomically while allowing configured already-bound work.
        Evidence: new- work exclusion tests.
  - [x] I3.14.d Apply finish, checkpoint-and-migrate, cancel, or deadline-kill
        policy to each in-flight stage with capability/idempotency validation.
        Evidence: in-flight matrix tests.
  - [x] I3.14.e Reassign queued work and only migratable running work with new
        attempt/endpoint identity and preserved lineage/reservations. Evidence:
        migration tests.
  - [x] I3.14.f Determine drained state from zero claims/in-flight callbacks/
        leases/reservations and expose progress/blockers. Evidence: drain
        completion tests.
  - [x] I3.14.g Restore admission explicitly after health/capability/security
        checks; never auto-enable expired maintenance targets. Evidence: restore
        tests.
- [x] I3.15 Implement run replay with explicit new-run identity. Evidence:
      historical executions remain immutable.
      <!-- evidence: evidence/v1-workbenches/run-replay/ (compiling reference; every run is
                      serialized before the replay and re-compared after resolution, preview,
                      confirmation, four recorded outcomes, a cache reuse of the source's own artifact
                      and a second replay) + run-replay.json via
                      scripts/v1-workbenches/generate-run-replay.mjs; 139 assertions, 45/45 mutants -->
  - [x] I3.15.a Resolve an authorized immutable source run and enumerate exact
        inputs, plan, workflow/model/dependencies, controls, environment,
        policy, rights, and unavailable/redacted fields. Evidence: replay
        eligibility tests.
  - [x] I3.15.b Distinguish exact replay, replay-under-current-policy, and fork-
        with-changes as separate typed operations. Evidence: replay mode tests.
  - [x] I3.15.c Validate current artifact availability/integrity, dependency
        admission, rights/consent, policy, target, quota, budget, and provider
        capability before confirmation. Evidence: stale replay tests.
  - [x] I3.15.d Preview substitutions, version drift, nonreproducible inputs,
        changed policy, expected cost, and output-identity consequences.
        Evidence: replay preview tests.
  - [x] I3.15.e Create a new request/plan/run/attempt identity linked to the
        immutable source and record replay mode plus explicit changes. Evidence:
        replay lineage tests.
  - [x] I3.15.f Never append progress/results/cost/decisions to or alter the
        source run, even when cache reuse is permitted. Evidence: historical
        immutability tests.
- [x] I3.16 Build brief-to-plane agent plans over `operation-orchestrator`,
      `job-envelope`, and `token-budget`. Evidence: tool grants, dependencies,
      stop conditions, approval points, and budget validate before dispatch.
      <!-- evidence: evidence/v1-workbenches/agent-plan/ (compiling reference; each of the
                  nine plan checks is broken on its own and the resulting plan is shown to dispatch
                  ZERO steps) + agent-plan.json via scripts/v1-workbenches/generate-agent-plan.mjs;
                  129 assertions, 48/48 mutants -->
  - [x] I3.16.a Define versioned Isis agent-plan and step contracts that compose
        canonical operation, job-envelope, budget, asset, brief, workflow, and
        provenance identities. Evidence: plan schema tests.
  - [x] I3.16.b Compile the authorized brief/spec into explicit ordered/DAG
        steps with typed inputs/outputs, dependencies, pre/postconditions, and
        completion criteria. Evidence: planning fixtures.
  - [x] I3.16.c Resolve each tool grant to an allowlisted versioned capability,
        operation scope, resource bounds, data classification, and
        actor/audience authority. Evidence: grant tests.
  - [x] I3.16.d Define hard stop conditions for validation/policy/rights/budget/
        timeout/cancellation/dependency/evidence failures and bounded retry/
        fallback behavior. Evidence: stop-condition tests.
  - [x] I3.16.e Insert explicit human approval points only where canonical
        policy requires them, binding approver role and unchanged evidence/plan.
        Evidence: approval binding tests.
  - [x] I3.16.f Allocate per-step and total token/tool/compute/cost/time budgets
        with non-transferable ceilings and reserve them before dispatch.
        Evidence: budget allocation tests.
  - [x] I3.16.g Validate graph acyclicity/reachability, contract compatibility,
        tool availability, grants, budgets, approvals, and output destinations
        before any agent execution. Evidence: zero-dispatch invalid-plan tests.
- [ ] I3.17 Build Isis-scoped agent run, queue, plan, tool-call, budget, output,
      and provenance views. Evidence: live traced journey and cross-tenant
      refusal.
      <!-- partial: evidence/v1-workbenches/agent-views/ (compiling reference; 134
              assertions, 36/36 mutants) covers a/b/c/e/f/g/h — the projection contract, the
              state model, pagination and event semantics, budget/output/provenance views and
              the one authorizer every entry point goes through, including the cross-tenant
              refusal half of this parent. NOT claimed: I3.17.d (live journey tests) and
              I3.17.i (accessibility/performance automation) need a running surface, so they
              and this parent stay unchecked. See agent-views.json runtimeGated. -->
  - [x] I3.17.a Define audience-safe query projections for agent request, plan,
        step, queue, run, attempt, tool call, budget, event, output, and
        lineage. Evidence: view contracts.
  - [x] I3.17.b Build the run list/detail state model with filters, stable URLs,
        loading/empty/error/stale/cancelled/partial states, and pagination.
        Evidence: state tests.
  - [x] I3.17.c Render plan DAG/step status/dependencies/approval/stop
        conditions and immutable version details without exposing hidden
        prompts/tools. Evidence: plan view tests.
  - [ ] I3.17.d Render queue position/priority class/estimate/reservation and
        live bounded event progress with reconnect/cursor/deduplication
        handling. Evidence: live journey tests.
  - [x] I3.17.e Render each authorized tool call’s tool/version/purpose/status/
        timing/budget/input-output schema summary/error and correlated step.
        Evidence: tool-call view tests.
  - [x] I3.17.f Render allocated/consumed/remaining token/tool/compute/cost/time
        budget with unknown/pending/reconciled states. Evidence: budget view
        tests.
  - [x] I3.17.g Render proposed/generated outputs, immutable asset/run lineage,
        provenance, quality/safety gates, decisions, and partial/missing states.
        Evidence: output/provenance view tests.
  - [x] I3.17.h Enforce tenant/project/actor/audience/object/property/function
        authorization on queries, streams, URLs, exports, and actions. Evidence:
        cross-tenant/adversarial authorization tests.
  - [ ] I3.17.i Verify keyboard/screen-reader/reduced-motion/responsive behavior
        and performance with long plans/events/tool-call histories. Evidence:
        accessibility/performance automation.
- [x] I3.18 Intake agent results as attributed proposals or generated assets,
      never as automatic approvals. Evidence: agents cannot waive gates, advance
      human signoff, promote environments, or publish.
      <!-- evidence: evidence/v1-workbenches/result-intake/ (compiling reference; every
                  privileged operation — waive-gate, advance-signoff, promote-environment,
                  publish, apply-proposal — refuses an agent AND a service principal, including
                  an agent holding every permission a human holds) + result-intake.json via
                  scripts/v1-workbenches/generate-result-intake.mjs; 216 assertions,
                  91/91 mutants -->
  - [x] I3.18.a Define result envelope with agent/run/step/tool/model/version,
        source inputs, output kind, claim/evidence, confidence, cost, and
        integrity binding. Evidence: result schema tests.
  - [x] I3.18.b Validate schema, signature/hash, tenant/project scope, expected
        step/output contract, size/type, moderation, and provenance on intake.
        Evidence: hostile result tests.
  - [x] I3.18.c Store analysis/recommendations/metadata as attributed proposals
        with proposed state and no mutation of canonical records. Evidence:
        proposal persistence tests.
  - [x] I3.18.d Store media outputs as generated asset revisions linked to the
        exact plan/run/step/tool inputs and pending all applicable gates.
        Evidence: generated asset lineage tests.
  - [x] I3.18.e Map no agent result field to approval, waiver, gate pass,
        promotion, publication, or human identity/authority. Evidence: mass-
        assignment/bypass tests.
  - [x] I3.18.f Route proposals/assets into authorized human review with accept/
        reject/modify/defer decisions recorded separately. Evidence: review
        workflow tests.
- [x] I3.19 Implement agent pause, redirect, cancel, budget kill, and incident
      controls. Evidence: race and partial-result tests preserve immutable run
      history.
      <!-- evidence: evidence/v1-workbenches/agent-controls/ (compiling reference; the
                  race half is the compare-and-set fence a pre-pause reader is caught by, the
                  partial half is retention + late-result quarantine, and the immutable half is
                  the projection asserted at EVERY log prefix plus a digest of the input run
                  taken before the suite and re-checked after it) + agent-controls.json via
                  scripts/v1-workbenches/generate-agent-controls.mjs; 168 assertions,
                  96/96 mutants -->
  - [x] I3.19.a Define separate authorized command contracts, eligible states,
        scope, reason, actor, idempotency, expected version, and outcome for
        pause/redirect/cancel/budget-kill/incident actions. Evidence: command
        schemas.
  - [x] I3.19.b Pause at a safe step/tool boundary, checkpoint resumable state,
        stop new grants/calls, retain leases as policy permits, and expose
        blockers. Evidence: pause race tests.
  - [x] I3.19.c Resume only after revalidating plan/dependencies/grants/policy/
        rights/budget/approvals and create a new attempt where required.
        Evidence: resume validation tests.
  - [x] I3.19.d Redirect through a new versioned plan revision with explicit
        changed objectives/steps/tools/budget and preserved completed history.
        Evidence: redirect lineage tests.
  - [x] I3.19.e Cancel queued/running steps and downstream dependencies with
        provider/tool propagation, late-result fencing, and partial-result
        retention. Evidence: cancellation tests.
  - [x] I3.19.f Kill on budget ceiling atomically before the next spend,
        handling in-flight nonrefundable usage and reconciling actual cost.
        Evidence: budget overshoot tests.
  - [x] I3.19.g Quarantine/freeze affected runs, outputs, grants, dependencies,
        and evidence on incident command while preserving investigation access.
        Evidence: incident containment tests.
  - [x] I3.19.h Keep append-only command/event/attempt/output history and never
        recast partial/cancelled/incident results as successful. Evidence:
        history projection tests.
- [x] I3.20 Implement multi-agent/evaluator consensus only as an inspectable
      decision aid with member identity/version, independence assumptions,
      votes/scores, disagreement, confidence, timeout/abstention, quorum,
      escalation, and human decision. Evidence: correlated agents cannot
      manufacture gate or approval authority.
      <!-- evidence: evidence/v1-workbenches/consensus/ (compiling reference; the panel
                  fixture contains REAL copies of one member — same model, provider, corpus and
                  prompt — rather than a correlation flag, and three of them carry one vote,
                  produce no recommendation, and reach no gate) + consensus.json via
                  scripts/v1-workbenches/generate-consensus.mjs; 141 assertions,
                  69/69 mutants -->
  - [x] I3.20.a Define consensus request/member/assessment/vote/score/
        abstention/disagreement/quorum/result/escalation contracts with exact
        versions and evidence refs. Evidence: consensus schemas.
  - [x] I3.20.b Register each eligible agent/evaluator identity, owner, model/
        code/prompt/config version, training/data/provider dependencies, scope,
        calibration, and lifecycle. Evidence: member registry tests.
  - [x] I3.20.c Record declared and detected shared model/provider/data/prompt/
        tool/dependency lineage as independence assumptions, never as proven
        independence. Evidence: correlation fixtures.
  - [x] I3.20.d Collect assessments against the same immutable subject revision,
        rubric/policy/version, blinded context where required, and per-member
        timeout. Evidence: collection tests.
  - [x] I3.20.e Preserve raw votes/scores/reasons/evidence/confidence plus
        abstention, timeout, error, conflict, and unavailable states. Evidence:
        assessment projection tests.
  - [x] I3.20.f Calculate configured aggregation/quorum only from eligible
        members, applying correlation groups, weights, minimum independence, and
        deterministic tie behavior. Evidence: aggregation fixtures.
  - [x] I3.20.g Surface disagreement dimensions, minority assessments,
        uncertainty, missing members, assumption violations, and escalation
        reason. Evidence: consensus UI/API tests.
  - [x] I3.20.h Store consensus as an attributed advisory result and require a
        separate authorized human/canonical gate decision. Evidence: authority
        boundary tests.
- [x] I3.21 Implement batch LLM/media planning and processing with per-item
      source/prompt/model/budget, bounded fan-out, partial results,
      cancellation, retries, aggregation, review, and cost attribution.
      Evidence: no cross-item context or tenant leak and failed items cannot be
      hidden by aggregate success.
      <!-- evidence: evidence/v1-workbenches/batch/ (compiling reference; the leak half is
                  the per-item allow-list context + tenant-and-item-namespaced credential, temp
                  dir, cache key, log sink, callback and destination, checked by scanning one
                  item's context for another's content; the hidden-failure half is a denominator
                  over EVERY item plus a review that cannot read the batch's state) + batch.json
                  via scripts/v1-workbenches/generate-batch.mjs; 139 assertions, 79/79 mutants -->
  - [x] I3.21.a Define batch, immutable item, dependency, per-item plan/run/
        output, aggregate, review, and cost contracts with maximum item/payload
        limits. Evidence: batch schemas.
  - [x] I3.21.b Validate every item’s tenant/project/source revision/prompt
        spec/ workflow/model/rights/policy/target independently before
        admission. Evidence: mixed-validity intake tests.
  - [x] I3.21.c Allocate per-item and batch
        token/tool/compute/cost/time/resource budgets without allowing one item
        to consume another’s hard ceiling. Evidence: budget isolation tests.
  - [x] I3.21.d Schedule bounded fan-out with fair queueing, per-provider/tenant
        concurrency, backpressure, and no unbounded prompt/media
        materialization. Evidence: batch load tests.
  - [x] I3.21.e Isolate item context, tools, credentials, temporary files,
        caches, logs, callbacks, and result destinations. Evidence: cross-item/
        cross-tenant leak tests.
  - [x] I3.21.f Retry only eligible failed item stages with idempotency and
        attempt limits; never rerun or rewrite successful siblings silently.
        Evidence: partial retry tests.
  - [x] I3.21.g Cancel one item or the whole batch with explicit downstream/
        in-flight semantics, late-result fencing, and reservation release.
        Evidence: cancellation race tests.
  - [x] I3.21.h Aggregate counts/measurements only with success/partial/failure/
        cancelled/skipped/unknown denominators and preserve each item’s state.
        Evidence: aggregate honesty tests.
  - [x] I3.21.i Route each output through applicable review/gates and prohibit
        aggregate success from approving failed/unreviewed items. Evidence:
        review boundary tests.
  - [x] I3.21.j Attribute estimates/actual cost/variance/retries/cache/shared
        work to each item and batch total without double counting. Evidence:
        batch cost reconciliation tests.

### I4 — Asset-kind quality and technical validation

- [x] I4.1 Define a versioned gate registry mapping every asset kind and target
      to mandatory, advisory, and unavailable gates. Evidence: coverage test has
      no unmapped publishable kind.
      <!-- evidence: evidence/v1-workbenches/gate-registry/ (compiling reference; coverage
                  walks every publishable kind × target × profile and release planning refuses
                  on a hole, so an unmapped publishable kind cannot ship) + gate-registry.json
                  via scripts/v1-workbenches/generate-gate-registry.mjs; 82 assertions,
                  42/42 mutants -->
  - [x] I4.1.a Define gate identity/version, evaluator requirements, supported
        asset/profile/target, applicability, severity, blocking class, inputs,
        outputs, units, thresholds, and lifecycle. Evidence: gate schema tests.
  - [x] I4.1.b Register every canonical asset kind and delivery target from the
        source-of-truth registries without parallel aliases or free-text kinds.
        Evidence: registry join test.
  - [x] I4.1.c Classify each gate per asset-kind/target/profile combination as
        mandatory, advisory, unavailable, or explicitly not applicable with
        reason. Evidence: matrix fixtures.
  - [x] I4.1.d Validate required evaluator/model/code/calibration/dependency
        versions are admitted in the execution environment. Evidence: dependency
        admission tests.
  - [x] I4.1.e Resolve the exact gate set deterministically and persist the
        registry/policy/profile versions consumed by each evaluation. Evidence:
        resolution tests.
  - [x] I4.1.f Fail release planning when a publishable
        asset-kind/target/profile row is missing or a mandatory gate is
        unavailable. Evidence: coverage and fail-closed tests.
- [x] I4.2 Implement image dimension, color-space, alpha, compression, and
      artifact checks. Evidence: calibrated image fixtures.
      <!-- evidence: evidence/v1-workbenches/image-checks/ (compiling reference over REAL
                  PNG chunks and JPEG segments and REAL rasters; the calibrated fixture
                  table walks a clean hero, a mislabelled file, an undersized thumbnail
                  and a decompression bomb end to end) + image-checks.json via
                  scripts/v1-workbenches/generate-image-checks.mjs; 257 assertions,
                  99/99 mutants -->
      <!-- partial: the fixtures are synthesized rasters with derivable values, not
                  photographic plates; a perceptual calibration against human-labelled
                  images is a data-collection exercise, not code -->
  - [x] I4.2.a Decode supported image formats with byte/pixel/dimension/time/
        memory limits and verify declared format against content. Evidence:
        hostile decoder tests.
        <!-- evidence: image-decode.ts + image-bytes.ts — signature sniffing, CRC-checked
                    PNG chunk walk and length-walked JPEG segments; all 10 refusals
                    asserted on hostile fixtures (truncation, bad CRC, mislabelled
                    format, a 300-byte 50000x50000 bomb, a segment flood) -->
        <!-- partial: the "time" budget is a deterministic STEP budget over the header
                    walk; a wall-clock timeout belongs to the runtime that hosts it -->
  - [x] I4.2.b Measure width, height, aspect, orientation, DPI/pixel density,
        and target-specific minimum/maximum/alignment constraints. Evidence:
        dimension boundary fixtures.
        <!-- evidence: image-geometry.ts — measured AS DISPLAYED (an Exif quarter-turn
                    transposes, so a sideways banner fails on the width a viewer sees);
                    boundaries asserted AT the bound in both directions; density read
                    from pHYs/JFIF/Exif and honestly absent when nothing declared it -->
  - [x] I4.2.c Detect embedded/declared/assumed color space, ICC profile,
        transfer/primaries, bit depth, gamut, and target conversion requirement.
        Evidence: color fixtures.
        <!-- evidence: image-color.ts — ICC colorants Bradford-adapted out of the D50
                    connection space then matched against the published primaries (the
                    published sRGB, Display P3 and Adobe RGB matrix tags are the
                    fixtures); gamut is the chromaticity-triangle area vs BT.709; an
                    untagged file is assumed-default and a profile this layer cannot
                    read is undecidable, never silently sRGB -->
  - [x] I4.2.d Measure alpha presence/range/premultiplication/fringing/coverage
        and enforce allowed/required/forbidden alpha by target. Evidence: alpha
        fixtures.
        <!-- evidence: image-alpha.ts — premultiplication INFERRED from the pixels (a
                    channel above its own alpha settles it; where nothing can settle it
                    the answer is indeterminate), fringing measured as the luma delta
                    between partial pixels and their opaque neighbours; present-but-
                    unused is a flatten, not a rejection; all three policies asserted -->
  - [x] I4.2.e Identify compression format/quality/subsampling/metadata and
        measure banding, blocking, ringing, blur, and target size budget.
        Evidence: compression fixtures.
        <!-- evidence: image-compression.ts — quality recovered by inverting the IJG
                    scaling against the Annex K luminance table in zigzag order (tables
                    built at 20/35/50/75/85/90 estimate back exactly; a clamped table is
                    not-estimable and a lossless image has no quality at all);
                    subsampling from the SOF sampling factors; banding/blocking/ringing/
                    blur measured on rasters whose values are derivable by hand -->
  - [x] I4.2.f Detect configured generation/edit artifacts with raw scores,
        regions, uncertainty, evaluator version, and no unsupported certainty.
        Evidence: labeled artifact fixtures.
        <!-- evidence: image-artifacts.ts — every CONFIGURED detector reports exactly
                    once; an evaluator we cannot admit reports unavailable with its
                    version and no score (never a zero), a score outside its calibrated
                    range is out-of-range rather than extrapolated, and a finding with
                    no uncertainty carries its number and NO band -->
        <!-- partial: the detector itself is the evaluator seam — this is the plumbing
                    that attributes, calibrates and refuses, over supplied raw findings -->
  - [x] I4.2.g Emit raw measurements separately from target/policy verdicts and
        remediation. Evidence: image gate contract tests.
        <!-- evidence: image-report.ts — RawMeasurements carries no outcome, no
                    violation and no remediation (asserted on the serialized record and
                    gated on the schema's own interface bodies); every violation cites
                    the measurement it read, the bound it compared against and both
                    units, and every check is shown to reach the verdict -->
- [x] I4.3 Implement portrait identity-consistency scoring with consent-aware
      applicability. Evidence: positive, negative, and not-applicable fixtures.
      <!-- evidence: evidence/v1-workbenches/identity-consistency/ (compiling reference;
                  the suite walks a positive match, a negative mismatch that is BANDED
                  rather than refused, a not-applicable empty frame, a face nobody could
                  compare, and a blocked consent) + identity-consistency.json via
                  scripts/v1-workbenches/generate-identity-consistency.mjs; 144
                  assertions, 66/66 mutants -->
      <!-- partial: the face detector and the embedding model are the evaluator seam —
                  similarity arrives as a probe over opaque handles, so this layer never
                  sees a vector; a measured ROC over a labelled corpus is a data
                  exercise, not code -->
  - [x] I4.3.a Resolve authorized identity subject, consent scope/term/status,
        reference set/revisions, intended use/audience, and comparison asset.
        Evidence: applicability input tests.
        <!-- evidence: identity-consent.ts — CONSENT IS A FACT ABOUT A PERSON: resolved by
                    subject, newest record wins (an older grant cannot resurrect a consent
                    a newer one withdrew), scope covers use/audience/territory, and the
                    resolved context carries the consent version, the reference revision
                    and which references were rejected -->
  - [x] I4.3.b Return explicit not-applicable/blocked when no identity claim,
        usable reference, valid consent, permitted use, or supported face region
        exists. Evidence: N/A/refusal fixtures.
        <!-- evidence: NOT APPLICABLE IS AN ANSWER, AND SO IS BLOCKED — no claim and no
                    comparable face are not-applicable (the question does not arise);
                    all 9 blocked reasons are separate answers, incl. absent/pending/
                    withdrawn/expired consent as four distinct events, with the term
                    tested AT the instant it ends -->
  - [x] I4.3.c Detect and associate faces across reference/output with
        confidence, multiple-person, occlusion, profile, age, and crop handling.
        Evidence: association corpus.
        <!-- evidence: identity-association.ts — A MATCH IS ONE-TO-ONE OR IT IS NOTHING:
                    mutual-best with a margin taken from the TIGHTER of the two sides, a
                    reference is consumed when claimed so a second face is reported as
                    another person, an age-band disagreement refuses to compare at all,
                    and all 8 ways not to associate are asserted one at a time -->
        <!-- partial: detection itself is the evaluator seam; this associates the
                    detections a detector returns -->
  - [x] I4.3.d Compute calibrated identity similarity per associated face and
        aggregate only according to versioned policy. Evidence: positive/
        negative calibration report.
        <!-- evidence: identity-scoring.ts — the slice must match the pose AND age band
                    exactly, thresholds are tested at the bound, an uncalibrated slice or
                    an out-of-range score keeps its raw value and gets NO band; AN
                    AGGREGATE THE POLICY DID NOT ASK FOR IS AN OPINION: single-face
                    refuses two faces, and an unbanded face stops the aggregate rather
                    than being skipped over by weakest-face -->
        <!-- partial: the calibration table is a fixture; deriving its thresholds from a
                    labelled corpus is a measurement exercise -->
  - [x] I4.3.e Report raw similarity, calibration band, uncertainty,
        association, reference quality, and failure reason without exposing
        biometric embeddings. Evidence: result/privacy tests.
        <!-- evidence: identity-report.ts — AN EMBEDDING IS THE PERSON: the reported
                    shapes cannot carry a vector, a crop or a handle (gated on the
                    schema's own interface bodies), and the suite serializes a report and
                    searches for the fixture's marker; an unassociated face is reported
                    WITH its reason rather than omitted -->
  - [x] I4.3.f Enforce retention/access/deletion rules for derived biometric
        processing artifacts independently of the asset. Evidence: privacy
        lifecycle tests.
        <!-- evidence: identity-retention.ts — THE DERIVATIVE HAS ITS OWN CLOCK: access is
                    purpose-, role-, time- and consent-bound (a withdrawal cuts it INSIDE
                    the window), the sweep actually removes expired artifacts at the
                    instant, erasure deletes everything biometric while the record that
                    proves we stopped survives, and both independence directions are
                    asserted — erasing a person leaves the pictures, deleting the asset
                    leaves the derivatives -->
- [ ] I4.4 Implement face/hand/anatomy artifact detection. Evidence: labeled
      evaluation corpus and threshold report.
      <!-- partial: a, c, d, e and g are done in evidence/v1-workbenches/anatomy-artifacts/
                  (110 assertions, 62/62 mutants). The PARENT stays open because its bar
                  names the labelled corpus and the threshold report over it, which are
                  I4.4.b and I4.4.f — data collection, not code -->
  - [x] I4.4.a Define versioned artifact labels, regions, severity,
        applicability, evaluator outputs, and annotation guidance for faces,
        hands, limbs, joints, body proportions, and interactions. Evidence:
        taxonomy schema.
        <!-- evidence: evidence/v1-workbenches/anatomy-artifacts/ (compiling reference) —
                    18 versioned labels over 4 groups with severity, applicability,
                    measurement kind and annotation guidance; inspectTaxonomy catches
                    duplicate, unregistered, unguided and exemptible-structural rows, and
                    the suite walks EVERY detection it produced to check it is reported in
                    the unit its own taxonomy declares (which caught two real unit errors)
                    + anatomy-artifacts.json via generate-anatomy-artifacts.mjs; 110
                    assertions, 62/62 mutants -->
  - [ ] I4.4.b Assemble licensed, consented, representative positive/negative/
        hard-case corpus with demographic, pose, style, crop, occlusion, and
        multi-person coverage. Evidence: corpus manifest.
        <!-- blocked: assembling a licensed, consented image corpus is data collection
                    with real people in it; no amount of code produces one -->
  - [x] I4.4.c Detect face feature count/placement/asymmetry/merging and
        high-severity structural artifacts with localized confidence. Evidence:
        face artifact fixtures.
        <!-- evidence: anatomy-face.ts — real geometry over the landmark graph: feature
                    counts, ordering down the face and eye separation as SCALE-FREE
                    fractions of the face box (the same face at half the size is the same
                    face), area asymmetry, and region overlap for merged features;
                    CONFIDENCE IS PROPAGATED (a finding is as sure as its weakest
                    landmark) and an occluded eye makes symmetry UNVERIFIABLE, not passed -->
        <!-- partial: the landmark estimator is the seam; this measures what it returns -->
  - [x] I4.4.d Detect hand/finger count/shape/joint/contact/occlusion artifacts
        with left/right and person association. Evidence: hand fixtures.
        <!-- evidence: anatomy-hands.ts — finger count AND non-canonical finger names,
                    joint bend from a real acos angle (with the domain clamp shown to
                    matter: at one fixture's coordinates the cosine floats to
                    1.0000000000000004 and the unclamped angle is NaN, so the most broken
                    finger in the frame reports nothing), distal taper, bone-vs-bone
                    segment intersection, and A LIMB BELONGS TO SOMEBODY: two left hands
                    on one figure is itself the artifact -->
  - [x] I4.4.e Detect limb/joint/torso/proportion/attachment/intersection and
        subject-object interaction anomalies. Evidence: anatomy fixtures.
        <!-- evidence: anatomy-body.ts — limb counts, attachment to the joint a limb says
                    it hangs off AND the gap to it, PROPORTION IS A RATIO (measured
                    against the published anthropometric fractions of the figure's own
                    height, so a smaller person is not an artifact), joint range, limb
                    self-intersection (limbs sharing a joint touch there by construction
                    and are not flagged), and an object declared held that touches no hand -->
  - [ ] I4.4.f Calibrate per-label/style/content thresholds and manual-review
        bands; report precision/recall/false-positive/false-negative slices.
        Evidence: threshold report.
  - [x] I4.4.g Emit raw detections/regions/scores/uncertainty separately from
        policy verdict, respecting sensitive-region access. Evidence: result
        contract tests.
        <!-- evidence: anatomy-report.ts — the raw Detection carries no outcome, severity
                    or reason (gated on the schema's own interface body); A REGION IS THE
                    PICTURE OF A BODY, so face and hand coordinates are withheld from
                    roles without that access — asserted on the SERIALIZED record — while
                    the finding itself still appears; a verdict cannot come back clean
                    while anything is unverifiable, and a structural count rejects at a
                    confidence the ladder would only have reviewed, in a stylized profile
                    too -->
- [x] I4.5 Implement texture resolution, tiling, seam, channel-packing, and PBR
      consistency checks. Evidence: known material fixtures.
      <!-- evidence: evidence/v1-workbenches/texture-checks/ (compiling reference over real
                  texels; a material authored to tile exactly with a normal that decodes to
                  unit length, and the same material broken one map at a time) +
                  texture-checks.json via scripts/v1-workbenches/generate-texture-checks.mjs;
                  119 assertions, 58/58 mutants. Every family of check is proved to REACH
                  the verdict, so no file could be quietly unwired -->
  - [x] I4.5.a Resolve texture-set membership, material/shader profile, target,
        UV/UDIM mapping, channel semantics, color spaces, units, and expected
        resolution. Evidence: texture-set fixtures.
        <!-- evidence: texture-maps.ts resolveSet — A MAP IS A MEMBER OF A SET, NOT A FILE:
                    a missing role, an undeclared one, two maps claiming the same role, a
                    numeric map tagged sRGB (checked in BOTH directions) and a claimed UDIM
                    tile with no map are all defects of the SET -->
  - [x] I4.5.b Measure per-map dimensions, aspect, bit depth, mip presence,
        texel density, UDIM coverage, naming, and target budgets. Evidence:
        resolution fixtures.
        <!-- evidence: dimensions and aspect, powers of two on BOTH sides, the mip chain
                    counted down to one texel (1024 implies eleven levels), byte budgets,
                    the naming pattern, and texel density in texels per WORLD UNIT — so a
                    smaller map over a smaller surface is the same density -->
  - [x] I4.5.c Evaluate intended tile periodicity, border continuity, obvious
        repetition, scale, and wrap modes in UV context. Evidence: tiling
        fixtures.
        <!-- evidence: texture-tiling.ts — A TILE THAT DOES NOT MEET ITSELF IS NOT A TILE:
                    the reading is the mean absolute difference across each wrap (both axes
                    have their own case), repetition is the smallest shift at which the map
                    lies on itself, A FLAT MAP DOES NOT REPEAT IT IS CONSTANT, and a texture
                    nobody said tiles is not judged on tiling at all -->
  - [x] I4.5.d Detect UV island/UDIM boundary seams, padding/bleed,
        tangent-space normal discontinuity, and channel misalignment. Evidence:
        seam fixtures.
        <!-- evidence: the boundary is measured across the island edge, padding is read
                    from the WORST row because that is the row the mip chain will find, the
                    normal across a seam is its own reading (a colour map has no normal to
                    turn), and a set that declares NO background reports no padding rather
                    than a number it could not have measured -->
  - [x] I4.5.e Verify channel packing/order/range/inversion/defaults for
        roughness/metalness/AO/height/opacity/masks per target profile.
        Evidence: packing fixtures.
        <!-- evidence: texture-pbr.ts — each declared slot is read from the channel the
                    profile names, a slot pointing past the end of the file is a blocker, a
                    channel with one value in it is a default rather than a map, and the
                    inversion check is LABELLED A HEURISTIC and only ever asks -->
  - [x] I4.5.f Verify base-color nonlighting, normal convention/normalization,
        physical metalness/roughness/range, and cross-map PBR consistency.
        Evidence: PBR material corpus.
        <!-- evidence: A NORMAL IS A UNIT VECTOR OR IT IS NOT A NORMAL (every texel decodes
                    to a length); the green convention is INFERRED by correlating the map
                    against the height it was baked from — both conventions are recovered
                    from real bakes and a flat normal infers nothing; METALNESS IS A
                    CLASSIFICATION NOT A DIAL; base colour is bounded at both ends, so a
                    baked shadow and an impossible highlight are both findings -->
        <!-- partial: the corpus is synthesized materials with derivable properties, not
                    scanned real-world ones -->
  - [x] I4.5.g Emit per-map/set raw measures, preview regions, verdicts, and
        target-specific remediation. Evidence: texture gate tests.
        <!-- evidence: texture-report.ts — the raw record carries no severity, bound,
                    remediation or outcome (gated on the schema's own interface bodies and
                    on the serialized record); every finding names its map, its measurement
                    and a remediation, and every bound repeats its measurement's unit -->
- [x] I4.6 Implement video duration, frame-rate, codec, resolution, freeze,
      duplicate-frame, flicker, and temporal-coherence checks. Evidence: crafted
      clips yield known results.
      <!-- evidence: evidence/v1-workbenches/video-checks/ (compiling reference over a real
                  ISO base media file built box by box and real frame planes; a clip
                  authored to be clean, and the same clip broken one thing at a time) +
                  video-checks.json via scripts/v1-workbenches/generate-video-checks.mjs;
                  200 assertions, 89/89 mutants. Every family of check is proved to REACH
                  the verdict, so no file could be quietly unwired -->
  - [x] I4.6.a Parse container/streams with byte/duration/frame/time/resource
        limits and verify declared media against decoded content. Evidence:
        hostile video fixtures.
        <!-- evidence: video-container.ts — A BOX THAT CONTAINS ITSELF IS NOT A CONTAINER:
                    every read is bounded (a byte past the end refuses rather than returning
                    a zero), a box that does not clear its own header is refused, a child is
                    bounded by its PARENT rather than by the end of the file, and depth, box
                    count, byte count, sample count and DURATION are all capped before
                    anything is allocated against them. A DECLARATION IS NOT A MEASUREMENT:
                    the time table, the size table and the pictures that decoded are three
                    claims about one clip, and the disagreement is the finding -->
  - [x] I4.6.b Measure duration, start/end/time base, frame count, constant/
        variable rate, dropped/irregular timestamps, and target tolerances.
        Evidence: timing fixtures.
        <!-- evidence: A FRAME HAS A TIME, NOT AN INDEX — the rate is read from the sample
                    deltas in the media timescale, so A MISSING FRAME IS STILL ON THE GRID
                    (a delta of exactly twice the nominal one) while a delta between two
                    grid points is a wobbly timestamp; the two are different departments and
                    are measured apart from each other -->
  - [x] I4.6.c Verify codec/profile/level/pixel format/bit
        depth/chroma/color/HDR/ bitrate/GOP/container and target compatibility.
        Evidence: codec matrix.
        <!-- evidence: video-codec.ts — the configuration record is walked past its
                    parameter sets to reach chroma and depth, and AN UNDECLARED PIXEL FORMAT
                    IS NOT A COMPLIANT ONE (a baseline record declares neither and neither is
                    invented for it); the rate is the coded sample sizes over the media
                    duration rather than a number in a header; the group of pictures includes
                    the tail after the last sync sample; and an HDR transfer an SDR target
                    cannot show is a GRADE, not a re-encode -->
  - [x] I4.6.d Measure encoded/display resolution, aspect, rotation, crop, safe
        area, and target dimension/alignment constraints. Evidence: resolution
        fixtures.
        <!-- evidence: A BAR IS NOT A PICTURE — the active picture is found in the rows and
                    columns themselves and only where they are flat AND dark on EVERY frame,
                    so a dark first frame is a fade, a bright flat band is a slate and a clip
                    that is dark throughout is a black clip rather than a crop it never had;
                    a quarter turn in the track matrix swaps the sides; the aperture is
                    bounded on all four sides; and the safe-area reading is LABELLED A
                    HEURISTIC and only ever asks -->
        <!-- partial: the safe-area reading is edge detail, not recognized text -->
  - [x] I4.6.e Detect configured freeze intervals and duplicate/near-duplicate
        frames while distinguishing intentional holds/loops. Evidence: freeze/
        duplicate fixtures.
        <!-- evidence: A HOLD IS NOT A FREEZE, AND A LOOP IS NOT A DUPLICATE — a run without
                    movement is still measured when the asset declared it held, and simply is
                    not a finding; and A LOOP CLOSES WHEN ITS LAST STEP LOOKS LIKE ITS OTHER
                    STEPS, so the join is held against the clip's own median step rather than
                    against zero (a loop whose last picture equalled its first would stutter
                    on every repeat), while a clip nobody declared a loop is not judged on
                    its join at all -->
  - [x] I4.6.f Detect luminance/chroma/exposure flicker with temporal windows,
        scene-cut awareness, localized regions, and uncertainty. Evidence:
        flicker fixtures.
        <!-- evidence: A CUT IS NOT A FLICKER — shots are found first and every window lives
                    inside one, so two halves eighty levels apart are a cut rather than a
                    grade going wrong; a corner that oscillates while the opposite corner
                    goes down by the same amount is found by its TILE behind a frame mean
                    that never moves; chroma is read from the worse of its two planes; a ramp
                    is exposure drift and not a strobe; and a shot too short to hold one
                    window is reported as UNCERTAIN with no number invented for it -->
  - [x] I4.6.g Measure temporal identity/geometry/texture/background/motion
        coherence with scene/shot boundaries and evaluator applicability.
        Evidence: coherence corpus.
        <!-- evidence: AN EVALUATOR WITHOUT ITS SUBJECT IS NOT A PASS — identity, geometry
                    and background need a subject track and, for the background, a camera
                    that was not moving; without them each says which input it lacked,
                    invents no reading, and the verdict CARRIES that it said so. The readings
                    are taken from the pixels: appearance histogram plus chroma inside the
                    box (a subject that keeps its brightness and changes hue is a different
                    subject), the measured extent of the brighter half of its own box, local
                    gradient energy, the frame outside the boxes, and the shift at which one
                    picture best lies on the next -->
        <!-- partial: the corpus is synthesized clips with derivable properties, and the
                    subject boxes are a detector's output taken as input -->
  - [x] I4.6.h Emit timecoded raw measurements/regions separately from verdicts
        and remediation. Evidence: video gate contracts.
        <!-- evidence: video-report.ts — the raw record is timecoded throughout and carries
                    no severity, remediation, bound or outcome (gated on the schema's own
                    interface bodies and on the SERIALIZED record); every finding cites a
                    measurement, repeats its unit in its bound and says when and where; a
                    file that cannot be read is refused with its reason rather than measured
                    as an empty one; and a file whose only problem is that it is not what it
                    said it was still does not pass -->

- [x] I4.7 Implement AV-sync and caption/transcript checks where audio is
      present. Evidence: aligned and misaligned fixtures.
      <!-- evidence: evidence/v1-workbenches/av-sync-checks/ (compiling reference over a real
                  two-track ISO base media file, speech built out of actual samples, a mouth
                  that opens on the same syllables, and caption documents as bytes) +
                  av-sync-checks.json via scripts/v1-workbenches/generate-av-sync-checks.mjs;
                  176 assertions, 91/91 mutants. The container walk is the I4.6 reference's,
                  reused rather than written twice. Every family of check is proved to REACH
                  the verdict -->
  - [x] I4.7.a Detect audio presence/tracks/languages/channels and return
        explicit not-applicable when the profile permits silent media. Evidence:
        applicability tests.
        <!-- evidence: av-tracks.ts — the tracks are grouped out of the container walk by
                    their own byte offsets (the only way two tracks in one file can be told
                    apart), each carrying its handler, codec, timescale, packed ISO 639-2/T
                    language, channels and sample rate. SILENT AND UNMEASURED ARE DIFFERENT
                    WORDS: no audio on a target that permits silence is NOT APPLICABLE and
                    says which of the three reasons applied, while silence nobody agreed to
                    is a blocker. GOTCHA proved in the suite: A SAMPLE RATE IS UNSIGNED —
                    48000 does not fit in a signed 16.16 integer part -->
  - [x] I4.7.b Normalize video/audio/caption time bases, edits, delays, handles,
        variable frame rate, and discontinuities without rewriting sources.
        Evidence: time-base fixtures.
        <!-- evidence: A CONVERSION IS NOT AN EDIT — each track keeps its own timescale, its
                    own edit list and its own deltas, and the common millisecond timeline is
                    a MAPPING taken from them: a declared delay pushes a track later, an edit
                    that skips media pulls what follows earlier, a head handle sits before
                    zero, and what the tracks have in COMMON is the only span on which they
                    can be compared. A FRAME'S TIME IS THE SUM OF THE DELTAS BEFORE IT, shown
                    against the index-times-nominal answer that would have been wrong -->
  - [x] I4.7.c Measure speech-to-mouth and general event-to-sound offset/drift
        with speaker/event association, confidence, and timecoded regions.
        Evidence: aligned/misaligned AV fixtures.
        <!-- evidence: av-offset.ts — a real RMS envelope over the samples, correlated
                    against the mouth track at every lag: SYNC IS A RELATION BETWEEN TWO
                    CLOCKS, so a constant lag and a drift are separate findings with separate
                    remediations, and A LEAD IS WORSE THAN A LAG (the two bounds are not the
                    same number). A PEAK WITHOUT A MARGIN IS NOT A MEASUREMENT: a perfectly
                    regular rhythm lines up equally well a syllable over, so the reading
                    reports its own ambiguity, invents no offset, and no slope is computed
                    out of two readings that were never firm -->
        <!-- partial: the mouth track is an upstream detector's output taken as input, and
                    the corpus is synthesized speech rather than recorded -->
  - [x] I4.7.d Parse caption/subtitle/transcript formats safely and validate
        encoding, language, cue order, overlap, duration, reading speed, line/
        character limits, and target rules. Evidence: caption format tests.
        <!-- evidence: av-captions.ts — a strict UTF-8 decode written out so that an overlong
                    form, a lone continuation byte, a surrogate half and a truncated sequence
                    each refuse with their own reason rather than becoming a replacement
                    character nobody notices; SubRip and WebVTT timestamps parsed rather than
                    guessed; A CUE IS A CLAIM ABOUT A TIME, so one that ends before it starts
                    is refused rather than measured; and READING SPEED IS A RATE, NOT A
                    LENGTH — markup and position overrides come off before anything is
                    counted -->
  - [x] I4.7.e Compare transcript/caption text to speech with language/speaker/
        non-speech cue handling, word timestamps, error metrics, and
        uncertainty. Evidence: transcript accuracy fixtures.
        <!-- evidence: av-transcript.ts — a word-level alignment whose edits are counted by
                    KIND (substitutions, deletions and insertions separately). A NON-SPEECH
                    CUE IS NOT A MISTAKE: bracketed sounds, music marks and speaker labels
                    come out first; A DIFFERENT LANGUAGE IS NOT AN ERROR RATE (a caption in
                    another language is unavailable, not a hundred per cent wrong); AN ERROR
                    RATE OVER A SHORT SAMPLE IS NOISE; and a recognizer that is not sure of
                    what it heard is not a reference to blame the captions against -->
  - [x] I4.7.f Emit separate raw AV-sync and caption/transcript measurements,
        not-applicable/unavailable states, verdicts, and remediation. Evidence:
        result contract tests.
        <!-- evidence: av-report.ts — the raw record is timecoded and carries no severity,
                    remediation, bound or outcome (gated on the schema's own interface bodies
                    and on the SERIALIZED record); the verdict carries BOTH what did not
                    apply and what could not be measured, which are different words for
                    different things; a caption document that cannot be read is refused with
                    its own reason rather than measured as an empty one; and a target that
                    never asked how they line up is told nothing was asked instead of being
                    handed a clean report nobody ran -->

- [x] I4.8 Implement audio sample-rate, channel, clipping, silence, loudness,
      true-peak, noise, and duration checks. Evidence: calibrated WAV fixtures.
      <!-- evidence: evidence/v1-workbenches/audio-checks/ (compiling reference over real
                  RIFF/WAVE files built chunk by chunk, decoded to floats, over signals with
                  derivable properties) + audio-checks.json via
                  scripts/v1-workbenches/generate-audio-checks.mjs; 172 assertions,
                  93/93 mutants. Every family of check is proved to REACH the verdict -->
  - [x] I4.8.a Decode supported audio/container formats with
        byte/sample/channel/ duration/time/memory limits and verify declared
        content. Evidence: hostile audio fixtures.
        <!-- evidence: audio-riff.ts — a bounded chunk walk (a byte past the end refuses
                    rather than returning a zero; the pad byte after an odd chunk belongs to
                    nobody and forgetting it lands mid-header; a chunk claiming more than the
                    file holds is refused) with byte, frame, channel and DURATION limits
                    applied before the arrays they would size are built. Each width is taken
                    apart by hand and each round-trips, including a negative sample at every
                    one of them. The header describes its samples three times over — block
                    alignment, byte rate and chunk size — and where those disagree the
                    disagreement is the finding -->
  - [x] I4.8.b Measure sample rate, bit depth/format, channel
        count/layout/order, phase/polarity, codec/bitrate, and target
        compatibility. Evidence: format/channel fixtures.
        <!-- evidence: audio-format.ts — rate, width, format (including an extensible file
                    whose real format is in its sub-format GUID), channel count, layout, the
                    speaker order its channel mask names, the bits a second the samples run
                    at, and the correlation between the channels: a pair carrying the same
                    programme with one of them inverted reads as minus one, and cancels the
                    moment anybody folds them to mono -->
  - [x] I4.8.c Detect sample/sequence clipping and inter-sample true peaks per
        channel with exact locations and calibrated oversampling. Evidence:
        clipping fixtures.
        <!-- evidence: A SAMPLE PEAK IS NOT A PEAK — the true peak is measured on a
                    four-times oversampled signal through a polyphase windowed-sinc built in
                    the file (odd tap count, so the zeroth phase is the sample itself and
                    every phase passes a constant as itself). A tone sampled either side of
                    every crest reports a peak three decibels above any sample in it, at a
                    position between two of them, while no single sample is at the ceiling;
                    and the reading can never come back UNDER the samples it contains -->
  - [x] I4.8.d Measure leading/trailing/internal silence, dropout, stuck
        samples, DC offset, and intended-silence exemptions. Evidence:
        silence/dropout fixtures.
        <!-- evidence: SILENCE SOMEBODY ASKED FOR IS NOT A DROPOUT — the same hole, declared,
                    produces neither finding. Where a run sits decides what it is (leading,
                    trailing or internal); a dropout is measured against the MEDIAN of the
                    programme around it rather than an absolute level, so a hole that never
                    reaches silence is still a hole; and a run of identical NON-ZERO samples
                    is a converter that stopped, while a run of zeros is silence -->
  - [x] I4.8.e Measure integrated/short-term/momentary loudness, loudness range,
        dialogue-gated loudness, and true peak to the target standard. Evidence:
        calibrated loudness fixtures.
        <!-- evidence: audio-loudness.ts — K-weighting (shelf then high pass), 400ms blocks
                    at a 100ms hop, the absolute gate at −70 LUFS and the relative gate ten
                    below the material itself. THE CALIBRATION IS AGAINST THEORY: the filter
                    exposes its own transfer function, and a 1kHz tone at −20dBFS measures
                    within a tenth of a decibel of what that function predicts — as does a
                    60Hz tone, which only the second stage explains. Doubling the amplitude
                    adds exactly six decibels, a stereo pair is exactly three above the same
                    programme on one channel, half a file of silence moves the integrated
                    loudness by under a decibel where an ungated mean would drop three, and a
                    file entirely below the absolute gate has no loudness at all. The range
                    is a PERCENTILE spread rather than a span, and dialogue is the same
                    measurement over the blocks a detector marked as speech -->
        <!-- partial: the weighting coefficients are the published ones for 48kHz, so a file
                    at any other rate is reported as not calibrated rather than measured -->
  - [x] I4.8.f Estimate noise floor/SNR/hum/buzz/hiss/clicks with speech/music/
        ambience applicability, regions, and uncertainty. Evidence: noise
        fixtures.
        <!-- evidence: A NOISE FLOOR IS A QUIET PERCENTILE, NOT A MINIMUM — one ten-
                    millisecond hole twenty decibels down is not the floor of the recording,
                    and a recording with no quiet passage in it has no measurable floor,
                    which is reported rather than guessed. AN EVALUATOR WITHOUT ITS CONTENT
                    IS NOT A PASS: ambience is the floor rather than something above one, so
                    it is not judged on its own quietness. The hum is a Goertzel bin at the
                    mains frequency AND its harmonics (a hum that is all third harmonic is
                    still a hum), hiss is the share of energy above four kilohertz, and a
                    click is one sample that jumps away from BOTH its neighbours -->
  - [x] I4.8.g Measure exact sample duration against declared/timeline/target
        timing and account for encoder delay/padding. Evidence: duration tests.
        <!-- evidence: A DECODED FILE IS LONGER THAN THE MUSIC IN IT — the encoder's priming
                    samples and its padding come off before the length is held against a
                    target, so the same file passes a target that asked for the programme and
                    fails one that asked for the file; a `fact` chunk is a claim held against
                    the samples rather than trusted over them; and a timeline with less room
                    than the programme needs is its own finding -->
  - [x] I4.8.h Emit per-channel/time-region raw results separately from verdicts
        and remediation. Evidence: audio gate contracts.
        <!-- evidence: audio-report.ts — the raw record is per channel and carries no
                    severity, remediation, bound or outcome (gated on the schema's own
                    interface bodies and on the SERIALIZED record); the verdict carries what
                    was not measured and why; a file with no samples in it is refused with
                    its reason rather than measured as a silent one; and a file whose only
                    problem is that its header contradicts itself still does not pass -->

- [x] I4.9 Implement mesh topology, manifoldness, watertightness, normals, UV,
      scale, pivot, material, and polygon-budget checks. Evidence: golden mesh
      corpus.
      <!-- evidence: evidence/v1-workbenches/mesh-checks/ — a COMPILING strict-tsc
                  reference (schema/math/parse/topology/manifold/normals/uv/transform/
                  material/budget/report + fixtures + a 274-assertion suite), gated three
                  ways by verify:inventory:v1-workbenches (generator --check, tsc --noEmit,
                  and the suite run under tsx). The corpus is a real glTF binary assembled
                  chunk by chunk and a real Wavefront OBJ read line by line; the shapes in
                  it are built so that what they are is known before anything measures them
                  (a cube of eight points and characteristic two, a torus of forty-eight and
                  characteristic nothing) -->
  - [x] I4.9.a Parse supported mesh/scene formats with vertex/face/object/
        material/dependency/depth/time/memory limits and content sniffing.
        Evidence: hostile mesh fixtures.
        <!-- evidence: mesh-parse.ts — THE BYTES DECIDE: the format is sniffed from the
                    glTF magic or from a text file having both points and faces, and a file
                    whose declared format disagrees is read as what it is with the
                    disagreement carried. 13 refusals, each reached by its own hostile
                    fixture (truncated, unknown-format, unsupported-version,
                    chunk-size-invalid, chunk-overruns-file, missing-required-chunk,
                    document-invalid, accessor-out-of-range, index-out-of-range,
                    unsupported-component-type, node-cycle, depth-limit-exceeded,
                    resource-limit-exceeded); byte, vertex, face, object, depth, dependency
                    and step limits all applied before the array they would size is built,
                    with the two paths to the shared resource refusal distinguished by
                    their own messages. A VERTEX LIMIT IS NOT AN INDEX LIMIT, and A CORNER
                    IS A TRIPLE: an OBJ point carrying two coordinates is two vertices,
                    indices count from one and backwards from the end -->
  - [x] I4.9.b Measure vertices/edges/faces/triangles/ngons, degenerate/zero-
        area/duplicate/isolated elements, poles, density, and topology by
        object. Evidence: topology fixtures.
        <!-- evidence: mesh-topology.ts — A SEAM IS NOT A CRACK: counted on welded points,
                    where welding is a search across the twenty-seven cells around each
                    candidate rather than a hash lookup, so a cube split for its normals is
                    twenty-four vertices and eight points. A TRIANGLE COUNT IS NOT A FACE
                    COUNT: the same cube as quads is six faces, twelve edges and twelve
                    triangles. Degenerate, zero-area, duplicate (by point SET, so a rotated
                    face is the same face), non-planar, isolated and redundant elements are
                    separated from one another — a corner that meets itself makes no new
                    edge and is reported once, as the thing it is -->
  - [x] I4.9.c Detect nonmanifold edges/vertices, boundaries,
        self-intersections, holes, disconnected shells, and target-specific
        watertightness. Evidence: manifold/watertight fixtures.
        <!-- evidence: mesh-manifold.ts — A HOLE IS A LOOP, NOT A MISSING FACE: boundary
                    edges are walked into the rings they form (an open box is one loop of
                    four, a bowtie is two). Shells are connected components over shared
                    edges; a pinched vertex is found by the faces around it failing to make
                    one fan; the characteristic comes out at two for a closed box either
                    triangulated or as quads, four for two boxes, and nothing at all for a
                    torus whose genus is therefore one. Self-intersection is real
                    triangle-against-triangle, and TWO TRIANGLES IN THE SAME PLANE HAVE NO
                    SEPARATING AXIS OUT OF IT — a torus, whose rings sit in bands at the
                    same height, is reported as a knot by any test that forgets that.
                    WATERTIGHT IS A PROPERTY OF A TARGET: all of it is measured, and the
                    same open box passes one target and fails another -->
  - [x] I4.9.d Validate face/vertex/tangent normals for
        missing/zero/inconsistent/ inverted state, smoothing groups, hard edges,
        and target convention. Evidence: normal fixtures.
        <!-- evidence: mesh-normals.ts — A NORMAL DOES NOT RIDE THE MATRIX THE POINTS RIDE:
                    vertex normals go through the inverse transpose, so a mirror above an
                    object turns its surface inside out while the file is unchanged, and
                    under a fourfold stretch the corner leans at the angle the transpose
                    predicts. Face planes come from Newell over the whole cycle. Winding is
                    read off the direction two faces cross the edge they share, so inverted
                    normals (volume still positive), one flipped face (three disagreeing
                    pairs) and a wholly inside-out solid (consistent, volume negative) are
                    three separable findings. A hard edge is only UNSPLIT where something
                    claims the surface is smooth across it — a smoothing group, or a shared
                    vertex normal — so the properly split cube is clean -->
  - [x] I4.9.e Validate UV sets, required channels, bounds, overlap, distortion,
        texel density, island padding, UDIM layout, and missing faces. Evidence:
        UV fixtures.
        <!-- evidence: mesh-uv.ts — A UV IS AN AREA, NOT A COORDINATE. A SHELL THAT FILLS A
                    TILE EXACTLY IS IN THAT TILE: an upper edge landing on a border belongs
                    to the tile it bounds, and a floor over the corners reports every
                    well-packed island as spilling next door. A face that leaves its tile is
                    a separate finding from an overlap; neighbours are NOT excluded from the
                    overlap test the way they are in space, because a shell folded back over
                    itself shares an edge AND covers the texture twice. Density is a length
                    over a length (the same sheet on four times the surface is half the
                    texels, not a quarter) and is carried as a distribution; padding is the
                    gap between two ISLANDS, in texels. A face has coordinates or it has
                    not: three corners of a quad is a face without them -->
  - [x] I4.9.f Resolve declared/source/scene/target units and axes; measure
        bounds, scale, transforms, origin/pivot, grounding, and orientation.
        Evidence: scale/pivot fixtures.
        <!-- evidence: mesh-transform.ts — A NUMBER WITHOUT A UNIT IS NOT A LENGTH: what the
                    file wrote down, what its format fixes, and what the pipeline knows are
                    three different facts trusted in that order, and each of the three is
                    exercised. A FILE THAT DECLARES NO UNIT HAS NO SCALE — an OBJ with
                    nothing behind it reports null rather than guessing metres, keeps its
                    bounds in file units, and carries the missing reading on the verdict.
                    The hundredfold error is caught against an expected size range. A
                    negative determinant is a mirror, grounding is measured along the axis
                    the target calls up and in the direction it calls up, a translation
                    moves the origin with the geometry so it does not move the pivot, and
                    the orientation heuristic asks for review rather than failing -->
  - [x] I4.9.g Validate material slots/assignments/names/shader compatibility/
        texture references and unassigned or unused materials. Evidence:
        material assignment tests.
        <!-- evidence: mesh-material.ts — slots, assignments and the frame a normal map
                    needs are per object; names, features, files and dependencies belong to
                    the library. A path that names a place (rooted, drive-lettered, fetched
                    over a network, or climbing out of the delivery) is told from one that
                    names a file in it, while an embedded image is already here. An OBJ
                    names its images inside a material library it did not bring, so nothing
                    is claimed about textures that cannot be read from what was delivered -->
  - [x] I4.9.h Enforce per-object/per-LOD/scene polygon, vertex, material, draw-
        call, and memory budgets for the selected target. Evidence: budget
        boundary tests.
        <!-- evidence: mesh-budget.ts — A SLOT NOBODY USES IS NOT A DRAW CALL, and a
                    material no face wears costs no texture memory. The renderer pays for
                    the vertices in the buffer rather than the points on the surface;
                    indices are two bytes until the count passes sixty-five thousand five
                    hundred and thirty-six and four bytes one vertex later (asserted at the
                    boundary itself); a texture costs its own area plus every mip under it
                    (a thousand-texel map is 5,592,404 bytes exactly). A level of detail is
                    a promise to be smaller, and a level the delivery promised and did not
                    bring is a finding of its own -->
  - [x] I4.9.i Emit per-object and aggregate raw measurements, locations,
        verdicts, and repair guidance. Evidence: mesh gate contracts.
        <!-- evidence: mesh-report.ts — A MEASUREMENT IS NOT A VERDICT: the raw record is
                    per object and per element and carries no severity, remediation, bound
                    or outcome (gated on the schema's own interface bodies AND on the
                    SERIALIZED record), while the verdict carries the object, the element,
                    the measurement, the bound and the repair. Each item's measurement is
                    shown to stand alone and to be carried through unaltered. Every one of
                    the seven families of check is shown to reach the verdict from a single
                    asset broken in all of them, so no file could be quietly unwired. AN
                    EVALUATOR WITHOUT ITS SUBJECT IS NOT A PASS — a pair scan too large to
                    run is carried as not measured — and a file whose only problem is that
                    its header contradicts itself still does not pass -->
- [x] I4.10 Implement rig hierarchy, bind pose, skin-weight, deformation, and
      animation-range checks. Evidence: valid and broken rig fixtures.
      <!-- evidence: evidence/v1-workbenches/rig-checks/ — a COMPILING strict-tsc
                  reference (schema/math/parse/hierarchy/bind/skin/deform/clips/report +
                  fixtures + a 234-assertion suite), gated three ways by
                  verify:inventory:v1-workbenches (generator --check, tsc --noEmit, and the
                  suite run under tsx). The corpus is a real binary glTF assembled chunk by
                  chunk — nodes, skins, inverse binds, joints, weights and animation
                  samplers — and a real BVH written line by line with its braces, its
                  channel lists and one motion row per frame; the skeletons in it are built
                  so that what they are is known before anything measures them (a chain of
                  three joints a metre apart, a limb of twelve points whose middle ring
                  hangs half from one joint and half from the next). 61 of 61 mutations to
                  the implementation are caught by the suite -->
  - [x] I4.10.a Parse skeleton/skin/animation data with
        bone/influence/key/frame/ clip/dependency/resource limits and stable
        joint identities. Evidence: hostile rig fixtures.
        <!-- evidence: rig-parse.ts — THE BYTES DECIDE: the format is sniffed from the glTF
                    magic or from text that declares a hierarchy, roots it and has motion
                    under it, and a file whose delivery disagrees is read as what it is with
                    the disagreement carried. 15 refusals, each reached by its own hostile
                    fixture (truncated, unknown-format, unsupported-version,
                    chunk-size-invalid, chunk-overruns-file, missing-required-chunk,
                    document-invalid, accessor-out-of-range, joint-index-out-of-range,
                    unsupported-component-type, joint-cycle, hierarchy-depth-exceeded,
                    sampler-length-mismatch, frame-width-mismatch, resource-limit-exceeded);
                    byte, joint, vertex, clip, key, depth and step limits all applied before
                    the array they would size is built. A KEY LIMIT IS NOT A VERTEX LIMIT, and
                    A NAME IS NOT AN IDENTITY: every joint is keyed by its path from the
                    root, so two joints called the same thing are two joints. A cycle is
                    refused before anything walks it, and a node carrying geometry is not a
                    joint however deep inside the armature it hangs -->
  - [x] I4.10.b Validate hierarchy roots, parent cycles, duplicate/missing
        joints, names, required bones, orientation, scale, transforms, and
        target convention. Evidence: hierarchy fixtures.
        <!-- evidence: rig-hierarchy.ts — A BONE IS THE GAP BETWEEN TWO JOINTS, NOT A JOINT:
                    the bone a joint owns is the one below it, so three joints in a chain are
                    two bones; A BRANCH HAS NO ONE DIRECTION, so the hips report none; a leaf
                    has none either unless the file says where it ends, which a BVH end site
                    does and a glTF does not; and the gap ABOVE a joint is the separate
                    reading that catches two joints sitting on top of one another. AN AXIS IS
                    DECLARED, NOT DISCOVERED — a humanoid is nearly as long below its root as
                    above it, so which way up comes from the container's own specification or
                    from the delivery, and where neither says, nothing is guessed. A mirror is
                    a negative scale and is told from a shear, which is not a rotation and a
                    scale at all -->
  - [x] I4.10.c Validate bind/reference/rest pose matrices, inverse binds,
        skeleton/mesh transform agreement, and expected pose/profile. Evidence:
        bind-pose fixtures.
        <!-- evidence: rig-bind.ts — AN INVERSE BIND IS A CLAIM, AND A CLAIM CAN BE CHECKED:
                    each one is put back through the skeleton and must come out as identity,
                    a half-metre error reads as a half-metre deviation, and one that
                    flattens a dimension is refused as something nothing can undo. A skin
                    that declares none is read the way a renderer reads it rather than as
                    what the exporter meant. Skinning replaces the transform on the node the
                    mesh hangs from, so a non-identity one is reported as lost. The bind pose
                    is classified by the arm's elevation — level is a T, forty-five degrees
                    down is an A, and twenty degrees is NEITHER and is asked about rather
                    than called the nearer of the two. Symmetry is a mirror of one side
                    against the other, and a skeleton bound at a hundred times the size of
                    the mesh is caught from both sides of the bound -->
  - [x] I4.10.d Measure per-vertex influence count, missing/negative/NaN
        weights, normalization, tiny weights, joint references, and target
        limits. Evidence: skin-weight fixtures.
        <!-- evidence: rig-skin.ts — AN INFLUENCE WITH NO WEIGHT IS NOT AN INFLUENCE: the
                    corpus writes four slots per vertex and carries weight in one or two, so
                    a target that allows three is told nothing and a target that allows one
                    is told about two. A WEIGHT THAT IS NOT NORMALIZED IS A SCALE, so the sum
                    is measured per vertex; a pair summing to one with a negative half is a
                    negative weight and not an unnormalized one; a weight too small to move
                    anything is prunable rather than wrong, and is still an influence. A
                    vertex with every weight at zero stays where it was bound while the rest
                    moves, and a joint the skin lists that nothing hangs from is named -->
  - [x] I4.10.e Exercise calibrated poses/clips and detect collapses, excessive
        stretch, volume loss, penetrations, detached vertices, and joint
        discontinuities. Evidence: deformation fixtures.
        <!-- evidence: rig-deform.ts — A POSE IS A VERSIONED INPUT, NOT A SCREENSHOT: linear
                    blend skinning is actually run at named, versioned poses, and at the pose
                    it was bound in nothing moves at all. The candy wrapper is caught for what
                    it is — half a joint and half of the same joint turned round is a matrix
                    that keeps one axis and flattens the other two, and the middle ring lands
                    exactly on the bone with no area left. A CREASE IS A PROPERTY OF THE
                    WEIGHTS, NOT OF THE POSE: the same bend folds flat under binary weights
                    and not at all under shared ones, while the volume lost is identical in
                    both, so creasing is the reading that tells them apart. A VERTEX THAT
                    LEAVES ITS NEIGHBOURS BEHIND IS NOT DEFORMING — every one of its edges
                    stretches, which is what tells it from a joint boundary doing its job.
                    A BOUND IS TWO-SIDED (one pose stretches and only stretches, another
                    crushes and only crushes), and a pair scan too large to run is carried as
                    not measured rather than as nothing found -->
  - [x] I4.10.f Validate clip names, ranges, frame/time bases, keys,
        interpolation, loop boundaries, root motion, events, and out-of-range
        tracks. Evidence: animation-range fixtures.
        <!-- evidence: rig-clips.ts — A ROTATION ORDER IS PART OF THE ROTATION: the BVH
                    channel list is the order the three angles are composed in, checked
                    against a value worked out by hand, and the same numbers in a different
                    order are a different pose. A QUATERNION AND ITS NEGATIVE ARE THE SAME
                    ROTATION AND NOT THE SAME KEY, which is why every pose in such a file is
                    right and the motion between two of them goes the long way round. A
                    MISSING FRAME IS STILL ON THE GRID, so a gap that is a whole number of
                    frames wide is a dropped frame and one that lands between them is a
                    retimed key. A CLIP THAT ENDS WHERE IT STARTS STUTTERS: the join is
                    judged against the clip's own median step, so a bounce that closes on a
                    step is clean, one whose last frame repeats its first plays that frame
                    twice, and one that jumps nine steps pops — while A TRACK THAT NEVER
                    MOVES CANNOT STUTTER. Root motion is measured on the ground plane only,
                    so going up is not going anywhere -->
  - [x] I4.10.g Emit joint/vertex/clip raw measurements and regions separately
        from target verdicts. Evidence: rig gate contracts.
        <!-- evidence: rig-report.ts — A MEASUREMENT IS NOT A VERDICT: the raw record is per
                    joint, per vertex and per clip and carries no severity, remediation,
                    bound or outcome (gated on the schema's own interface bodies AND on the
                    SERIALIZED record), while the verdict carries the joint or the clip, the
                    measurement, the bound and the repair. Every one of the five families of
                    check is shown to reach the verdict from a single rig broken in all of
                    them, so no file could be quietly unwired. AN EVALUATOR WITHOUT ITS
                    SUBJECT IS NOT A PASS — a container that carries no skin, an open surface
                    that encloses nothing, a leaf that declares no end and an arm that sits
                    at neither pose are all carried as not measured — and a file that cannot
                    be read is refused with its reason rather than measured as an empty
                    skeleton, which would have no cycles, no weights and no clips, and would
                    pass -->

- [x] I4.11 Implement gaussian-splat geometry, PSNR/SSIM/LPIPS, memory, and
      renderer-compatibility checks. Evidence: benchmark scene fixtures.
      <!-- evidence: evidence/v1-workbenches/splat-checks/ — a COMPILING strict-tsc
                  reference (schema/math/parse/geometry/properties/render/metrics/resources/
                  compatibility/report + fixtures + a 180-assertion suite), gated three ways
                  by verify:inventory:v1-workbenches (generator --check, tsc --noEmit, and
                  the suite run under tsx). The corpus is a real binary PLY of the kind a
                  gaussian-splatting trainer writes, assembled header line by header line and
                  then row by row, and the splats in it are built so that what they are is
                  known before anything measures them: a DC coefficient chosen to stand for
                  exactly white, a stored scale of zero that is a splat one unit across, and
                  a stored opacity of zero that is a splat exactly half opaque. 61 of 61
                  mutations to the implementation are caught by the suite -->
  - [x] I4.11.a Parse supported splat/container versions with point/property/
        coefficient/size/time/memory limits and required-field validation.
        Evidence: hostile splat fixtures.
        <!-- evidence: splat-parse.ts — THE BYTES DECIDE: a PLY says so on its first line and
                    a delivery that calls it something else is read as what it is with the
                    disagreement carried. 11 refusals, each reached by its own hostile
                    fixture (truncated, unknown-format, unsupported-version, header-invalid,
                    unsupported-encoding, unsupported-property-type, required-property-missing,
                    sh-coefficient-count-invalid, row-count-mismatch, camera-invalid,
                    resource-limit-exceeded); byte, point, step and pixel limits all applied
                    before the array they would size is built. A STORED NUMBER IS NOT THE
                    QUANTITY IT STANDS FOR — the opacity goes through the sigmoid and the
                    scales through the exponential on the way out — A DEGREE IS A COUNT, NOT
                    A DECLARATION, and the rest coefficients are read channel by channel
                    rather than every third one, which is the classic way to get three
                    colours that are each a third of three others -->
  - [x] I4.11.b Measure point
        count/density/distribution/bounds/scale/orientation, invalid
        positions/covariances/scales/rotations/opacities, and outliers.
        Evidence: geometry fixtures.
        <!-- evidence: splat-geometry.ts — every reading is on the DECODED quantities, so a
                    file full of negative stored scales is a file full of small splats rather
                    than a broken one. A COVARIANCE IS A ROTATION AND THREE LENGTHS, so what
                    can go wrong is the lengths: the needle (longest axis over shortest), the
                    boulder, and the one that decoded to nothing at all. A cloud in a plane
                    has no volume to be dense in and says so; outliers are found against a
                    median absolute deviation, and A ROBUST SPREAD OF NOTHING IS NOT A SMALL
                    SPREAD — four splats the same distance out give the method no scale, and
                    that is reported rather than used to call the fifth one a floater -->
  - [x] I4.11.c Validate spherical-harmonic degree/coefficient count/range,
        color/opacity encoding, precision, and source-format semantics.
        Evidence: property fixtures.
        <!-- evidence: splat-properties.ts — A DC COEFFICIENT IS NOT A COLOUR: the zeroth
                    band is a constant, so the colour is C₀ times the coefficient plus a
                    half, which is why a file of zeros is mid grey and why the coefficient
                    that stands for white is 1.7724538509. A colour out of gamut by less than
                    one display level is in gamut, because the coefficients come back out of
                    four-byte floats. A coefficient below what the target's precision holds
                    becomes a zero on arrival, and a file whose every stored opacity sits
                    inside the unit interval has had the sigmoid applied twice -->
  - [x] I4.11.d Render the same versioned calibrated cameras/reference frames
        under pinned renderer/config/color settings. Evidence: benchmark render
        reproducibility tests.
        <!-- evidence: splat-render.ts — the splats really are projected the EWA way
                    (Σ' = J W Σ Wᵀ Jᵀ), sorted by depth and blended front to back with the
                    transmittance walked down, so WHAT IS IN FRONT IS WHAT YOU SEE: two
                    splats at one place on the film and different depths render as the near
                    one, and turning the pair round renders as the other. A PICTURE IS TAKEN
                    FROM SOMEWHERE — the camera set's version, the renderer's version and the
                    colour space travel with every frame, the same scene renders byte for
                    byte the same twice, and the colour space is part of the picture rather
                    than a note about it. A DEGREE ABOVE ZERO IS A COLOUR THAT DEPENDS ON
                    WHERE YOU STAND: one band renders the same from both cameras and two do
                    not -->
  - [x] I4.11.e Compute PSNR, SSIM, and LPIPS with explicit masks/color space/
        crop/resolution/aggregation and not-applicable state when references are
        absent. Evidence: metric fixtures.
        <!-- evidence: splat-metrics.ts — peak signal to noise and structural similarity are
                    arithmetic and are computed here, against values that are facts: no error
                    at all is infinity rather than a large number, one level out over a whole
                    picture is 48.1308 dB exactly, a picture is itself at exactly one, and
                    two structureless pictures a tenth of a level apart come out at
                    22006.5025/22106.5025, which is the luminance term with its constant
                    worked out by hand. A LEARNED METRIC WITHOUT ITS WEIGHTS IS NOT A METRIC:
                    LPIPS is taken only from the backend the target names, a report from
                    another backend is refused rather than used, and a target that gates on it
                    with nothing behind it is told that its gate is not a gate. A REFERENCE
                    THAT IS NOT THERE IS NOT A PASS -->
  - [x] I4.11.f Measure encoded size, decoded CPU/GPU memory, peak VRAM, upload/
        load time, render frame time, and target point budget. Evidence:
        resource benchmarks.
        <!-- evidence: splat-resources.ts — A BUDGET IS PAID IN BYTES SOMEBODY COUNTED: the
                    file is the file, the decoder holds the row it read once per splat, and
                    the card holds a position, an opacity, the six independent numbers of the
                    covariance and every coefficient of every channel — fifty-two bytes a
                    splat at degree zero and two hundred and thirty-two at degree three, half
                    that at half precision. A TIME IS A MEASUREMENT: a delivery that brought
                    no benchmark has no times, and they are left null rather than worked out
                    from a bandwidth somebody assumed; where a peak was really measured, the
                    bigger of it and the counted copy is what the budget has to cover -->
  - [x] I4.11.g Test required web/mobile/native/engine renderer feature/format/
        precision/SH/compression/device compatibility and degraded modes.
        Evidence: renderer compatibility matrix.
        <!-- evidence: splat-compatibility.ts — A RENDERER IS A LIST OF THINGS IT CANNOT DO:
                    the declared maximum degree, point count, precisions, compressions and
                    memory against what the file actually needs, per named renderer, with a
                    renderer the delivery names and the target does not know reported as such
                    rather than assumed to work. A FALLBACK NOBODY IMPLEMENTED IS NOT A
                    FALLBACK — a degraded mode counts only where the renderer declares it —
                    and the card's copy is then counted at the degree it would really carry,
                    which is what makes a scene fit on a card the untouched file would not -->
  - [x] I4.11.h Emit raw geometry/quality/resource/compatibility results with
        benchmark versions separately from verdicts. Evidence: splat gate
        contracts.
        <!-- evidence: splat-report.ts — A MEASUREMENT IS NOT A VERDICT: the raw record is
                    per splat, per camera and per renderer and carries no severity,
                    remediation, bound or outcome (gated on the schema's own interface bodies
                    AND on the SERIALIZED record), while the verdict carries the thing, the
                    measurement, the bound and the repair, and the camera set and benchmark
                    versions travel with the readings taken under them. Every one of the six
                    families of check is shown to reach the verdict from a single delivery
                    broken in all of them. AN EVALUATOR WITHOUT ITS SUBJECT IS NOT A PASS — a
                    cloud with no volume, a spread of nothing, a camera with no reference
                    frame, a learned metric with no backend and a delivery nobody benchmarked
                    are all carried as not measured — and a file that cannot be read is
                    refused with its reason rather than measured as an empty cloud, which
                    would have no outliers, no needles and no memory, and would pass -->
- [x] I4.12 Implement scene-package reference, dependency, unit, axis, and
      missing-asset checks. Evidence: broken package fixtures.
      <!-- evidence: evidence/v1-workbenches/package-checks/ — a COMPILING strict-tsc
                  reference (schema/digest/paths/parse/graph/security/dependencies/
                  conventions/manifest/report + fixtures + a 167-assertion suite), gated
                  three ways by verify:inventory:v1-workbenches (generator --check, tsc
                  --noEmit, and the suite run under tsx). The corpus is a real archive: a
                  local header in front of every member, a central directory at the end, an
                  end-of-directory record after that, every member stored plain and padded so
                  its data starts on a boundary a reader can map it at — and a real manifest
                  inside it naming the entry scene, the conventions and the references. Both
                  digests are the real ones, checked against their own published vectors
                  (SHA-256 of "abc" and the CRC-32 check value). 61 of 61 mutations to the
                  implementation are caught by the suite -->
  - [x] I4.12.a Parse package/manifest/scene formats with archive/path/symlink/
        entry/size/depth/reference/resource protections. Evidence: hostile
        package tests.
        <!-- evidence: package-parse.ts + package-paths.ts — A PATH IN AN ARCHIVE IS A CLAIM,
                    NOT A LOCATION: every name is normalized before it is judged, both
                    separators are separators, and an entry that walks out of the package,
                    names a drive, names a server, or so much as mentions a parent (even one
                    that resolves back inside) is refused rather than followed. THE DIRECTORY
                    IS THE TRUTH AND THE HEADERS ARE A COPY, so the name, the method and the
                    checksum are compared between the two and a pair that disagree is refused
                    rather than one of them being picked — which is the whole of the parser-
                    differential class of attack. A DECLARED SIZE IS NOT A SIZE: the budget is
                    spent against the total the headers CLAIM before anything is unpacked. 14
                    refusals, each reached by its own hostile fixture, plus the alignment rule
                    a package meant to be mapped where it lies has to keep -->
  - [x] I4.12.b Build a canonical dependency graph for scenes, subscenes,
        variants, geometry, materials, textures, rigs, animation, audio,
        scripts, plugins, and external refs. Evidence: graph fixtures.
        <!-- evidence: package-graph.ts — twelve kinds of node, edges taken from the
                    references as each file writes them, and then a breadth-first WALK from
                    the entry scene so that every node carries the shortest way to reach it
                    (the texture is two steps out, by way of the material). A file nothing in
                    the manifest so much as mentions is an ORPHAN; a member the manifest
                    declares and the entry scene cannot get to is UNREACHABLE; they are
                    different findings about different files and the suite shows both -->
  - [x] I4.12.c Resolve dependencies only within authorized package/registry
        scope, pin versions/hashes, and reject absolute/traversal/mutable remote
        references. Evidence: dependency security tests.
        <!-- evidence: package-security.ts — nothing here resolves anything on a disk; every
                    reading is on the claim. A HASH PINS THE BYTES AND A VERSION PINS THE
                    INTENT, which are two different promises: a remote reference carrying a
                    version and no digest points at whatever is there tomorrow, and is
                    refused as a reference. Absolute names, names that count their way out of
                    the package, protocols with no registry named, and registries the target
                    does not resolve from are each their own finding — while a reference that
                    counts a parent and lands back INSIDE the package is what every scene
                    format writes, and stays legal -->
  - [x] I4.12.d Detect missing, unreadable, corrupt, hash-mismatched, duplicate,
        conflicting, cyclic, unsupported, or unused dependencies with paths.
        Evidence: broken dependency fixtures.
        <!-- evidence: package-dependencies.ts — AN UNUSED DEPENDENCY IS NOT A MISSING ONE:
                    one is a file nobody asked for and the other is a request nobody shipped
                    a file for, they are found by walking the same graph in opposite
                    directions, and the suite shows each one NOT being reported as the other.
                    A CYCLE IS NOT A DEEP GRAPH — the cycle finder keeps the path the walk
                    took, and is checked on its own against a shape whose cycle is known. The
                    archive's own checksums catch bytes that changed after they were written;
                    the delivery's SHA-256 pins catch bytes that were swapped for other
                    perfectly valid ones -->
  - [x] I4.12.e Reconcile file/object/scene units, axes, handedness, up/forward,
        transforms, frame/sample rates, and color spaces. Evidence: convention
        mismatch fixtures.
        <!-- evidence: package-conventions.ts — AN AXIS IS DECLARED, NOT DISCOVERED, and
                    there are three places a declaration can come from: the member, the
                    package, or the target. A member that declares nothing INHERITS the
                    package's, and the inheritance is RECORDED — because a model authored the
                    other way up that says nothing looks exactly like one authored correctly
                    that says nothing, and the only difference is that somebody has to go and
                    look. Seven conventions per member, each resolved with its source; a rate
                    agrees within a tolerance rather than by being equal to a number -->
  - [x] I4.12.f Validate package manifest completeness, entry scene, target
        versions, licenses/attribution, provenance, and deterministic inventory.
        Evidence: manifest tests.
        <!-- evidence: package-manifest.ts — A LICENCE IS A FACT ABOUT A FILE, NOT ABOUT A
                    PACKAGE: a package-level licence covers what its authors made, and a
                    member that carries none is reported even inside a package that carries
                    one. The inventory digest is SHA-256 over path, size and checksum, sorted
                    — so the same members in a different order are the same package, and the
                    same names over different bytes are not, both of which the suite shows -->
  - [x] I4.12.g Emit per-dependency/convention raw state, impact graph, verdict,
        and remediation. Evidence: package gate contracts.
        <!-- evidence: package-report.ts — A MEASUREMENT IS NOT A VERDICT: the raw record is
                    per entry, per reference and per convention and carries no severity,
                    remediation or outcome (gated on the schema's own interface bodies AND on
                    the SERIALIZED record), while the verdict carries the entry, the
                    reference, the measurement and the repair. Every one of the five families
                    of check is shown to reach the verdict from a single package broken in
                    all of them. AN EVALUATOR WITHOUT ITS SUBJECT IS NOT A PASS — a delivery
                    that pins nothing, a reference out to a registry, and a member that
                    declares no conventions of its own are all carried as not measured — and
                    a package that cannot be read is refused with its reason rather than
                    measured as an empty one, which would have no cycles, no orphans and no
                    missing dependencies, and would pass -->
- [x] I4.13 Implement delivery-target profiles for web, mobile, broadcast, game
      engine, DCC, and marketplace. Evidence: one asset can pass one target and
      fail another for known reasons.
      <!-- evidence: evidence/v1-workbenches/target-profiles/ — a COMPILING strict-tsc
                  reference (units/schema/colour/budget/timecode/frames/catalogue/registry/
                  evaluate + six authored profile families + fixtures + a 367-assertion
                  suite), gated three ways by verify:inventory:v1-workbenches (generator
                  --check, tsc --noEmit, and the suite run under tsx). A TARGET DOES NOT
                  MEASURE, IT DECIDES: nothing here reads an asset — the numbers came from
                  the I4.5-I4.12 gates — so one record of measurements goes to every target
                  unchanged and each reaches its own verdict over it. The hero scene package
                  PASSES the engine and the marketplace and FAILS the web (180 MB before
                  first paint), the handset (BC7, 4096, 85 MiB, 240k triangles and six
                  weights a vertex) and the Maya pipeline (a Blender scene), and the European
                  television master PASSES EBU R128 and FAILS ATSC A/85 on four counts — one
                  of them the same true peak, −1.4 dBTP, which is inside one ceiling and two
                  tenths of a decibel over the other. 104 of 104 mutations to the
                  implementation are caught by the suite -->
  - [x] I4.13.a Define immutable target-profile identity/version/lifecycle,
        supported asset kinds, formats, constraints, mandatory/advisory gates,
        packaging, metadata, and policy references. Evidence: profile schema.
        <!-- evidence: profile-schema.ts + profile-catalogue.ts + profile-registry.ts +
                    profile-units.ts — identity is a target and an exact three-part version;
                    lifecycle is draft/active/deprecated/retired and all four are published so
                    each refusal is reachable. AN EDITED PROFILE IS A NEW PROFILE: the digest
                    is SHA-256 over the canonicalized content and an entry that no longer
                    hashes to the one it carries is refused rather than used. A constraint
                    names a measurement in the 52-entry catalogue of what the earlier gates
                    produce, one of 16 rule kinds, an obligation (mandatory by default, so
                    nothing becomes advisory by being written carelessly) and a policy
                    reference; publishing refuses 13 ways, including a rule asked of the wrong
                    kind of measurement and A LIMIT IS A UNIT — a ceiling written in metres
                    for a triangle count, or texture memory held to a number of pixels -->
  - [x] I4.13.b Author web profiles for browser/runtime/codec/format/color/
        dimensions/performance/download/accessibility/security constraints.
        Evidence: web profile fixtures.
        <!-- evidence: profiles-web.ts (16 constraints) + profile-colour.ts — codecs every
                    evergreen browser decodes (AVC/VP9/AV1, and not HEVC), a 12 MB first-view
                    budget, the 8192 upload limit, WebGL 2 or WebGPU, a text alternative, a
                    caption track WHEN there is speech to caption, no embedded scripts and no
                    external references. A COLOUR IS NOT A NAME: gamut containment is built
                    from the primaries and the white point through XYZ, and the construction
                    predicts the luminance weights the standards publish (0.2126/0.7152/0.0722
                    for Rec.709, 0.2627/0.6780/0.0593 for Rec.2020) and the published
                    BT.2020-to-BT.709 matrix. A BOUND IS TWO-SIDED, so clipping to white and
                    clipping to black are measured separately — which is what shows when two
                    spaces disagree about white itself -->
  - [x] I4.13.c Author mobile profiles for OS/device/GPU/memory/package/codec/
        texture/audio/network/store and offline constraints. Evidence: mobile
        profile fixtures.
        <!-- evidence: profiles-mobile.ts (11 constraints) + profile-budget.ts — an ASTC/ETC2
                    sampler that has never heard of BC7, 2048 maps, a 64 MiB texture budget,
                    four weights a vertex, stereo, a 200 MB cellular cap, an offline manifest,
                    a store age rating, and a minimum OS read as NUMBERS so 17.10 is after
                    17.9. A DEVICE BUDGET IS SPENT BY THE WHOLE ASSET, NOT BY ITS BIGGEST
                    PART: memory is summed over whole blocks and over the mip chain, which
                    costs MORE than the textbook four thirds (699,064 bytes for a 1024 BC1
                    map, not 699,050.67) — and the desktop set is over the budget while no
                    single map in it is -->
  - [x] I4.13.d Author broadcast profiles for standard/raster/frame/interlace/
        color/HDR/audio/loudness/caption/timecode/slate/container constraints.
        Evidence: broadcast fixtures.
        <!-- evidence: profiles-broadcast.ts (two profiles, 12 constraints each) +
                    profile-timecode.ts — EBU R128 at 25 interlaced with EBU-TT-D captions,
                    −23 ±0.5 LUFS and −1 dBTP; ATSC A/85 at 30000/1001 with CEA-708, −24 ±2
                    LKFS and −2 dBTP. A FRAME RATE IS A RATIONAL NUMBER: 30000/1001 is not
                    29.97 and the comparison is a cross-multiplication, drop-frame exists
                    because of the difference, and the arithmetic is proven against the
                    standard's own numbers — 17,982 frames in ten minutes, 107,892 in an hour,
                    and every one of 28,572 labels counting back to the frame it labels. HDR
                    delivered to an SDR service is three findings, not one: the transfer goes
                    to the encoder and the peak (983 nits, read out of a PQ code value through
                    ST 2084) goes to the grade -->
  - [x] I4.13.e Author game-engine profiles for engine/version/platform/mesh/
        texture/material/rig/animation/LOD/collision/performance/import rules.
        Evidence: engine fixtures.
        <!-- evidence: profiles-engine.ts (12 constraints) + profile-frames.ts — 500k
                    triangles, three levels of detail each keeping at most three fifths of
                    the one above, collision, BC formats, powers of two, 256 joints, eight
                    weights, and clips at 30 or 60. A HANDEDNESS CONVERSION IS A MIRROR:
                    the change of basis between two declared frames is computed, and getting
                    from a right-handed authoring application to this left-handed engine has
                    determinant −1 — which a crate survives (advisory, and it is still a
                    hundred times too small) and a skeleton does not (refused), so the same
                    conversion is two constraints with two obligations -->
  - [x] I4.13.f Author DCC profiles for application/version/plugin/scene/unit/
        axis/color/reference/cache/round-trip constraints. Evidence: DCC
        fixtures.
        <!-- evidence: profiles-dcc.ts (8 constraints) — which application wrote the scene,
                    which version of it (asked ONLY when the application is the one this
                    pipeline opens, so a Blender scene fails once rather than twice), which
                    plugins it needs to resolve its nodes, which colour configuration and
                    cache formats it reads, whether its references are pinned, and the axis
                    and unit conversion into Y-up centimetres. The round trip is proven rather
                    than guarded: both bases are orthonormal by construction, so the suite
                    goes there and back over five frame pairs and asserts the identity and
                    that the units come home -->
  - [x] I4.13.g Author marketplace profiles for category/format/package/
        technical/rights/metadata/preview/moderation/submission constraints.
        Evidence: marketplace fixtures.
        <!-- evidence: profiles-marketplace.ts (13 constraints) — category, a single zip with
                    a manifest, a 2 GB intake, a triangle advisory, licences the store can
                    sell, a description long enough to be one, three previews at 1200 on the
                    short side, no open moderation reports and no third-party marks. The
                    attribution constraint is the conditional one: the LICENCE decides whether
                    the credit is even a question, so the same missing credit is a failure
                    under CC BY and NOT APPLICABLE under CC0, with the reason naming the
                    licence that waived it -->
  - [x] I4.13.h Resolve profiles by exact target/version and prove the same
        measurements yield independently explainable pass/fail/N/A verdicts
        across targets. Evidence: cross-target golden suite.
        <!-- evidence: profile-registry.ts + profile-evaluate.ts — A PROFILE IS A VERSION,
                    NOT A NAME: "latest", "3" and "3.1" are refused rather than rounded to
                    whatever is newest, a retired profile refuses and names its replacement,
                    a draft one refuses because nothing was published, and a deprecated one
                    still decides (the deliveries planned against it were correct when they
                    were made) with its lifecycle on the verdict. The cross-target suite runs
                    one record past five targets and the promo past both broadcasters: every
                    result carries its own measured value, its own limit rendered in the unit
                    that target wrote it in, and its own reason. TWO TARGETS THAT DISAGREE
                    MUST DIFFER IN WHAT THEY ASK is CHECKED rather than assumed — two
                    profiles that asked the same thing under the same obligation and answered
                    differently would be a bug in the evaluator, the detector for it is proven
                    on a positive control, and an outcome nobody reached is not an opinion -->
        <!-- partial: the golden verdicts live in the suite rather than in a committed
                    snapshot file; there is no runtime here to record one from -->
  <!-- partial: I4.13 is the profile LAYER — the profiles are authored against the
              measurements the earlier gate references produce, not against a running
              pipeline; wiring them to live Isis deliveries needs the runtime -->
- [x] I4.14 Persist raw measurements separately from policy verdicts. Evidence:
      threshold changes can recompute verdicts without reprocessing media.
      <!-- evidence: evidence/v1-workbenches/measurement-records/ — a COMPILING strict-tsc
                  reference (schema/identity/store/privacy/evaluate/recompute + fixtures + a
                  154-assertion suite), gated three ways by verify:inventory:v1-workbenches.
                  The parent's evidence is asserted directly: a threshold moves, the verdict
                  is decided again, and every reading in the store is byte-identical
                  afterwards. 70 of 70 mutations to the implementation are caught -->
  - [x] I4.14.a Define immutable measurement records with subject revision,
        gate/evaluator/code/model/config/calibration versions, inputs, units,
        values, regions, uncertainty, applicability, and integrity hash.
        Evidence: measurement schemas.
        <!-- evidence: record-schema.ts + record-identity.ts + record-store.ts — AN IMMUTABLE
                    RECORD IS ONE WHOSE IDENTITY IS ITS CONTENT: the id is a real SHA-256 over
                    the canonical form under the name of its kind, nothing allocates an
                    identifier, and there is no operation anywhere that edits one. AN INTEGRITY
                    HASH THAT COVERS ONLY THE VALUE IS NOT AN INTEGRITY HASH, proven one field
                    at a time — fifteen fields, fifteen distinct identities. A record that says
                    two things (applied with no number, absent with one, absent with no reason)
                    is refused rather than sealed, and the append-only store refuses a record
                    that does not hash to what it says -->
  - [x] I4.14.b Store measurement artifacts and sensitive derived data with
        classification, authorization, encryption, retention, and deletion
        rules. Evidence: measurement privacy tests.
        <!-- evidence: record-privacy.ts — SENSITIVE DERIVED DATA IS NOT THE MEASUREMENT: a
                    loudness reading is a number and the decoded programme is a recording of
                    somebody; a skin-tone reading is a number and the crop is a photograph of a
                    face. Four classifications, four purposes, a key per artifact, a retention
                    window in days, and six typed denials that tell "you may not" from "not any
                    more" from "it is gone" — deletion outranking expiry because it is the
                    reason. A purpose has to be one the ARTIFACT allows AND one the PRINCIPAL
                    holds, which are two different refusals. A DELETED ARTIFACT IS NOT A MISSING
                    MEASUREMENT: the number survives its picture, and only re-processing becomes
                    impossible -->
  - [x] I4.14.c Define verdict records that reference measurements plus exact
        gate-registry/target/policy/threshold versions and never copy ambiguous
        untyped values. Evidence: verdict schemas.
        <!-- evidence: record-schema.ts + record-evaluate.ts — A VERDICT CITES VERSIONS, NOT
                    VALUES: it holds the identities of the measurements it read and the exact
                    gate-registry, target-profile (name, version and digest), policy and
                    threshold versions, and there is no field anywhere in it that could hold a
                    number taken out of a measurement — which the suite checks on the serialized
                    record by looking for the readings' own values in it -->
  - [x] I4.14.d Evaluate pass/fail/advisory/manual-review/N/A/unavailable/error
        deterministically from immutable measurements. Evidence: verdict
        evaluation fixtures.
        <!-- evidence: record-evaluate.ts — seven outcomes, seven causes, one fixture each, and
                    a precedence in which a verdict is the worst thing anybody said. THE
                    UNCERTAINTY BAND CUTS BOTH WAYS: a reading whose own error bar straddles the
                    limit cannot say which side it is on, so a pass inside the noise is as much
                    a manual review as a failure inside it — a gate that only reviews the
                    failures lets through everything that landed on the friendly side of noise.
                    The edge of the band belongs to the review, because a comparison at exactly
                    the error bar is decided by the last decimal place -->
  - [x] I4.14.e Recompute new verdict identities after threshold/policy changes
        without mutating measurements or prior verdicts. Evidence: recompute
        history tests.
        <!-- evidence: record-recompute.ts + record-store.ts — A RECOMPUTE IS A NEW VERDICT,
                    NOT A CHANGED ONE: the new one names the one it replaces, the predecessor
                    stays byte-identical, two verdicts cannot replace the same predecessor
                    (which would fork the history), and what a subject IS now is the one nothing
                    superseded rather than the one stored last — asserted against a store handed
                    the same two verdicts the other way round -->
  - [x] I4.14.f Require media reprocessing when evaluator inputs/code/model/
        calibration change and explain why verdict-only recomputation is
        insufficient. Evidence: invalid recompute tests.
        <!-- evidence: record-recompute.ts — AND AN EVALUATOR IS NOT A THRESHOLD: each of code,
                    model, config and calibration moving is detected on its own, names the
                    readings it invalidates, refuses the arithmetic, and explains that deciding
                    again from them would be an opinion about a measurement nobody took. When
                    the media those readings were taken from has been deleted or has aged out of
                    its retention window the plan is neither recomputed nor failed but
                    reprocess-impossible: somebody has to supply it again -->
        <!-- partial: the plan is over a record store rather than a running pipeline; the
                    reprocessing it demands is somebody else's job to actually run -->
- [x] I4.15 Version evaluator code, model, calibration corpus, and thresholds.
      Evidence: every gate result is reproducible or labeled non-reproducible.
      <!-- evidence: evidence/v1-workbenches/evaluator-versioning/ — a COMPILING strict-tsc
                  reference (schema/pins + four registers + binding + lifecycle + reproducibility
                  + fixtures + a 226-assertion suite), gated three ways by
                  verify:inventory:v1-workbenches. The parent's claim is computed rather than
                  asserted: certifyGateResults returns everyResultAccountedFor over the reports,
                  and the only verdict that could falsify it — a nonreproducible result naming no
                  source — cannot be produced, because reproduce() throws instead. 38 of 38
                  mutations to the implementation are caught -->
  - [x] I4.15.a Register evaluator identity/version, source/artifact hash,
        build/runtime/container, dependencies, configuration, supported inputs,
        and lifecycle. Evidence: evaluator registry schema.
        <!-- evidence: version-pins.ts + evaluator-registry.ts — A NAME THAT DOES NOT RESOLVE IS
                    NOT A VERSION: source and artifact digests, compiler, flags, an image pinned
                    by @sha256: rather than a tag, the dependency closure, a digest per
                    configuration and the media kinds it accepts, all folded into one admission
                    digest. A FLOATING REF IS A NAME, NOT A VERSION — latest/main/HEAD, a semver
                    range, a wildcard segment and a bare tag are refused at REGISTRATION, not
                    discovered at replay. A version means one thing for ever, so registering a
                    second, different thing under it is refused while re-registering the same
                    thing is idempotent; and standing is kept out of the digest, so retiring a
                    build leaves what it computes untouched while a compiler bump does not.
                    Declarations must describe the thing: a CPU evaluator may not claim a GPU's
                    excuses, a GPU one must bind the kernel switch it admits to needing -->
  - [x] I4.15.b Register model/checkpoint/preprocessor/tokenizer version/hash/
        source/license/security/evaluation and runtime compatibility. Evidence:
        model dependency tests.
        <!-- evidence: model-registry.ts — THE PREPROCESSOR IS PART OF THE MODEL: a checkpoint is
                    a function from tensors to tensors and the thing that makes those tensors
                    decides which frame it saw, so a score taken under one preprocessor and
                    pinned to another is refused rather than footnoted — that number is about a
                    different function. Six ways a runtime can be wrong come back together
                    instead of one at a time. A LICENCE IS A RUNTIME FACT: weights granted for
                    evaluation may not run in a commercial gate. A PROVIDER ATTESTATION IS A
                    PROMISE, NOT A PIN — a hosted model pins to `provider:<build>`, may not also
                    claim a checkpoint digest, and must be reachable through a hosted runtime -->
  - [x] I4.15.c Register calibration corpus version/manifest/hashes/license/
        consent/coverage/labels/splits and controlled-access location. Evidence:
        corpus registry tests.
        <!-- evidence: corpus-registry.ts — A CORPUS IS PEOPLE. The manifest digest is an
                    order-independent fold over item digests, so re-ordering the file does not
                    restate the corpus and relabelling one photograph does. A HOLDOUT THAT SHARES
                    AN ITEM WITH THE CALIBRATION SPLIT IS NOT A HOLDOUT, refused by item and
                    naming both splits. COVERAGE IS DECLARED, NOT INFERRED: strata are claimed,
                    counted in the calibration split against a floor, and the gap is reported
                    where the corpus is actually thin — the two darkest Monk tones, one of which
                    is not in the calibration split at all. Consent carries a purpose, an expiry
                    and a count of who actually gave it; a location anybody may read is a
                    redistribution whatever the ticket calls it; and no fabricated statistics —
                    an inter-labeller agreement one person reached alone is refused -->
  - [x] I4.15.d Register threshold/rubric/aggregation/uncertainty/manual-band/
        target-profile versions with approval and effective dates. Evidence:
        threshold schema tests.
        <!-- evidence: threshold-registry.ts — AN EFFECTIVE DATE IS NOT A REGISTRATION DATE. All
                    five — thresholds, rubric, aggregation, uncertainty policy, manual-review
                    band — are versioned together with the target profile, an approval naming who
                    and where it is written down, and a half-open window. A set may not take
                    effect before it was approved, its window may not be empty, and TWO SETS IN
                    EFFECT AT ONCE IS A REFUSAL rather than a conflict to resolve by sort order.
                    The registry answers a profile AND a moment: the suite registers a newer,
                    more-recently-approved set and shows the OLDER one deciding a run in June -->
  - [x] I4.15.e Bind all exact versions and deterministic runtime controls to
        each measurement; record hardware/provider/environment where relevant.
        Evidence: execution binding tests.
        <!-- evidence: execution-binding.ts — the four strings on an I4.14 measurement are parsed
                    into coordinates, resolved against all four registers, checked against what
                    the evaluator says it is made of, and folded into one binding digest. RECORDED
                    IS NOT BOUND: all eight environment fields are written down, only the ones the
                    evaluator declared itself sensitive to enter the digest — so a gated sum is
                    the same run on another chassis while a convolution is a different run after a
                    driver upgrade. BINDING MORE THAN MATTERS IS AS BAD AS BINDING LESS: exactly
                    the declared controls, one short and one over being the same refusal from
                    opposite sides. The reading's own identity is deliberately NOT in the digest,
                    so two subjects measured under one setup share a binding -->
  - [x] I4.15.f Reproduce golden results within declared tolerances or label the
        measurement nonreproducible with the specific uncontrolled source.
        Evidence: reproducibility suite.
        <!-- evidence: reproducibility.ts — A TOLERANCE WIDER THAN THE DECISION BAND PROVES
                    NOTHING, in two distinguishable ways: it reaches past the boundary nearest
                    THIS reading, or it is wider than the whole review band and so could pin no
                    reading's verdict. Either way the answer is INCONCLUSIVE — a third word, kept
                    out of the pass count on purpose — while a tolerance of exactly zero stays a
                    legitimate bit-exactness claim. A DIVERGENCE MUST NAME THE UNCONTROLLED
                    SOURCE: it is attributed to declared sources that individually bound it, and
                    one larger than any of them, or under a claim of none, is thrown as a false
                    determinism claim rather than filed as a caveat — which is why nonreproducible
                    has no path that does not name something. A comparison across two different
                    bindings is refused as not-a-replay rather than blamed on the code -->
  - [x] I4.15.g Prevent retired/quarantined/unlicensed/unadmitted dependencies
        from new runs while retaining historical resolvability. Evidence:
        lifecycle tests.
        <!-- evidence: lifecycle.ts — LIFECYCLE IS A GATE ON THE FUTURE, NOT AN ERASURE OF THE
                    PAST: draft, retired and quarantined stop the next run; `resolve` and
                    `admissible` are two functions rather than one with a flag, so what took a
                    reading in 2026 still answers in 2029, down to the compiler. Deprecation gates
                    nothing, because "there is a newer one" is a fact about the catalogue.
                    QUARANTINE IS A DOUBT ABOUT THE PAST; RETIREMENT IS NOT — quarantine alone
                    reaches backwards and marks the readings taken under it suspect, carrying the
                    finding rather than a flag, and without editing records whose identity is
                    their content. An unlicensed model and an out-of-consent corpus are
                    inadmissible for a different reason than a withdrawn one, and say which -->
        <!-- partial: the register is a store rather than a running admission controller; nothing
                    here stops a process that declines to ask it -->
- [x] I4.16 Add uncertainty and manual-review bands. Evidence: borderline cases
      never become false automatic passes.
      <!-- evidence: evidence/v1-workbenches/uncertainty-bands/ — a COMPILING strict-tsc
                  reference (schema + representations + propagation + bands + routing +
                  aggregation + presentation + fixtures + a 209-assertion suite), gated three
                  ways by verify:inventory:v1-workbenches. The parent's evidence is the one
                  mechanism by which a borderline reading becomes a confident one: there is no
                  path anywhere here from an absent error bar to a number, `requireHalfWidth`
                  throws instead, and the generator greps both arithmetic modules for a `?? 0`.
                  36 of 36 mutations to the implementation are caught -->
  - [x] I4.16.a Define permitted uncertainty representation per measurement:
        confidence interval, calibrated probability, variance, quality flag,
        unknown, or unavailable. Evidence: uncertainty schema tests.
        <!-- evidence: uncertainty-model.ts — UNKNOWN IS NOT ZERO. Six representations, three of
                    which have a width and three of which do not, and the only function that
                    returns one THROWS for those three rather than defaulting. AN UNCALIBRATED
                    SCORE IS NOT A PROBABILITY: a probability names its calibration method and
                    states that calibration's own error, or it belongs here as a quality flag. A
                    VARIANCE WITHOUT A DISTRIBUTION IS NOT AN INTERVAL: the coverage factor comes
                    from the published t table (12.706 at one degree of freedom, 2.228 at ten,
                    4.032 at five and 99%) and from a real inverse-normal beyond thirty, and the
                    suite asserts that a small sample WIDENS the bar by about a third rather than
                    narrowing it. A coverage the table does not carry is refused rather than
                    interpolated, because interpolating it restates the coverage -->
  - [x] I4.16.b Propagate input/reference/evaluator/aggregation uncertainty
        without treating missing or N/A values as zero confidence. Evidence:
        propagation fixtures.
        <!-- evidence: propagation.ts — ONE UNKNOWN POISONS THE COMBINATION, AND AN INAPPLICABLE
                    ONE DOES NOT, which are two different absences given opposite treatment. The
                    arithmetic is ordinary (quadrature at ρ=0, a plain sum at ρ=1, the general
                    form between, all three asserted to order correctly); what is not ordinary is
                    that a term with no width makes the WHOLE combination unknown and names the
                    term, because √(0.1²+0²) is indistinguishable from having combined
                    everything — the suite pins the exact number that must not come out. A term
                    that did not apply is dropped and listed, since there was no value either.
                    Bars in different units or at different coverages do not combine at all, and
                    two readings one of which has no bar neither agree nor conflict -->
  - [x] I4.16.c Define nonoverlapping automatic fail, manual-review, and
        automatic pass bands per gate/profile/version with boundary inclusivity.
        Evidence: boundary tests.
        <!-- evidence: bands.ts — THE BANDS MUST TILE THE LINE and EVERY BOUNDARY BELONGS TO
                    EXACTLY ONE SIDE. Four distinct refusals at registration — a gap, an overlap,
                    an edge both sides claim and an edge neither does — because they are four
                    different mistakes, and the last is a hole exactly one value wide at exactly
                    the value people tune to hit. A target window runs fail/review/pass/review/
                    fail under the same rules, so the loudness gate has two review strips. And A
                    READING THAT SPANS A BOUNDARY CANNOT SAY WHICH SIDE IT IS ON, symmetrically:
                    the bar reaching OUT of the pass band is as much a review as the one reaching
                    out of the fail band. `decisionGeometry` computes what I4.15.f consumes -->
  - [x] I4.16.d Route uncertainty/quality/conflict/missing-dependency conditions
        into manual review even when the point estimate crosses pass. Evidence:
        borderline fixtures.
        <!-- evidence: routing.ts — A CAUSE OUTRANKS A POINT ESTIMATE. Each of an unknown bar, a
                    poor quality flag, two evaluators separated by more than their combined
                    allowance, and a dependency that produced nothing is asserted on a reading
                    sitting ten times its own error bar inside the pass band — which is the case
                    a gate written around failures never checks and the case that lets everything
                    through. A CAUSE CANNOT RESCUE A FAILURE, AND IT CANNOT CONFIRM A PASS: the
                    asymmetry is deliberate, since doubt about a reading over the limit does not
                    make it safe and a queue of definite failures is a queue nobody reads. A GATE
                    THAT DID NOT APPLY HAS NO BAND, AND A GATE THAT DID NOT RUN IS NOT A PASS -->
  - [x] I4.16.e Prohibit aggregation from averaging a manual-review/unknown
        mandatory gate into automatic pass. Evidence: aggregate bypass tests.
        <!-- evidence: aggregation.ts — A MANDATORY GATE MAY NOT BE AVERAGED. The bypass is
                    always the same and always looks defensible: weight every gate, sum, compare.
                    So a rule that gives a mandatory gate a weight is refused at the RULE rather
                    than at the result, and there are two independent barriers — the advisory
                    mean also skips mandatory gates even when handed such a rule directly. The
                    advisory score is still computed and reported, and can only ever ask for a
                    person, never excuse one. A gate that did not apply is excluded rather than
                    scored 1.0, and a subject where no mandatory gate applied is not entitled to
                    a pass at all -->
  - [x] I4.16.f Present point value, units, uncertainty, band, threshold,
        applicability, evidence, and review reason. Evidence: UI/API tests.
        <!-- evidence: presentation.ts — PRECISION IS A CLAIM ABOUT UNCERTAINTY: the value is
                    rounded to the place its bar reaches by the Particle Data Group's rule, all
                    three branches asserted (0.4 keeps one figure, 0.12 keeps two, 0.0964 rounds
                    up to 0.10 and carries the value's decimal place with it). An unknown renders
                    as "unknown — not ±0" and the suite asserts the string never contains a
                    rendered zero bar; the row is fed the COMBINED uncertainty rather than the
                    reading's own, so a decision taken with no usable width cannot be shown
                    beside a tidy ±0.05. An absent reading renders no number at all. And A REVIEW
                    WITHOUT A REASON IS A QUEUE -->
        <!-- partial: the row is a view model rather than a rendered surface; no component here
                    consumes it yet -->
- [x] I4.17 Add batch gate execution with per-gate progress and cancellation.
      Evidence: partial results are preserved but cannot authorize release.
      <!-- evidence: evidence/v1-workbenches/batch-gates/ — a COMPILING strict-tsc reference
                  (schema + plan freeze + scheduler + progress + cancellation + aggregate +
                  fixtures + a 151-assertion suite), gated three ways by
                  verify:inventory:v1-workbenches. The parent is two claims pulling opposite
                  ways and both are asserted: a measurement completed a second before a
                  cancellation survives it by exact attempt identity, and neither a partial nor
                  a COMPLETE batch can authorise anything. 29 of 29 real mutations to the
                  implementation are caught -->
  - [x] I4.17.a Define evaluation batch, subject, gate item, dependency,
        attempt, progress, measurement, verdict, cancellation, and aggregate
        contracts. Evidence: batch gate schemas.
        <!-- evidence: batch-schema.ts — eight gate states of which five are terminal, four
                    attempt outcomes, six ordered progress stages, four resource classes, five
                    failure classes of which three are retryable, and typed records for the
                    frozen plan, the gate item (dependsOn/timeoutMs/retryBudget/attempts/
                    obligation), the attempt, the result, the reservation, the event and the
                    aggregate. Twenty-one refusals, every one thrown somewhere and reached by
                    the suite -->
  - [x] I4.17.b Resolve and freeze exact subjects/revisions/gate registry/
        evaluators/target profiles/policies before queueing. Evidence: batch
        planning tests.
        <!-- evidence: batch-plan.ts — A FROZEN PLAN IS THE ONLY THING THAT MAY BE QUEUED. A
                    four-hundred-gate batch takes an hour; resolving anything lazily means the
                    first gate and the last gate ran under different rules, invisibly, on the day
                    somebody lands a registry change at lunchtime. So subjects, revisions,
                    registry, evaluator builds, profile and policy are resolved once and hashed
                    (the same canonical digest as I4.14), and `queue` refuses a plan that no
                    longer hashes to what it froze rather than repairing it — repairing it would
                    mean choosing which half of the batch to believe. Dependency cycles are found
                    at plan time and reported as the cycle, because a deadlock in a scheduler is
                    indistinguishable from slowness -->
  - [x] I4.17.c Schedule bounded parallel gate execution by resource class with
        dependencies, fairness, retries, timeouts, and per-gate isolation.
        Evidence: scheduler tests.
        <!-- evidence: scheduler.ts — A SCHEDULER IS A PROMISE ABOUT RESOURCES, NOT ABOUT SPEED:
                    bounds are per class, so a GPU-saturated batch still progresses on its
                    network gates. FAIRNESS IS PER SUBJECT, NOT PER GATE — the fixture is built
                    so a readiness-ordered queue would hand both GPU slots to the one asset
                    holding two GPU gates, and the suite asserts they go to two DIFFERENT assets.
                    A DEPENDENCY IS A GATE THAT MUST HAVE FINISHED, AND A FAILED ONE IS NOT A
                    FINISHED ONE: the dependent is skipped with the reason, which the suite
                    checks is not a failure of its own. A RETRY IS A NEW ATTEMPT with its own
                    epoch and identity, only for transient/timeout/resource-exhausted, and the
                    exhausted-budget message is told apart from the never-retryable one -->
  - [x] I4.17.d Emit monotonic per-gate stage/percent/measurement-ready/verdict-
        ready progress with event id/cursor/deduplication. Evidence: progress
        stream tests.
        <!-- evidence: progress.ts — PROGRESS IS MONOTONIC, AND AN EVENT IS DELIVERED ONCE.
                    Percent may not decrease, stages may not regress in the declared order, a
                    readiness flag may not un-set, and all four are refused by name rather than
                    clamped — clamping hides the bug that produced the regression. An identifier
                    IS its cursor, so a reconnecting consumer asks for everything after the last
                    it saw; re-emitting the same stage returns the SAME event rather than a
                    second one, since a retrying emitter must not double a stream somebody is
                    deduplicating. measurement-ready and verdict-ready are separate flags because
                    I4.14 keeps the reading apart from the opinion about it -->
  - [x] I4.17.e Cancel queued/running gates with evaluator propagation, late-
        result fencing, reservation release, and explicit cancellation outcome.
        Evidence: cancellation race tests.
        <!-- evidence: cancellation.ts — cancellation is a race by construction: at the moment
                    somebody presses stop, evaluators are mid-decode and will produce good
                    numbers seconds later. Both naive answers are wrong, so ONE comparison
                    against the cancellation instant decides both — and a result from a
                    superseded epoch is fenced the same way. Every running gate's slot is
                    released as it is cancelled, a completed gate is NOT un-completed by the
                    batch stopping, and a cancellation with no reason is refused because it
                    leaves everybody guessing whether it was a person or a crash -->
  - [x] I4.17.f Persist valid completed measurements/verdicts from partial runs,
        failures, and cancellations with exact attempt identities. Evidence:
        partial preservation tests.
        <!-- evidence: cancellation.ts — what finished first is kept, by attempt identity and
                    epoch, so a measurement from attempt two is never confused with the one
                    attempt one failed to produce. A result is written ONCE: a second, different
                    measurement for one attempt is refused, which is what makes "preserved" mean
                    something — a store that can be overwritten cannot promise to have kept
                    anything. The suite cancels mid-flight and asserts the earlier measurement is
                    still there by name while the late one never entered the store -->
  - [x] I4.17.g Compute aggregate state with mandatory/advisory/N/A/unavailable/
        error/cancelled counts and never emit release authorization. Evidence:
        aggregate authority tests.
        <!-- evidence: aggregate.ts — eleven counts and a completeness word, and NO field that
                    could be read as an authorisation. That is checked three ways: the generator
                    greps the interface body for ten banned names, the suite greps the SERIALIZED
                    object for them, and `carriesNoAuthorization` is itself proven to catch each
                    one by being handed an object that has it — because the way this fails is
                    that somebody adds a convenient boolean next quarter. `decisionReadiness`
                    answers the narrower honest question (is there enough for the AUTHORITY to
                    decide) and `requireReleaseAuthorization` throws for anything that asks this
                    module for a yes or no, including over a batch where every gate completed -->
        <!-- partial: the scheduler is a pure step function over an in-memory state rather than a
                    running worker pool; nothing here executes an evaluator -->

- [ ] I4.18 Add accessible quality comparison views. Evidence: raw values,
      thresholds, units, status, and remediation are available without color or
      canvas.
      <!-- evidence: evidence/v1-workbenches/comparison-views/ — a COMPILING strict-tsc
                  reference (schema + status encoding + projection + summary/detail + regions +
                  comparison + remediation + fixtures + a 124-assertion suite), gated three ways
                  by verify:inventory:v1-workbenches. a-f are done; the PARENT stays unchecked
                  because its evidence is about what a reader can get from a rendered surface,
                  and I4.18.g is the item that would establish that -->
      <!-- partial: I4.18.g (screen reader, keyboard, zoom/reflow, contrast, reduced motion,
                  large-set performance) needs a real browser over a real page. A view model
                  cannot stand in for one, and the generator says so in `notClaimed` rather than
                  quietly claiming the parent -->
  - [x] I4.18.a Define a typed view model joining exact asset revision, target
        profile, gate registry, measurement, verdict, uncertainty, override, and
        evaluation run. Evidence: projection tests.
        <!-- evidence: projection.ts — A JOIN IS A CLAIM THAT TWO THINGS ARE ABOUT THE SAME
                    SUBJECT. Six things join into a row and each is a place to assemble one out
                    of parts that do not belong together — a verdict about another revision or
                    another asset, a verdict that never cited this reading, a run under another
                    profile or registry, an override attached to a different verdict. None of
                    those looks wrong on screen, so each is refused rather than rendered. AN
                    OVERRIDE IS AN OPINION WITH A NAME ON IT: it cannot edit an I4.14 record and
                    does not try, the row carries both, an accepted failure comes out as neither
                    that failure nor a pass, and an acceptance whose date has passed stops
                    applying -->
  - [x] I4.18.b Present summary counts/status with text/icon/pattern plus
        filter/ sort/search and explicit mandatory/advisory/N/A/unavailable
        distinction. Evidence: component/accessibility tests.
        <!-- evidence: status-encoding.ts + summary.ts — A STATUS THAT IS ONLY A COLOUR IS NOT A
                    STATUS, and the check made here is the stronger one: the usual defence is "we
                    also have an icon" and then five statuses share a circle, so EACH of the word,
                    the shape and the fill pattern must separate ALL seven on its own and any
                    collision in any channel is a refusal. No colour name appears in the module at
                    all — colour is a fourth channel a theme may add, and nothing here depends on
                    it. Counts are per status AND split by obligation, because "three failures"
                    is a different sentence depending on whether they were mandatory; and the two
                    absences I4.16 separated stay separate in every channel -->
  - [x] I4.18.c Present every raw value, unit, evaluator/version, threshold/
        band/operator, uncertainty, applicability, timestamp, and verdict
        without relying on color. Evidence: detail view tests.
        <!-- evidence: summary.ts — thirteen required labels, checked as a list, so that the
                    detail lines read aloud in order ARE the row: gate, obligation, status,
                    asset, metric, unit, uncertainty, applicability, evaluator build, threshold,
                    band, timestamps and verdict, plus the acceptance and the evidence when there
                    are any. A NUMBER WITHOUT ITS UNIT IS NOT A VALUE: localisation moves the
                    group separator and the decimal mark across three locales and never touches
                    the unit symbol, and a value rendered with no unit is refused -->
  - [x] I4.18.d Provide accessible timecoded/spatial/mesh/audio regions using
        synchronized text tables/lists whenever visual canvas is unavailable.
        Evidence: noncanvas navigation tests.
        <!-- evidence: regions.ts — A CANVAS IS AN ENHANCEMENT, NOT A CHANNEL. A flicker at
                    00:04:12:07, a 288x288 patch at 480,220, vertices 1204-1336, samples
                    88200-92000 of the left channel: all numbers, and the waveform and the mesh
                    viewer are ways of drawing them. Each of five kinds declares the coordinates
                    it needs AND the ones it may not carry (a mesh region with a timecode was
                    built from the wrong template), and `tableIsComplete` checks the table
                    determines the regions on its own — two regions rendering one row is a table
                    that has lost one -->
  - [x] I4.18.e Compare asset revisions/targets/evaluation versions without
        mixing measurements and explain added/removed/changed gates. Evidence:
        comparison fixtures.
        <!-- evidence: comparison.ts — A COMPARISON MUST NOT MIX MEASUREMENTS. Revision 6 read
                    -23.4 and revision 7 reads -23.1, and everybody goes looking at the mix —
                    unless the meter was upgraded between the runs, in which case the 0.3 is an
                    unknown mixture of the asset changing and the METER changing, which is the
                    entire question anybody was asking. So a delta is computed only under a
                    matching evaluator build, and a moved build comes back incomparable with NO
                    number rather than a number with a warning beside it, because a number with a
                    warning beside it is a number people quote. A gate the registry gained is
                    `added` rather than a regression, and every gate on either side appears
                    exactly once -->
  - [x] I4.18.f Present authorized remediation, responsible owner, rerun/review
        action, and immutable evidence links with keyboard-safe focus behavior.
        Evidence: remediation journey tests.
        <!-- evidence: remediation.ts — REMEDIATION IS AUTHORIZED OR IT IS A SUGGESTION. Hiding
                    the buttons a viewer may not press makes a tidy screen and makes the
                    interface lie; the reader then asks in chat what happened to it. So the
                    action is shown, marked unavailable, naming the capability it needs — which
                    turns "why can't I fix this" into "who has isis.gate.rerun" — and it stays in
                    the tab order. FOCUS ORDER IS PART OF THE CONTRACT: total, ordered,
                    duplicate-free, with a declared return target for everything that opens,
                    because focus that lands nowhere strands a keyboard user at the top of a
                    four-hundred-row table. The evidence links are the row's immutable ones -->
  - [ ] I4.18.g Verify screen reader, keyboard, zoom/reflow, contrast, reduced
        motion, localization/number units, and large gate-set performance.
        Evidence: automated accessibility/performance tests.

### I5 — Safety, rights, consent, and provenance

- [x] I5.1 Enforce model-card and model-version license completeness before
      dispatch. Evidence: missing or incompatible rights fail closed.
      <!-- evidence: evidence/v1-workbenches/model-licensing/ (compiling strict-TS reference,
                  271 assertions, 101/101 mutants caught) + scripts/v1-workbenches/
                  generate-model-licensing.mjs (coherence + drift gate, 11 tests). Every refused
                  path is held to the only claim that matters: the provider log is empty.
                  Not claimed: the wiring into apps/isis/generation-api -->
  - [x] I5.1.a Define required model-card/version license fields for source,
        license identity/text/version, owner, allowed/prohibited uses,
        commercial use, derivatives, attribution, territory, term, and evidence.
        Evidence: license schema tests.
        <!-- evidence: license-schema.ts — ABSENCE IS NOT A PERMISSION. ModelCard.licenses and
                    ModelVersion.licenses are `Json @default("[]")` columns, so today a model
                    nobody has done the paperwork for and a model whose terms forbid everything
                    are the same value; and the nearest typed policy in the tree
                    (ThreeDAssetResolvedLicensePolicy) spends its dimensions as
                    `allowsCommercialUse: boolean`, where a licence that never mentions commercial
                    use cannot be told from one that forbids it. Every permission here is
                    three-valued and silence is a value the record must state out loud. AN
                    UNRECOGNISED TERM IS NOT A PERMISSION: a clause reading "see appendix B" is
                    refused rather than rounded towards yes. Fourteen fields, each deleted in turn
                    by the suite; perpetual and not-revoked are positive statements with no null
                    to be mistaken for silence -->
  - [x] I5.1.b Validate exact model/version/adapters and all transitive licensed
        dependencies have complete verified nonexpired metadata. Evidence:
        completeness tests.
        <!-- evidence: completeness.ts — THE CLOSURE IS THE LICENCE. Nobody runs a checkpoint on
                    its own and the non-commercial clause is always in the adapter somebody pulled
                    off a hub, so the terms are resolved over the whole transitive closure and an
                    uncatalogued dependency is refused rather than skipped. A TERM HAS TWO ENDS:
                    not-yet-effective, expired and revoked are three refusals, and a licence that
                    is both revoked and expired is revoked, because expiry was in the text from the
                    beginning and revocation is the licensor changing their mind. Verification is
                    by an approved authority within a freshness bar rather than by anybody at any
                    time -->
  - [x] I5.1.c Evaluate license compatibility against actor, tenant, project,
        workflow, input/reference rights, intended use, target, territory, and
        distribution terms. Evidence: compatibility matrix.
        <!-- evidence: compatibility.ts — RESTRICTIONS MEET AND OBLIGATIONS UNION, in opposite
                    directions: one adapter forbidding commercial use makes the whole run
                    non-commercial and no permissive checkpoint buys it back, while an attribution
                    owed to one upstream is not discharged by another upstream not asking for one.
                    A CHOICE IS BETWEEN WHOLE LICENCES, NOT BETWEEN THEIR CLAUSES, so nobody takes
                    the commercial grant from one and the attribution waiver from the other. THE
                    REFERENCE IMAGE IS PART OF THE LICENCE — every model can be clean and the run
                    still refused. THE TARGET DECIDES WHETHER IT IS COMMERCIAL rather than the
                    operator's checkbox; THE LICENSEE IS THE TENANT rather than the operator; and a
                    licence is bound to where the work goes rather than where the operator sits.
                    Nine dimensions, and every refusal names the node that imposed it -->
  - [x] I5.1.d Revalidate licenses/revocations/effective dates immediately
        before dispatch and bind the decision/evidence versions to the run.
        Evidence: stale-license race tests.
        <!-- evidence: dispatch.ts — A DECISION IS AS OF AN INSTANT. The plan was cleared on
                    Tuesday, the licensor pulled the model on Wednesday, the queue drains on
                    Thursday, and every one of those is a normal day. So the plan decision is an
                    input and never an authority: the decision is taken again against a fresh read
                    at the dispatch instant, a read that saw different evidence supersedes rather
                    than agrees, and the run binds the decision, the policy version and the exact
                    evidence versions it was read against. A CLEARANCE CANNOT OUTLIVE THE LICENCE
                    IT RESTS ON -->
  - [x] I5.1.e Return missing/incompatible/expired/revoked/unknown as typed
        fail-closed outcomes with no provider call. Evidence: zero-dispatch
        negative tests.
        <!-- evidence: dispatch.ts + license-schema.ts — A GATE THAT RUNS AFTER THE CALL IS NOT A
                    GATE. There is one path to a provider, it takes a clearance no other module can
                    mint (a unique symbol), and six refused paths are swept with a counting
                    provider whose log must be empty. THE OUTCOME IS A ROUTE AND THE REFUSAL IS A
                    REASON: twenty refusals map onto five fail-closed outcomes by a table that is
                    total by construction and can never route to cleared, and a decision takes its
                    name from the worst thing found rather than the first thing checked -->
- [x] I5.2 Enforce training-data provenance requirements by workflow risk.
      Evidence: policy fixtures cover unknown, restricted, and approved data.
      <!-- evidence: evidence/v1-workbenches/training-provenance/ (compiling strict-TS reference,
                  236 assertions, 109/109 mutants caught) + scripts/v1-workbenches/
                  generate-training-provenance.mjs (coherence + drift gate, 12 tests). The
                  fixtures cover unknown, restricted and approved data, and four more besides.
                  Not claimed: the wiring into the live dispatch path, and the ratification of
                  the matrix by a named owner -->
  - [x] I5.2.a Define workflow-risk classes and the minimum dataset/source/
        collection/license/consent/opt-out/geography/lineage evidence required
        for each. Evidence: provenance policy schema.
        <!-- evidence: provenance-schema.ts — THE BAR MOVES WITH THE WORKFLOW. "Is the provenance
                    of this dataset good enough" is not a question about the dataset: the same
                    corpus is fine for an engineer trying something on a Tuesday and nowhere near
                    good enough for a campaign that puts a real face on a billboard, and a gate
                    with one fixed bar either stops the Tuesday or waves through the billboard.
                    Four rungs over the eight kinds of evidence the item lists, and THE LADDER
                    MUST BE A LADDER — the nesting is checked, because a higher rung that quietly
                    asks for less reads fine and inverts the policy -->
  - [x] I5.2.b Resolve exact model/version/adapter training-data declarations,
        dataset versions, upstream sources, known exclusions, and evidence
        freshness. Evidence: provenance resolver tests.
        <!-- evidence: resolver.ts — A DATASET IS ITS VERSION: the lookup is by identity AND
                    version, an unpinned "latest" is refused at the door, and an attestation
                    scoped to another version does not count towards this one — otherwise a corpus
                    cleaned last month certifies a model trained before the cleaning. AN OPT-OUT IS
                    A DATE, NOT A FLAG: a sweep that ran before the snapshot leaves everything that
                    arrived in between, while the declaration says "opt-out honoured" the whole
                    time. Expiry, revocation and age are three separate ways for a reading to stop
                    counting -->
  - [x] I5.2.c Classify each dependency as approved, restricted, unknown,
        incomplete, expired, revoked, or conflicting without treating absence as
        approval. Evidence: classification fixtures.
        <!-- evidence: classification.ts — ABSENCE IS NOT APPROVAL: there is no path from
                    nothing-on-file to approved. All seven classes, and TWO ANSWERS ARE WORSE THAN
                    NO ANSWER — two live attestations that disagree about the same version make a
                    dataset `conflicting`, which outranks the absence classes, because one of the
                    two signatures is wrong and nothing on the page says which. TAINT FLOWS
                    DOWNSTREAM AND NEVER WASHES OUT, and the ancestor that caused it is named -->
  - [x] I5.2.d Apply
        intended-use/audience/identity/style/child/protected-source/ commercial
        risk rules to the resolved provenance graph. Evidence: risk-policy
        matrix.
        <!-- evidence: risk-policy.ts — A RULE CAN ONLY RAISE THE CLASS. All seven rules are
                    escalations naming a rung the workflow must at least stand on, so there is no
                    evaluation order for somebody to refactor and the answer is the maximum however
                    they are shuffled. A RULE THAT DOES NOT NAME ITS SUBJECT IS A RUMOUR: every
                    graph-level escalation carries the dataset that triggered it, and the
                    request-level ones honestly carry none. THE MATRIX IS THE POLICY, AND IT MUST BE
                    MONOTONE — 28 declared cells, approved allowed at every rung, restricted and
                    revoked blocked at every rung, nothing softening as the rung rises -->
  - [x] I5.2.e Block or require named review according to the ratified risk
        class and persist the exact decision inputs. Evidence: enforcement
        tests.
        <!-- evidence: enforcement.ts — A REVIEW WITH NO NAME ON IT IS NOT A REVIEW: the roles that
                    may sign are named per rung, a review with an empty roster is refused, and
                    BLOCKED IS NOT A REVIEW WITH NOBODY FREE — a block names no reviewer at all
                    rather than one who cannot help. A DECISION YOU CANNOT REPRODUCE IS NOT A
                    DECISION: the interesting question a year later is never what it said but why,
                    when the registry has been rewritten twice since, so `replay` re-derives the
                    action from the record's own nine kept fields with no registry in sight, and a
                    record whose inputs no longer reach its action is refused -->
- [x] I5.3 Enforce reference-asset rights attestations. Evidence: expired or
      revoked rights invalidate downstream releases.
      <!-- evidence: evidence/v1-workbenches/reference-rights/ (compiling strict-TS reference,
                  170 assertions, 81/81 mutants caught) + scripts/v1-workbenches/
                  generate-reference-rights.mjs (coherence + drift gate, 13 tests). The digest is a
                  real SHA-256 held to the NIST vectors and the signatures are a real HMAC-SHA256
                  held to RFC 4231, so every tamper test breaks something rather than being told it
                  did; the asymmetric signature binds through a seam whose production
                  implementation is @oshun/content-signing. Not claimed: the wiring into the live
                  reference-intake path, and a named owner ratifying the recall policy -->
  - [x] I5.3.a Define attestation subject/version/hash, owner/licensor, license/
        permission basis, allowed uses, derivative/training rights, attribution,
        territory, term, restrictions, evidence, and signer authority. Evidence:
        attestation schema tests.
        <!-- evidence: attestation-schema.ts — AN ASSET IS ITS BYTES: the subject is an id, a
                    version AND a content digest, and the digest is the one that decides, because a
                    reference re-exported at another quality is a different asset wearing the same
                    filename. A RECORD CANNOT REPORT ITS OWN REVOCATION, so the signed body has no
                    revocation field at all — a licensor withdrawing permission is not going to come
                    back and re-sign the record to say so. A PERMISSION HAS A ROLE: the eight ways a
                    file is actually fed to a model are enumerated, because a grant reading "may be
                    used as a reference" cannot tell a style transfer from a face swap -->
  - [x] I5.3.b Verify attestation signature/authority, referenced asset hash,
        evidence integrity, effective dates, and current revocation state.
        Evidence: attestation validation tests.
        <!-- evidence: verification.ts — A SIGNATURE IS NOT AN AUTHORITY. Two checks that fail for
                    different reasons: the signature verifies against the signer's key, and the
                    SIGNER is checked against a registry of who may sign for that owner at that
                    time. An intern's key verifies exactly as well as the rights desk's, and
                    somebody who left in March cannot sign in April. The asset and evidence hashes
                    are recomputed from the bytes rather than compared between two declarations,
                    because two declarations agreeing with each other is not evidence about a
                    file -->
  - [x] I5.3.c Evaluate scope against the exact conditioning/reference role,
        workflow/model use, generated derivative, audience, project, target,
        territory, and publication channel. Evidence: rights matrix.
        <!-- evidence: scope.ts — A GRANT IS FOR A ROLE, NOT FOR A FILE, and every role a run puts
                    the asset in is checked, including the second one. A DERIVATIVE IS NOT A USE:
                    feeding a reference to a model and publishing what came out are two
                    permissions, which is exactly what a reference-only licence says. A RESTRICTION
                    IS A CLAIM ABOUT THE OUTPUT, NOT ABOUT THE INPUT — no-face-swap is tripped by
                    what the run does, not by the file being opened -->
  - [x] I5.3.d Bind accepted attestation versions to plan/run/output lineage and
        propagate required attribution/restrictions. Evidence: lineage tests.
        <!-- evidence: lineage.ts — A LINEAGE IS A CLAIM ABOUT THE PAST, AND THE PAST DOES NOT
                    CHANGE: the binding records the attestation and its version, and rebinding a run
                    to a later version is refused rather than absorbed, because otherwise every
                    historical run appears to have been cleared under terms nobody had read at the
                    time. AN OBLIGATION TRAVELS FURTHER THAN THE ASSET: the reference is not in the
                    output and the credit it earned is, so attributions and restrictions accumulate
                    down the derivation graph and a credit owed twice is paid once -->
  - [x] I5.3.e On expiry/revocation/change, mark dependent release eligibility
        stale and trigger blast-radius/recall policy without rewriting prior
        evidence. Evidence: invalidation tests.
        <!-- evidence: invalidation.ts — REVOCATION REACHES BACKWARDS AND EXPIRY DOES NOT, which is
                    the distinction the whole item turns on: a term ending was always coming, so
                    nothing new goes out and what already shipped shipped under a live licence; a
                    revocation is the licensor saying the permission was never what we thought, and
                    it reaches the releases that already happened. STALE IS NOT INELIGIBLE, because
                    a workbench that renders one as the other teaches everybody to ignore red rows.
                    THE PAST IS APPENDED TO, NOT EDITED: each invalidation writes a new ledger entry
                    and the earlier ones are proven byte-identical by their own digests. The blast
                    radius is everything downstream; the recall is a policy question about the
                    channel each release went out through -->
- [x] I5.4 Enforce face-synthesis subject consent with scope, purpose,
      territory, expiry, and revocation. Evidence: consent matrix tests.
      <!-- evidence: evidence/v1-workbenches/face-consent/ (compiling strict-TS reference,
                  265 assertions, 74/74 mutants caught) + scripts/v1-workbenches/
                  generate-face-consent.mjs (coherence + drift gate, 15 tests). The consent matrix
                  is thirteen dimensions over five checkpoints, each enforced at the earliest
                  checkpoint at which it is answerable and re-checked at every later one. The
                  digest is imported from the I5.3 reference rather than written a second time.
                  Verification finding recorded against the shipped
                  libs/isis/face-synthesis/src/lib/ethical-safeguards.ts. Not claimed: the face
                  encoder and detector are real dependencies taken through seams rather than
                  implemented here, the wiring into the live Isis path, and the ages-of-majority
                  table being sourced from counsel -->
  - [x] I5.4.a Define verified subject identity, authorized representative,
        consent evidence/signature, synthesis operations, reference set,
        purpose/use, audience, project, territory, term, disclosure, retention,
        and revocation contract. Evidence: face-consent schema.
        <!-- evidence: consent-schema.ts — A FACE IS NOT A FILE: I5.3's subject is an asset
                    identified by its bytes and this one is a person, so the reference photographs
                    are evidence ABOUT her rather than the thing consented to. A CONSENT NAMES
                    OPERATIONS, NOT "AI": eight operations are enumerated, because a release saying
                    "may be used with generative tools" cannot tell a scratch removal from a
                    puppeteering. A REVOCATION CONTRACT IS PART OF THE CONSENT — channels, effect
                    latency and reach are required fields — and the signed body carries no
                    withdrawal state, because a subject changing her mind will not re-sign the
                    record to say so. 17 required fields, each deleted in turn by the suite -->
  - [x] I5.4.b Verify subject/representative authority and liveness/identity
        evidence in the approved host without exposing biometric artifacts.
        Evidence: verification tests.
        <!-- evidence: verification.ts + enclave.ts — A SIGNATURE FROM THE SUBJECT IS NOT PROOF THE
                    SUBJECT SIGNED IT (signature and identity assurance fail for different reasons;
                    a self-asserted identity never clears the floor for face synthesis). LIVENESS
                    IS A MOMENT, NOT A PROPERTY: a capture is bound to a challenge nonce and a
                    window, and one that PREDATES the challenge did not answer it however recent.
                    A TEMPLATE NEVER LEAVES THE HOST — the vectors live in a closure behind opaque
                    handles, no exported operation returns one, and the view is assembled from an
                    allow-list without the score, which is an oracle to hill-climb against -->
  - [x] I5.4.c Match detected/declared subject and requested face identity use
        to exact consent scope before planning, dispatch, comparison, export,
        and release. Evidence: consent matrix.
        <!-- evidence: matching.ts + biometrics.ts — A CHECK AT RELEASE IS TOO LATE FOR A FACE
                    ALREADY SENT: the matrix records the earliest checkpoint at which each of 13
                    dimensions is answerable and requires enforcement there, and the invariant takes
                    the table as an argument so a deferred row can be handed to it and refused.
                    Every gate goes through one door and every later gate asks again. A VERIFICATION
                    THRESHOLD IS NOT AN IDENTIFICATION THRESHOLD — a 1e-4 per-comparison rate over a
                    gallery of 10,000 false-matches 0.6321389535670295 of the time, pinned exactly;
                    A SCORE IS NOT AN IDENTITY, so the band is inconclusive rather than rounded.
                    Zero-dispatch proved by a counting provider on all 10 dispatch-time refusals -->
  - [x] I5.4.d Enforce stricter rules for multiple subjects, minors, public
        figures, sensitive contexts, cross-purpose reuse, and synthetic subject
        ambiguity. Evidence: high-risk fixtures.
        <!-- evidence: high-risk.ts + detection.ts — AGE IS AS OF THE USE, NOT AS OF THE SIGNATURE
                    (leap-day arithmetic exercised on both sides of 29 February) and MAJORITY IS A
                    JURISDICTION'S ANSWER, so an unlisted territory refuses rather than defaulting
                    to eighteen. GUARDIAN AUTHORITY ENDS AT MAJORITY: the day she turns eighteen the
                    consent on file is her guardian's and not hers. BEING PUBLIC IS NOT CONSENTING;
                    A SENSITIVE CONTEXT IS DECIDED BY THE OUTPUT rather than the requester's
                    checkbox; EVERY FACE IN THE FRAME IS A SUBJECT; and AN AMBIGUOUS SUBJECT IS NOT
                    A SYNTHETIC ONE — a generated face matching an enrolled person is that person -->
  - [x] I5.4.e Propagate expiry/revocation to future use and governed downstream
        availability while preserving immutable historical proof. Evidence:
        revocation tests.
        <!-- evidence: revocation.ts — A WITHDRAWAL IS EFFECTIVE WHEN IT IS RECEIVED, NOT WHEN IT IS
                    PROCESSED, or every hour of backlog buys an hour of use she did not agree to;
                    and A WITHDRAWAL THROUGH THE WRONG CHANNEL IS STILL A WITHDRAWAL. AN EXPIRY IS
                    NOT A WITHDRAWAL: a term ending stops the next release and leaves what shipped,
                    and she changing her mind reaches it, with the action chosen per audience.
                    DELETING THE FACE IS NOT DELETING THE RECORD THAT WE HAD IT — retention destroys
                    the templates and photographs and keeps the digests and decisions, because the
                    only thing that can answer "did you have permission in March" is a record made
                    in March; the ledger is digest-chained and refuses a rewritten history -->
- [x] I5.5 Enforce voice-cloning consent and speaker verification in the
      authorized host. Evidence: impersonation and scope-escalation tests.
      <!-- evidence: evidence/v1-workbenches/voice-consent/ (compiling strict-TS reference,
                  233 assertions, 96/96 mutants caught) + scripts/v1-workbenches/
                  generate-voice-consent.mjs (coherence + drift gate, 16 tests). The audio
                  arithmetic is real — durations and levels are measured off synthesised PCM and
                  held to published values — and the digest is imported from the I5.3 reference
                  rather than written a second time. Verification finding recorded against the
                  shipped libs/isis/audio-generation VoiceCloneRequest and the
                  libs/isis/music-generation keyword guardrail. Not claimed: the speaker-verification
                  system, the spoofing and replay countermeasures, the claim classifier and the voice
                  model are dependencies taken through seams rather than implemented here, and the
                  wiring into the live Isis voice path is separate -->
  - [x] I5.5.a Define speaker identity, consent grant, voice model/version,
        reference recordings/hashes, allowed text/use/style/language/audience/
        channel/territory/term, disclosure, retention, and revocation. Evidence:
        voice-consent schema.
        <!-- evidence: voice-consent.ts — A VOICE CONSENT IS ABOUT WORDS AS WELL AS SOUND, which is
                    the difference from I5.4's face consent and the half that gets left out: a
                    likeness in a shot looks like her, and a voice reading a script is heard as her
                    saying it. So the grant carries a script policy (prohibited claims, whether she
                    speaks in the first person, a length) alongside the uses. AN ENROLLMENT IS NOT A
                    FILE UPLOAD: every reference recording carries an ownership basis, and
                    third-party-unverified is in the vocabulary precisely so "we are not sure where
                    this came from" is expressible and refusable. 21 required fields, each deleted in
                    turn by the suite, which also asserts the message names the missing field -->
  - [x] I5.5.b Perform speaker identity/liveness/authority verification and
        reference quality/ownership checks only in the authorized host.
        Evidence: host-bound verification tests.
        <!-- evidence: audio.ts + speaker.ts + enrollment.ts — A DECLARED DURATION IS NOT A DURATION:
                    the same 2,646,000-byte buffer is thirty seconds of 44.1kHz mono and 9.1875
                    seconds of 48kHz stereo 24-bit, so an enrollment minimum is computed from the
                    bytes and the format. FULL SCALE IS NOT SYMMETRIC (the rail is 32767/32768, and a
                    detector testing for 1.0 finds nothing in a take that is clipped solid). A
                    SPEAKER VERIFIER CANNOT TELL YOU FROM YOUR OWN CLONE — the load-bearing fact: a
                    model built from her enrollment scores high against it by construction, so
                    verification establishes which voice and never whether a human, and A SPOOF GETS
                    IN AT THE COUNTERMEASURE RATE ALONE (0.05, not the 5e-8 product with a 1e-6
                    verifier). AN EMBEDDING NEVER LEAVES THE HOST -->
  - [x] I5.5.c Bind trained/enrolled voice artifacts to verified
        speaker/consent/ source versions and prohibit unauthorized export or
        model substitution. Evidence: voice artifact lineage tests.
        <!-- evidence: artifact.ts — A VOICE ARTIFACT IS ITS MODEL AND ITS VERSION, and the same name
                    and version over a different training corpus is the substitution a version number
                    cannot see. AN ARTIFACT IS BOUND TO THE CONSENT VERSION IT WAS CLEARED UNDER, so
                    rebinding to a later one is refused rather than absorbed. THE SOURCE IS PART OF
                    THE ARTIFACT: a model trained on a take the consent no longer covers is stale
                    rather than innocent, and that is a different refusal from a substitution because
                    the remedy is a retrain rather than a routing fix. AN EXPORT IS A COPY A
                    WITHDRAWAL CANNOT REACH — exactly one of four destinations keeps a withdrawal
                    enforceable and the other three need the consent to have licensed them -->
  - [x] I5.5.d Revalidate requested script, purpose, audience, language/style,
        channel, territory, term, and actor at preview, generation, and release.
        Evidence: scope matrix.
        <!-- evidence: scope.ts + script.ts — A PREVIEW IS A GENERATION THAT DOES NOT SHIP, so all
                    sixteen dimensions are checked at all three stages and the stage decides only
                    where the audio may go and whether the label must be on it yet. REVALIDATE MEANS
                    ASK AGAIN: the later stages trust the earlier clearance for nothing except proof
                    that it happened. A KEYWORD LIST IS A DETECTOR, NOT A GATE — whether a consent is
                    required follows which artifact is loaded, never the wording of the request — and
                    AN UNASSESSED SCRIPT IS NOT A CLEAN SCRIPT, so a line the classifier cannot read
                    refuses rather than coming back empty the way a substring scan does -->
  - [x] I5.5.e Detect and block direct impersonation, claimed-authority,
        third-party enrollment, replay, cross-project, expired/revoked, and
        scope- escalation attempts. Evidence: adversarial voice tests.
        <!-- evidence: adversarial.ts — a total table, seven attacks, seven distinct refusals. AN
                    IMPERSONATION IS DETECTED IN THE OUTPUT, NOT IN THE REQUEST: the rendered audio
                    is scored against the registry of people this workbench can recognise, so a job
                    that steers a stock voice until it sounds like somebody is refused having broken
                    no field on any form. A CLAIM OF AUTHORITY IS NOT AUTHORITY. A REPLAY IS NOT A
                    SPOOF AND NEITHER IS THE OTHER — a recording of her real voice is not synthetic,
                    so the synthesis detector has nothing to find and a separate replay countermeasure
                    is what closes it. AN ESCALATION IS NOT A SCOPE ERROR: one means she never agreed
                    and needs a new consent, the other means she did and the approved job was
                    smaller, and it needs a new preview -->
- [x] I5.6 Enforce child-safety restrictions and age-assurance boundaries.
      Evidence: policy and redaction tests.
      <!-- evidence: evidence/v1-workbenches/child-safety/ (twelve files, strict tsc + a 401-
                  assertion suite run under tsx, both gated in verify:inventory:v1-workbenches) and
                  evidence/v1-workbenches/child-safety.json, generated and drift-gated by
                  scripts/v1-workbenches/generate-child-safety.mjs. 44 refusals, 36 rules, 30
                  vocabularies, 8 known answers; 113/113 mutants killed with the control surviving.
                  The verification half is recorded against the shipped
                  libs/isis/ai-providers/.../civitai/content-safety-provider.ts and gated so it
                  cannot rot: (1) it conflates the viewer's age with the subject's under one word;
                  (2) its only minor-safety signal is a CONJUNCTION over English prompt substrings
                  (nsfwCheck.found && minorKeywords.some(...)), so dropping either word switches it
                  off and it looks at no reference and no output; (3) verifyAge returns the same
                  "verified" for a tick-box, a truthy documentId and a third party whose branch
                  reads the caller's own verified:true; (4) its age is Math.floor(elapsed/365.25),
                  which reads seventeen on the day somebody born 1 March 2000 turns eighteen — the
                  error runs one way, denying adults rather than admitting children, and the sign is
                  an accident of the constant rather than a decision; (5) it stamps an expiry on the
                  one claim that cannot expire and then never reads it, so AgeGateStatus carries an
                  'expired' member nothing can produce. NOT CLAIMED: the estimator, the issuers, the
                  classifier and the human review queue are seams, and wiring this gate into the
                  live Isis generation path is separate -->
  - [x] I5.6.a Define child-safety operation/content/identity/reference/storage/
        sharing/publication restrictions by actor, audience, host, jurisdiction,
        and uncertainty. Evidence: child-safety policy schema.
        <!-- evidence: child-safety.ts + jurisdiction.ts — seven restricted subjects, each with its
                    own refusal, conditioned on all five axes. AN AGE GATE IS ABOUT THE AUDIENCE AND
                    A CHILD-SAFETY RULE IS ABOUT THE SUBJECT: these are two people and two
                    questions, and the shipped Isis surface answers both under the word "age", so
                    every rule here carries the party whose age it turns on. UNCERTAINTY IS A
                    CONDITION, NOT A GAP IN THE TABLE — every row answers minor, adult and
                    nobody-knows, and the parse-time invariant is that the third is never more
                    permissive than the first. A TABLE WITH A HOLE IN IT IS A PERMISSION, so a
                    subject with no rule refuses as unmapped rather than falling through. AN AGE IS
                    A CALENDAR FACT, NOT A DIVISION, and THE APPLICABLE AGE DEPENDS ON THE QUESTION,
                    NOT ONLY ON THE PLACE: majority, digital consent and age-sensitive content are
                    three columns over six jurisdictions, WHEN MORE THAN ONE PLACE APPLIES THE
                    STRICTEST ONE DECIDES, and AN UNLISTED TERRITORY HAS NO ANSWER -->
  - [x] I5.6.b Treat declared/detected/uncertain minor status, age-sensitive
        content, and child identity references with fail-closed escalation
        rules. Evidence: applicability fixtures.
        <!-- evidence: status.ts + fixtures.ts — UNCERTAINTY IS A FIRST-CLASS STATE: three values,
                    not a boolean whose false already decided what to do when nobody knows. The
                    resolution is asymmetric on purpose. A DECLARATION IS NOT A DETERMINATION — the
                    shipped verifyAge('self-declaration') returns the same "verified" a document
                    check does, so nothing downstream can tell a tick-box from a passport; here a
                    declared adult establishes nothing and a declared child establishes everything,
                    because the only person with no incentive to lie in that direction is the one
                    telling you they are a child. A DETECTION CAN RAISE PROTECTION AND NEVER LOWER
                    IT. THE ABSENCE OF A SIGNAL IS NOT AN ADULT, and the resolution records every
                    source it consulted so a question nobody asked is distinguishable from one that
                    came back unsure. A SENSITIVE CONTEXT IS DECIDED BY THE OUTPUT (declared union
                    detected), and a reference to somebody nobody can age is exactly as unusable as
                    a reference to somebody known to be twelve -->
  - [x] I5.6.c Accept only approved privacy-preserving age-assurance assertions
        with issuer, method class, confidence/threshold, scope, freshness, and
        no raw identity disclosure. Evidence: assertion tests.
        <!-- evidence: assurance.ts — AN ASSERTION IS NOT A BIRTHDATE. The parsed type has a closed
                    key set and a payload carrying birthDate or documentId refuses for THAT before
                    anything else is looked at, so disclosure is unrepresentable rather than
                    forbidden — the same move I5.4 made for the face template. A METHOD CLASS IS
                    PART OF THE CLAIM and self-declaration is not on the approved list. A
                    CONFIDENCE BELOW ITS THRESHOLD READS UNKNOWN, NEVER ADULT: readEstimate's
                    return type has no 'adult' in it at all. AN AGE-SENSITIVE OUTPUT RAISES THE BAR.
                    YOU CANNOT GET YOUNGER, SO THE TWO DIRECTIONS EXPIRE DIFFERENTLY — an at-least
                    claim is monotone and an under claim decays a year per year, which is the exact
                    inversion of the shipped code that stamps a 365-day expiry on the adult claim
                    and then never reads it. AN ACCURACY IS NOT A PROTECTION: with a quarter of the
                    traffic under age and a check 98% right both ways, one admitted person in 148 is
                    a child and one refused in seventeen is an adult, and the two move opposite ways -->
  - [x] I5.6.d Prohibit child sexualization/exploitation, unsafe
        transformations, biometric/identity misuse, disallowed cloning, and
        prohibited retention/ sharing at input, plan, dispatch, output, and
        release. Evidence: end-to-end policy tests.
        <!-- evidence: prohibitions.ts + gate.ts — seven prohibitions over five checkpoints, one
                    door, and a total table. A PROHIBITION CHECKED ONLY AT THE END IS AN INVITATION
                    TO SPEND THE MONEY FIRST, so every row names the earliest checkpoint at which it
                    can be decided and EVERY LATER CHECKPOINT ASKS AGAIN with no gaps in between.
                    WHAT CANNOT BE SEEN YET CANNOT BE CLEARED YET: three of the seven genuinely
                    cannot be decided at input, asking one early is itself a refusal, and saying so
                    beats running a check that always passes and calling the row covered. A
                    TRANSFORMATION IS A CLAIM ABOUT A PERSON in both directions — aging a child up
                    and regressing an adult down are the same laundering move from opposite ends, so
                    the unsafe set is keyed on the status rather than being one list. Every rule
                    keys on "not established as an adult", so uncertainty is protected like a child,
                    and the suite pins the dispatcher's call count at zero for every refusal that
                    lands before dispatch — and honestly at one for the sharing rule, which cannot
                    fire until an artifact exists -->
  - [x] I5.6.e Redact sensitive child/age evidence from lower-tier UI/API/logs/
        exports while retaining authorized safety-review access. Evidence:
        redaction tests.
        <!-- evidence: redaction.ts — A REDACTION IS NOT A DELETION: the withheld marker stays in
                    the view carrying the kind and the tier that would be needed, because an absence
                    teaches the operator there was nothing to see and that is how a case gets closed
                    on an empty screen. THE MARKER SAYS WHAT IS MISSING AND WHY, NEVER WHAT IT SAID
                    — the Withheld type has no value field, so a leaking marker does not type-check.
                    A LOWER TIER STILL HAS TO BE ABLE TO ACT, so the refusal and the request survive
                    every projection. A CASE HANDLE IS NOT A PERSON, and the leak scan exists
                    precisely to catch the commonest version of this — using the identity as the
                    handle, so it sits unredacted in every log line above a panel reading "withheld".
                    YOU CANNOT REDACT WHAT YOU HAVE ALREADY WRITTEN: the projection happens at write
                    time and there is deliberately no function that turns a record back. A COPY
                    OUTLIVES THE READER — a reviewer reads the evidence on a screen and the same
                    evidence still does not enter an export -->
  - [x] I5.6.f Route uncertainty/reportable signals to authorized human safety
        review and incident/reporting procedures without automatic accusation.
        Evidence: escalation tests.
        <!-- evidence: escalation.ts — AN OBSERVATION IS NOT AN ACCUSATION. What goes to review is a
                    measurement — which instrument looked, at what, and what it read — and there is
                    no free-text field on an escalation at all, because the free-text field is where
                    a sentence about somebody whose age estimator was wrong by two years gets
                    written, and once it exists it is in a queue, a log, an export and somebody's
                    memory. The accusatory field names refuse by key. A QUEUE NOBODY IS AUTHORIZED
                    TO READ IS NOT A REVIEW: routing into a queue with no safety reader is the
                    failure that looks exactly like success. AN ESCALATION IS NOT A REFUSAL AND A
                    REFUSAL IS NOT AN ESCALATION — both have to happen, and each half alone has its
                    own refusal. A REPORT IS A HUMAN ACT: the draft is a one-member union, the filed
                    type is produced by exactly one function, and that function needs a named person
                    holding the lead role, because an automated system that files reports about
                    people will file reports about people who did nothing -->
- [x] I5.7 Implement input and output moderation with explicit uncertainty.
      Evidence: classifier version, thresholds, and human-review queue.
      <!-- evidence: evidence/v1-workbenches/moderation.json + moderation/ (12-file
      strict-tsc reference, 542 assertions, 107/107 mutants caught); generator
      scripts/v1-workbenches/generate-moderation.mjs (+ --check drift gate, 15
      tests). Evaluator version, model digest and threshold-set version are pinned
      into every observation and verdict; the queue carries locks, derived
      priority and no path from elapsed time to permission. The classifiers
      themselves are taken through the RunEvaluator seam, not implemented. -->
  - [x] I5.7.a Define moderation subjects/categories, input/output stages,
        evaluator versions, thresholds/bands, regions/timecodes, uncertainty,
        action policy, and audience projections. Evidence: moderation schemas.
        <!-- evidence: moderation.ts + evaluators.ts — 16 subjects over 2 stages,
        11 categories (3 absorbing), 6 actions on ONE axis with humanReview as a
        separate boolean, half-open bands walked over all 1001 scores, 6 locus
        kinds with per-modality validity, 3 outcome kinds, 5 audiences. -->
  - [x] I5.7.b Moderate brief text, structured controls, reference media,
        imported specs/graphs, filenames/metadata, and linked sources before
        planning/dispatch. Evidence: input coverage tests.
        <!-- evidence: input-coverage.ts — 8 input subjects, dispatch guard,
        graph-text extraction, unresolved-link refusal; reproduces the shipped
        substring matcher beside a boundary matcher and measures both over an
        8-brief corpus (6 substring artifacts, 8/8 vs 4/8 tagged). -->
  - [x] I5.7.c Moderate every generated/derived media component, transcript/
        caption/metadata, preview, bundle, and variant before exposure/release.
        Evidence: output coverage tests.
        <!-- evidence: output-coverage.ts — 8 output subjects, exposure-driven
        coverage, evidence-stage check that stops an input pass discharging an
        output one, and locus mapping through crop/scale/transcode/frame-extract/
        contact-sheet with restyle refusing rather than guessing. -->
  - [x] I5.7.d Combine multiple classifier/rule results without averaging away a
        blocking category, and preserve conflicts/unavailable/error states.
        Evidence: aggregation tests.
        <!-- evidence: aggregation.ts — maximum within a category and strongest
        action across them; absorbing categories resolve by strength not
        declaration order; disputes, unavailability and indeterminacy are kept
        distinct from a clean reading; the shipped mean is reproduced only so the
        dilution (900 -> 500 -> 366 as clean evaluators are added) is measured. -->
  - [x] I5.7.e Persist raw attributed scores/regions/uncertainty separately from
        policy verdict/action and exact evaluator/threshold versions. Evidence:
        moderation result tests.
        <!-- evidence: result.ts — ObservationRecord carries no action and no
        verdict; VerdictRecord pins policy/threshold-set versions; the ledger is
        append-only with supersession; replay re-decides from stored observations
        and refuses fresh readings; stored counts are recomputed from evidence. -->
  - [x] I5.7.f Route configured uncertainty/manual bands to a prioritized human
        queue with subject lock, evidence, decision, and no false auto-pass.
        Evidence: review queue tests.
        <!-- evidence: queue.ts — priority derived lexicographically from four
        recorded facts, locks with expiry that returns the item still held and
        counts the abandonment, decisions requiring the lock holder and cited
        evidence from this verdict, senior role to release an absorbing flag, and
        requireNotAutoPassed refusing on both axes. -->
        <!-- partial: the human review rota behind the queue is an operational
        commitment, not a code artifact; that half stays with the operators. -->
  <!-- partial: wiring this gate into the live Isis generation path is separate
  from the reference and is not claimed here. -->
<!-- I5.7 verification finding: the shipped surfaces are
libs/isis/ai-providers/src/providers/civitai/content-safety-provider.ts and
apps/isis/web/src/security/content-governance-policy-utils.ts. Both match keywords
as substrings, so "a sensual portrait in a canteen" is classified illegal at rating
X (canteen contains teen) while "a nine-year-old, unclothed" comes back safe at a
reported confidence of 90 — higher than the 82 for the canteen. The reported
confidence is a mean over the tags that fired and a hardcoded 90 when none did;
scanOutput accepts an imageUrl and classifies output.text ?? ''; disabling output
scanning returns a manufactured safe:true/confidence:100; the classification cache
is keyed on content alone; checkRule returns on the first matching condition; and
parseScanReport trusts stored counts while silently dropping the findings behind
them. Full detail in the shippedComparison block of moderation.json. -->

- [x] I5.8 Implement similarity/originality screening appropriate to visual,
      audio, 3D, and video assets. Evidence: results are advisory or blocking
      only according to ratified policy.
      <!-- evidence: evidence/v1-workbenches/similarity.json + similarity/ (13-file
      strict-tsc reference, 357 assertions, 86/86 mutants caught); generator
      scripts/v1-workbenches/generate-similarity.mjs (+ --check drift gate, 14
      tests). Organising rule: AN INVARIANCE IS A CHOICE, AND EVERY INVARIANCE IS
      ALSO A BLIND SPOT — every descriptor declares both lists and they are checked
      disjoint and non-empty at parse time. No classifier or embedding model is
      implemented; descriptors arrive as vectors through their seams. -->
  - [x] I5.8.a Define protected/reference corpus scope, version, rights/access,
        modality-specific fingerprints/embeddings, thresholds, uncertainty, and
        permitted decision use. Evidence: screening policy schema.
        <!-- evidence: screening.ts + metrics.ts — 4 modalities over 23 channels, 5
        rights classes (2 actionable), 15 adversary transforms, 5 metrics, 5 named
        uncertainty sources; a corpus carries id, version, coverage AND the basis
        for that coverage, because a not-found against 2% of the space is not an
        originality finding. -->
  - [x] I5.8.b Implement visual whole-image and localized composition/content/
        style similarity with crop/transform/near-duplicate handling. Evidence:
        visual similarity fixtures.
        <!-- evidence: visual.ts — tile-grid windows searched exhaustively or
        honestly bounded, mirror search with the flip recorded, zero-mean
        normalised cross-correlation (raw cosine over brightness matches every
        window alike), perceptual-hash Hamming contrasted against the shipped
        Jaccard-over-sha256 which has no intermediate value. -->
  - [x] I5.8.c Implement audio fingerprint/melody/harmony/rhythm/timbre/voice/
        sample similarity with time alignment and licensed-source exclusions.
        Evidence: audio similarity fixtures.
        <!-- evidence: audio.ts — reproduces the shipped chroma detector and
        measures both inversions (relative keys 1000 -> block; semitone
        transposition 286 -> allow); interval histograms, root motion,
        tempo-normalised IOI, bounded sample alignment, licensed-source exclusion
        before scoring. The speaker comparison is a seam to the I5.5 reference. -->
  - [x] I5.8.d Implement 3D geometry/topology/shape/texture/material/scene
        similarity with units/transforms/partial components. Evidence: 3D
        similarity fixtures.
        <!-- evidence: three-d.ts — reproduces the shipped duplicate detector and
        measures the theft it misses (0.457, below its own 0.65 report floor);
        mean-normalised shape distribution invariant to rotation and scale, Euler
        characteristic as a filter rather than a distance, unit-normalised volume,
        per-component scene matching that keeps the best part rather than a mean. -->
  - [x] I5.8.e Implement video frame/shot/temporal/audio similarity with
        reencoding/crop/rate/edit alignment. Evidence: video fixtures.
        <!-- evidence: video.ts — everything addressed in milliseconds so 24fps and
        30fps are the same timeline; shot durations aligned as subsequences under
        an explicit tolerance, with inserted and dropped shots named; the temporal
        channel read against the query so a cutdown is entirely a copy. -->
  - [x] I5.8.f Return candidate matches, protected-source identities only when
        authorized, raw scores/regions, uncertainty, corpus/version, and no
        unsupported ownership conclusion. Evidence: result/privacy tests.
        <!-- evidence: disclosure.ts — a stable pseudonymous handle for the
        unauthorised, identity for 2 of 4 audiences and scores for 3 (the identity
        is rationed, never the evidence); corpus/version/coverage and uncertainty
        on every result including negative ones; a conclusion-vocabulary gate over
        emitted statements naming the 3 facts a distance cannot supply. -->
  - [x] I5.8.g Apply advisory/manual/blocking action solely from ratified
        modality/use/risk policy and require human rights review where
        specified. Evidence: policy-boundary tests.
        <!-- evidence: policy.ts — rows indexed by modality/channel/use with
        ordered thresholds and a per-row action ceiling, so a channel at its
        ceiling is not outvoted and style may advise and never block; an
        unratified policy still screens and cannot act above advisory, with the
        cap recorded rather than silent; a required rights review needs a named
        holder of the role. -->
        <!-- partial: the ratification itself is a human act and the rights-review
        rota is an operational commitment; both stay with the rights authority. -->
  <!-- partial: building the protected corpus, and the extractors that produce the
  descriptors, are separate from the reference and are not claimed here. -->
<!-- I5.8 verification finding: the two shipped surfaces fail in opposite
directions and both are exactly computable.
libs/isis/ai-providers/.../copyrighted-style-detector.ts compares a 12-bin chroma
histogram at 0.93: C major and its relative A minor are the same seven pitch
classes so unrelated music scores 1 and is BLOCKED, while a semitone transposition
scores exactly 2/7 and is ALLOWED — and no threshold separates them, because the
separation is not in the descriptor. libs/isis/3d-marketplace-ops/duplicate-
detection.ts aggregates channels by a weighted mean and drops everything under
0.65, so byte-identical textures on a 10%-decimated mesh aggregate to 0.457 and
are never reported; its dominant geometry term is a step function over a sha256,
its texture channel is Jaccard over digests, and its volume guard is truthiness so
an absent volume and a measured zero both score 0. Neither records a corpus
version, coverage, uncertainty or ratification, and both disclose the matched
source identity unconditionally. Full detail in similarity.json. -->

- [x] I5.9 Implement visible watermark policy per audience and asset kind.
      Evidence: output inspection confirms coverage and legibility.
  - [x] I5.9.a Define watermark requirements by asset kind, audience, preview/
        review/release state, target/channel, disclosure text/mark, placement,
        persistence, and removal authority. Evidence: policy matrix.
  - [x] I5.9.b Render image/texture watermarks with safe-area, scale, contrast,
        opacity, repetition, crop/resize resilience, and alpha/color handling.
        Evidence: image inspection fixtures.
  - [x] I5.9.c Render video/animation watermarks with temporal coverage,
        movement/placement, scene/crop considerations, HDR/color, and captions/
        safe areas. Evidence: video inspection fixtures.
  - [x] I5.9.d Render audio watermarks/disclosures where policy requires with
        intelligibility, timing, loudness, localization, and accessibility.
        Evidence: audio disclosure tests.
  - [x] I5.9.e Apply 3D/splat/package preview overlays or embedded visible
        disclosure assets without corrupting source geometry/package. Evidence:
        3D/package tests.
  - [x] I5.9.f Verify the final stored/delivered bytes for required legibility/
        coverage after target transformation and block on missing marks.
        Evidence: post-write inspection tests.
  - [x] I5.9.g Record policy/version/render config/result and govern authorized
        removal as a new output revision. Evidence: watermark lineage tests.
<!-- evidence: evidence/v1-workbenches/watermark/ (14-file strict-tsc reference; 523
     assertions run under tsx; 135/135 mutants killed with an inert control surviving) +
     evidence/v1-workbenches/watermark.json, generated and drift-gated by
     scripts/v1-workbenches/generate-watermark.mjs (+ 19 known-value tests), wired into
     pnpm inventory:v1-workbenches / verify:inventory:v1-workbenches and CI.
     I5.9.a is a policy matrix that is TOTAL over 8 asset kinds x 4 audiences x 3 release
     states x 6 channels: every one of the 576 cells resolves or refuses by name. Behind it
     is a 5-kind x 10-transform survival table in which three cells are 'depends-on-placement'
     because a crop is a geometric question no table answers, and every one of the other 47
     is checked against what the delivery pipeline actually does. Disclosure is proved
     monotone in audience reach over the whole cross product, with the recipient trace
     excluded by name because you cannot trace a leak to an audience you did not name.
     Localisation refuses rather than falling back. The shipped leak-trace token is
     reproduced and its two exact collisions demonstrated; the reference replaces it with an
     HMAC over a length-prefixed encoding that round-trips.
     I5.9.b-e render and measure for real: WCAG relative luminance and contrast (white on
     black is exactly 2100 hundredths, #767676 on white 454), compositing in linear light,
     the exact minimum-opacity inverse, the crop guarantee period + side - 1 proved by
     exhausting offsets, the sqrt(21) ceiling over an unknown backdrop, SMPTE ST 2084 PQ
     against BT.2408's 203-nit reference white, coherent decibel summation, and a per-locale
     speaking model. I5.9.f applies the channel's transforms to real pixels and re-measures
     the DELIVERED frame, refusing to run at all on bytes that have not been through it.
     I5.9.g is an append-only revision chain in which a removal is a new revision and the AI
     disclosure has no removal authority at all.
     Records six shipped Isis surfaces as a verified structural comparison: the quality gate
     treats `watermark` as a zero-tolerance ARTIFACT while the marketplace controls REQUIRE
     one, and running the shipped keyword detector over the marketplace's own control string
     raises exactly that artifact — so the gate meant to pass the disclosed asset rejects it
     for carrying the disclosure. Full detail in watermark.json. -->

- [x] I5.10 Implement robust invisible watermarking where supported. Evidence:
      channel-transformation survival evaluation.
  - [x] I5.10.a Define supported modalities/formats/targets, payload,
        key/version, embed/detect algorithms, strength/quality limits,
        disclosure, and unavailable policy. Evidence: invisible-mark schema.
  - [x] I5.10.b Embed a minimal opaque ledger/output identifier after generation
        at the correct pipeline stage without encoding sensitive metadata.
        Evidence: embed/extract tests.
  - [x] I5.10.c Verify the stored artifact contains the expected mark with
        confidence and no unacceptable quality/technical-gate regression.
        Evidence: post-embed validation tests.
  - [x] I5.10.d Evaluate detection survival across configured resize/crop/
        transcode/compression/color/audio
        resample/time-stretch/noise/screenshot/ recording transformations.
        Evidence: robustness benchmark.
  - [x] I5.10.e Measure false-positive/collision/wrong-payload/tamper rates on
        marked and unmarked calibrated corpora. Evidence: calibration report.
  - [x] I5.10.f Protect embed/detect keys and detailed parameters, support key
        rotation, and retain authorized historical verification. Evidence: key
        lifecycle/security tests.
  - [x] I5.10.g Return explicit unsupported/unavailable/degraded state and never
        claim invisible marking from policy intent alone. Evidence: honest-state
        tests.
<!-- evidence: evidence/v1-workbenches/invisible-mark/ (12-file strict-tsc reference; 312
     assertions run under tsx; 107/107 mutants killed with an inert control surviving) +
     evidence/v1-workbenches/invisible-mark.json, generated and drift-gated by
     scripts/v1-workbenches/generate-invisible-mark.mjs (+ 15 known-value tests), wired into
     pnpm inventory:v1-workbenches / verify:inventory:v1-workbenches and CI.
     The embedder is REAL: quantisation index modulation with a keyed dither, so "an additive
     error smaller than half a step is corrected and one larger is not" is a theorem checked
     over every value in a period in both directions, and the payload type has one field —
     a 32-bit opaque ledger identifier — so the shipped forensic payload's operatorId does
     not typecheck into it. The detector's null distribution is computed EXACTLY with big
     integers (per-bit agreement of nine chips is 252/168/72/18/2 out of 512, convolved 32
     times into 129 outcomes), giving 695 in a billion at 210 of 288 chips and about one at
     220 — so the threshold is derived rather than chosen. The robustness benchmark applies
     its fourteen transformations for real: six survived, eight did not, no row the per-chip
     guarantee promised was lost, and one row it gave up on came back anyway. Raising the
     step fourfold beats the noise it could not beat and does nothing whatever for a resize,
     which is why availability for any resizing channel is 'unavailable' with the visible mark
     of I5.9 named as the fallback rather than a low score. The calibration runs a seven-file
     labelled corpus and reports one miss and one wrong payload beside the model's rate.
     Records the two shipped surfaces as a verified structural comparison: the voice module
     is a 446-line PLANNER that touches no audio and returns the same six stages for every
     input, its predicted recoverability rises by exactly 0.08 when a JSON sidecar is attached
     to the same audio, its survivability moves by exactly 0.12 on whether a rights form was
     signed, and its identifier is an unkeyed digest of the request. Full detail in
     invisible-mark.json. -->

- [x] I5.11 Implement C2PA Content Credentials with ingredient lineage.
      Evidence: signed artifacts validate through the pinned toolchain.
  - [x] I5.11.a Define assertion/claim profile per asset kind/target with
        creator/ tool identity, actions, ingredients, AI disclosure, rights,
        timestamps, hashes, and privacy filtering. Evidence: manifest profile
        tests.
  - [x] I5.11.b Build ingredient lineage from immutable source/reference/model/
        workflow/run/output/edit identities and represent redacted/external/
        missing ingredients explicitly. Evidence: lineage mapping fixtures.
  - [x] I5.11.c Generate canonical manifests bound to final output bytes after
        target transformation and before registry write verification. Evidence:
        manifest generation tests.
  - [x] I5.11.d Sign through approved external key custody with certificate/
        timestamp/status checks, rotation, and no private-key application
        access. Evidence: signing integration tests.
  - [x] I5.11.e Embed or sidecar credentials according to format/target and keep
        package references/hash bindings deterministic. Evidence: packaging
        tests.
  - [x] I5.11.f Validate signature, trust chain, timestamp, status, asset hash,
        assertions, ingredients, and policy using the pinned independent
        toolchain. Evidence: verification fixtures.
  - [x] I5.11.g Handle unsupported formats/signing failure/invalid credentials
        as explicit blocking or degraded states per target profile. Evidence:
        failure-mode tests.
<!-- evidence: evidence/v1-workbenches/content-credentials/ (17-file strict-tsc reference; 608
     assertions run under tsx; 96/97 mutants killed by the suite with an inert control surviving)
     + evidence/v1-workbenches/content-credentials.json, generated and drift-gated by
     scripts/v1-workbenches/generate-content-credentials.mjs (+ 15 known-value tests), wired into
     pnpm inventory:v1-workbenches / verify:inventory:v1-workbenches and CI.
     The cryptography is REAL and third-party checkable: RFC 8032 Ed25519 over FIPS 180-4 SHA-512,
     both held to published vectors in the suite and cross-checked byte-for-byte against
     node:crypto over 60 random hashes and 25 random keypairs while it was written, so a MAC is not
     quietly standing where a signature goes. Canonicalisation is RFC 8785, which is where the
     shipped finding lives: c2pa-metadata-embedding.ts:223 sorts assertion keys with
     localeCompare and claim-signing.ts:105 sorts with the default comparator, so the assertion
     hashes a manifest publishes are computed under one rule and the signature over it under
     another; the reference reproduces both functions verbatim and shows the two sha256 digests
     differ. The planner's serialiser also maps null, NaN, Infinity and -Infinity to one string
     (six colliding pairs) and renders a hole in an array as "[,1]", which is not JSON.
     The hard binding is a theorem, not a claim: flipping every byte of a file with a ten-byte
     exclusion changes the digest for all fifty-four bytes outside it and none of the ten inside,
     and a uniform file excluded at two different places digests differently because the exclusion
     list is hashed in. Five container encodings insert and recover a manifest byte-exactly with
     real CRC-32, and whether stripping the credential returns the original bytes is MEASURED per
     format: true for png/jpeg/mp4, false for wav/webp/glb, which is exactly the rewritesHeader
     column. Custody is a port with no method that returns key material and a signDigest that takes
     32 bytes; path validation runs at the SIGNING instant, a timestamped signature survives
     certificate expiry and an untimestamped one does not, and a revocation's reason decides what it
     kills (a supersession spares everything signed before it; a compromise with no invalidity date
     takes everything). The validator recomputes from the bytes, has no second input for the signing
     certificate, refuses a manifest that grades its own homework, and treats an unknown as
     indeterminate rather than valid. Two guards in gate.ts are documented in the source as
     unreachable given the fixed manifest reservation; the suite checks the invariant they rest on
     rather than pretending to reach them, and the reservation's value is the one mutation the
     drift gate rather than the suite catches, because it is a tunable. NOT the C2PA wire format:
     no JUMBF, CBOR, COSE, DER, X.509 or RFC 3161, so these manifests will not open in the c2pa CLI
     and are not offered as though they would. Full detail in content-credentials.json. -->
- [x] I5.12 Implement external provenance-ledger recovery. Evidence: stripped
      manifest can be matched without false association.
  - [x] I5.12.a Define a privacy-minimized ledger record containing opaque
        asset/ output identity, strong content hash/fingerprint versions,
        credential digest, issuance/status, and authorized recovery metadata.
        Evidence: ledger schema.
  - [x] I5.12.b Write ledger records transactionally with release/output state
        and verify external acknowledgement/integrity. Evidence: write failure
        tests.
  - [x] I5.12.c Recover exact-byte matches by cryptographic hash and transformed
        candidates by calibrated modality-specific fingerprints as distinct
        result classes. Evidence: recovery fixtures.
  - [x] I5.12.d Enforce confidence/collision/ambiguity thresholds and return
        multiple/no-match/manual-review rather than false association. Evidence:
        false-match corpus.
  - [x] I5.12.e Authorize recovery queries, rate-limit enumeration, and redact
        tenant/creator/source metadata until proof/role permits disclosure.
        Evidence: privacy/abuse tests.
  - [x] I5.12.f Respect recall/revocation/deletion/legal-hold status while
        retaining minimum required provenance proof. Evidence: lifecycle tests.
<!-- evidence: evidence/v1-workbenches/ledger-recovery/ (14-file strict-tsc reference; 468
     assertions run under tsx; 87/87 mutants killed by the suite with an inert control surviving)
     + evidence/v1-workbenches/ledger-recovery.json, generated and drift-gated by
     scripts/v1-workbenches/generate-ledger-recovery.mjs (+ 14 known-value tests), wired into
     pnpm inventory:v1-workbenches / verify:inventory:v1-workbenches and CI.
     The threshold is DERIVED, not chosen: a real two-dimensional DCT-II produces a 63-bit code
     (the DC dropped because it is the brightness, which is why 63 and not 64), and exact
     big-integer binomial arithmetic turns a false-association budget and a ledger size into a
     Hamming threshold — eight bits against a thousand records, five against a million, two against
     a billion. Five bits is not many, and the reference says so rather than raising the threshold
     until the fixtures pass. Robustness is MEASURED over six rows: a 5% exposure change and a box
     blur move the code by zero bits, blur-then-exposure by two, heavy noise by ten, a hard grade by
     eighteen. The load-bearing measurement is that PLATE_B — a different picture — sits at eight
     bits, NEARER than the same picture under heavy noise, which is why the band between the
     identification and review thresholds produces a case and never a name, and why a win by under
     two bits is downgraded. Audio is a second algorithm rather than the same one renamed: a
     sub-band double difference that survives a 0.3x and a 2x gain AND a constant spectral tilt at
     zero bits, where a single difference would have lost ten of thirty-two. Over a labelled corpus
     of nine: six correct, one review, one miss, one true negative, zero false associations, and the
     five buckets are asserted to account for every query. The record is built from nothing upwards
     with a three-way exposure table (record / authorized / never), the write is transactional in
     both directions with an authenticated receipt and a durable sequence, disclosure needs
     possession of the C2PA credential of I5.11 or a role whose standing is checked against the
     tenant, enumeration is caught by the hit rate rather than the rate limit alone, and erasure
     drops the identifying half while retaining the digests, the date and the status. Two of my own
     defects were found and fixed while building it: a flat frame produced a 63-bit code made
     entirely of floating-point rounding error (now refused by a structure floor measured against
     the DC), and the identification and review bands were two filters that could drift apart (now
     one bandOf function, tested at both boundaries). NOT CLAIMED: no JPEG decode, no filterbank,
     and no rotation, crop or scale invariance whatever — a cropped picture will not be found, which
     is a real limit of a DCT construction and is stated rather than left to be discovered; and the
     false-match model assumes bit independence that real photographs do not have, which makes the
     true rate worse than the published one, so a corpus is measured beside it. Full detail in
     ledger-recovery.json. -->
- [x] I5.13 Implement consent, license, model, workflow, and source blast-radius
      queries. Evidence: revocation preview lists every dependent run, asset,
      collection, and release.
  - [x] I5.13.a Build immutable reverse dependency edges from consent, rights/
        license, source/reference, dataset/model/adapter, workflow/node, policy,
        run, output, asset, bundle, collection, and release versions. Evidence:
        dependency graph tests.
  - [x] I5.13.b Accept only authorized exact subject/version/hash/revocation
        inputs and distinguish direct from transitive dependency. Evidence:
        query contract tests.
  - [x] I5.13.c Traverse cycles/depth/fan-out safely with stable pagination and
        complete typed nodes/edges/reasons. Evidence: large graph tests.
  - [x] I5.13.d Report affected plans/runs/outputs/assets/variants/bundles/
        collections/gallery/marketplace/releases/targets and current state.
        Evidence: blast-radius golden fixtures.
  - [x] I5.13.e Calculate prospective policy action, urgency, owner,
        replacement/ remediation, and unresolved/unknown dependencies without
        mutating them. Evidence: preview tests.
  - [x] I5.13.f Enforce tenant/project/property-level redaction while preserving
        accurate authorized aggregate counts. Evidence: cross-tenant tests.
<!-- evidence: evidence/v1-workbenches/blast-radius/ (13-file strict-tsc reference; 302
     assertions run under tsx; 87/87 mutants killed by the suite with an inert control surviving)
     + evidence/v1-workbenches/blast-radius.json, generated and drift-gated by
     scripts/v1-workbenches/generate-blast-radius.mjs (+ 15 known-value tests), wired into
     pnpm inventory:v1-workbenches / verify:inventory:v1-workbenches and CI.
     A BLAST RADIUS IS A QUESTION ASKED BACKWARDS: edges are content-addressed over their own
     triple, so recording a dependency twice is one edge and an altered one no longer hashes to
     itself; every endpoint carries a version, because "this output used the kiln model" is not
     answerable; and the reverse index is rebuilt and checked in both directions rather than stored
     and trusted. The two ends of an edge fail DIFFERENTLY — a missing dependency is a broken write,
     a missing dependent is an unresolved one that gets counted — which was a fix, not the first
     design. Three properties carry the item and each is checked rather than asserted: a ten-long
     chain against a depth bound of six reaches six and names what it left unwalked; a key cursor
     and an offset cursor run over the same mid-pagination insert, and the offset returns a row the
     reader has already seen AND skips one they never will while the key cursor does neither; and
     four principals ask the same question and get four different visible sets (11, 12, 12, 0) with
     one identical total of 12, where a filter would have told the aggregate-only auditor zero.
     Urgency is DERIVED from the exposed count, which is the direct contrast with the shipped
     finding, and requireUrgencyDerived exists to stop it being simplified back. The preview is
     prospective by construction and the graph digest proves it. Two design bugs of my own were
     found and fixed while building: the traversal conflated MERGING a second path into a node with
     DOWNGRADING its relation (the merge is the ordinary case; the check now runs against the graph),
     and the graph builder refused the wrong endpoint. Mutation testing then found nine more holes,
     all one class — overlapping guards, where removing any single one left the refusal NAME
     unchanged and the suite silent — now isolated with per-guard cases asserting the message.
     SHIPPED FINDING: libs/isis/model-governance-3d/src/upstream-change-monitor.ts raises exactly
     this item's triggers ('license-drift' among them) and its twelve-field alert names no run,
     output, asset, bundle, collection or release; its severity is a literal per alert kind, so the
     function has nowhere for a radius to go and a drift behind one draft and one behind forty
     published releases are both 'critical'; and the generated LineageEdgeType's five values are all
     asset-to-asset, expressing 2 of the 12 kinds a revocation can start at — there is no edge type
     in which "this asset exists because of that person's consent" could be written down. NOT
     CLAIMED: the graph is an in-memory index; persistence, sharding, concurrent reverse-index
     consistency and cross-shard pagination are not solved, the bounds would need a resumable walk
     rather than a truncation in production, and the small-bucket disclosure problem (a hidden count
     of one is close to an identification) is named rather than solved. Full detail in
     blast-radius.json. -->
- [x] I5.14 Implement recall and quarantine propagation. Evidence: affected
      gallery items and releases become unavailable according to policy.
  - [x] I5.14.a Define recall/quarantine case, trigger, subject, scope,
        severity, actor/authority, policy, target actions, deadlines, status,
        and evidence. Evidence: case schemas.
  - [x] I5.14.b Freeze the authorized blast-radius snapshot and require explicit
        include/exclude reasons for any bounded scope adjustment. Evidence:
        scope tests.
  - [x] I5.14.c Quarantine affected model/workflow/source/output/asset/bundle
        versions from new planning, reuse, review, and release atomically.
        Evidence: admission tests.
  - [x] I5.14.d Unpublish/disable affected gallery, marketplace, collection,
        campaign, download, API, CDN, and target-distribution references
        according to channel policy. Evidence: propagation integration tests.
  - [x] I5.14.e Revoke/replace signed URLs, caches, credentials/manifests where
        supported and verify downstream state; record irrecoverable copies.
        Evidence: post-action verification tests.
  - [x] I5.14.f Notify required owners/reviewers/consumers/incidents with
        audience-safe reasons and track acknowledgements/escalation. Evidence:
        notification tests.
  - [x] I5.14.g Restore only via a new authorized case decision after
        remediation and complete verification; preserve recall/quarantine
        history. Evidence: restoration tests.
<!-- evidence: evidence/v1-workbenches/recall-propagation/ (14-file strict-tsc reference; 319
     assertions run under tsx; 91/91 mutants killed by the suite with an inert control surviving)
     + evidence/v1-workbenches/recall-propagation.json, generated and drift-gated by
     scripts/v1-workbenches/generate-recall-propagation.mjs (+ 16 known-value tests), wired into
     pnpm inventory:v1-workbenches / verify:inventory:v1-workbenches and CI.
     A RECALL IS A DECISION SOMEBODY MADE: the case carries an actor, an authority CEILING (a
     reviewer decides up to routine, a rights officer up to elevated, and a court order is
     legal-only however small the radius) and required evidence, and nothing in it reads a clock —
     every instant is an argument, so a case replays identically and an escalation compares two
     recorded times. The I5.13 report is FROZEN and digested before anything is decided; adjustments
     are bounded at a quarter of the frozen set, each names an item, a direction, a reason and an
     actor, and inclusions and exclusions are reported separately because an exclusion is the
     dangerous direction. Quarantine is ONE admission set four gates consult, so a split between
     gates is unconstructible here — and the same check, pointed at the shipped four-boolean record,
     refuses it immediately. The channel table refuses to let an unverifiable channel report
     'confirmed', and the hardest honesty in the item is arithmetic: the fixture propagation
     confirms three channels, issues three it cannot verify, and records 4139 copies in hands nobody
     controls, so it is NOT contained and the summary carries that number beside the successes
     rather than under them. Notifications are audience-filtered — a court order and a consent
     withdrawal produce the SAME external string, and every trigger the reference knows passes its
     own unsafe-word filter — with acknowledgements tracked separately from sends and escalation
     driven by the deadline. Restoration is a new case with its own actor, authority, remediation
     and verification, and it needs a prior state captured at quarantine time or it is refused
     rather than guessed at.
     SHIPPED FINDING (libs/isis/3d-quality-gates/src/delivery/output-quarantine.ts): its four
     reasons express NONE of the seven recall triggers (zero of seven, computed); its four
     independent block flags put a DISPOSED output at blocked-for-release and admitted-for-reuse,
     and two of the four gates this reference asks about have no flag at all; its release input has
     no actor, authority, remediation or verification (all four missing, computed); and release
     writes visibility 'internal-only' as a constant because the record never stored the prior
     value — structural, not behavioural, since the information is gone the moment the quarantine is
     applied. Its timestamps default to `new Date()`, so a replayed decision produces a different
     record. Said plainly: for what it is, a holding pen for outputs that failed this house's own
     gates, the module is reasonable; the finding is that it is being read as a recall mechanism.
     NOT CLAIMED: nothing talks to a CDN, a marketplace or a mail server — the channel table is the
     contract and the adapters are what a deployment writes; and making four real gates consult one
     store transactionally is a distributed-systems problem this reference does not solve. Full
     detail in recall-propagation.json. -->
- [x] I5.15 Protect biometric and protected-source fields in UI, logs,
      notifications, exports, and support tools. Evidence: redaction suite.
  <!-- evidence: evidence/v1-workbenches/field-protection.json + the compiling reference under
       evidence/v1-workbenches/field-protection/ (403 assertions, 55 named refusals, 39 rules,
       144/154 mutants killed by the suite, inert control survived). Gated three ways in
       verify:inventory:v1-workbenches: the generator's --check drift gate, `tsc --noEmit` over the
       reference's own strict tsconfig, and a node --test suite that runs check.ts and asserts its
       assertion and refusal counts. The classification is grounded in eighteen real Isis
       properties across five shipped files, each of which the generator re-reads.
       SHIPPED FINDING: Isis has two redactors — apps/isis/web/src/security/redaction-utils.ts and
       libs/isis/llm-providers/src/client/pii-redactor.ts — and both are TEXT redactors, while every
       biometric property in Isis is a number vector, a buffer or a caller-supplied map. Keyword
       coverage of the eighteen protected properties is zero, by full path and by leaf; the only
       number the web one touches is `tokenCount`, which it turns into a string; its walk keeps one
       WeakSet it never clears, so an object referenced twice comes back as '[REDACTED]' the second
       time and an auditor cannot tell that apart from a refusal; and one URL has its query secret
       redacted and its identical userinfo secret preserved, under two different markers. -->
  - [x] I5.15.a Classify face/voice embeddings, identity evidence, age
        assurance, consent artifacts, protected source/corpus, similarity
        matches, and derived identifiers at schema/property level. Evidence:
        data inventory.
  - [x] I5.15.b Define per-role/purpose/host/tenant field visibility, masking,
        aggregation, use, export, retention, and break-glass rules. Evidence:
        access matrix.
  - [x] I5.15.c Apply centralized projection/redaction before UI/API/event/
        websocket/search/index/cache exposure. Evidence: property authorization
        tests.
  - [x] I5.15.d Apply structured log/trace/metric/error/audit scrubbing and
        block sensitive values from labels, URLs, exception bodies, and debug
        dumps. Evidence: telemetry leak tests.
  - [x] I5.15.e Render notifications/email/webhooks/support views with safe
        references and separately authorized drill-down. Evidence: notification/
        support redaction tests.
  - [x] I5.15.f Sanitize CSV/JSON/package/portable-spec/evidence exports and
        protect against hidden metadata/filenames/formulas. Evidence: export
        leak tests.
  - [x] I5.15.g Enforce encryption/key separation/retention/deletion and audit
        every authorized sensitive-field access. Evidence: lifecycle/access
        tests.
- [x] I5.16 Implement authorized exception workflows without mutating original
      evidence. Evidence: waiver scope, expiry, approver, and audit.
  <!-- evidence: evidence/v1-workbenches/exception-workflow.json + the compiling reference under
       evidence/v1-workbenches/exception-workflow/ (15 files, 536 assertions, 66 named refusals all
       reached, 46 rules, 371/373 mutants killed with two survivors that are inert by
       construction and say so in the source). Gated three ways
       in verify:inventory:v1-workbenches: the generator's --check drift gate, `tsc --noEmit` over
       the reference's own strict tsconfig, and a node --test suite (18 tests) that runs check.ts
       and asserts its assertion and refusal counts. The gate registry is grounded in sixteen gates
       drawn from ten real Isis files, each of which the generator re-reads and greps for the
       identifier the gate was drawn from.
       THE EXCEPTION DOES NOT CHANGE THE MEASUREMENT, IT CHANGES WHAT WE DO ABOUT IT — the failing
       verdict travels through the projection byte-identically (checked by digest on both sides) and
       the effect is 'permitted-under-exception', never 'pass'. Nothing reads a clock: every instant
       is an argument, so establishing the same exception twice produces the same entry hash byte for
       byte. THE FLOOR IS CHECKED BEFORE THE AUTHORITY, and a compound gate inherits the floor of its
       parts under its own distinct refusal, because nobody asks to waive child safety — they ask to
       waive the release bundle that contains it. An unstated scope dimension grants NOTHING, all
       seven dimensions are evaluated every time (four of four mismatches reported), and the waiver is
       revalidated at each governed action rather than held as a token.
       SHIPPED FINDING: there are TWO exception workflows in this monorepo and neither knows about
       the other, and the one with the weaker control governs the more consequential thing.
       libs/contracts/src/common/entitlement-exception-policy.ts (V1-CX-003) waives an ENTITLEMENT and
       has an eight-rung role ladder, `approver_same_as_requester`, an approver-outranks-requester
       check, per-class duration caps and dual control. libs/isis/model-governance-3d/src/
       exception-approval.ts (646 lines, exported from the library index) waives a MODEL GOVERNANCE
       POLICY DECISION and NEVER COMPARES THE REQUESTER TO THE APPROVER — the word "requester" does
       not appear in the file, which the generator asserts non-vacuously. Also computed rather than
       asserted: an empty scope list means EVERYTHING (all six dimensions default to `[]` and the
       badge says "Global approval"); the mismatch report short-circuits on `&&` so an operator sees
       1 of 6 wrong dimensions; `effectiveAt` is computed, stored, published on the type and never
       read by `determineStatus`, so a waiver marked effective in 2030 is active in 2026; three of
       the six decision verbs the ledger names (reject, request-changes, withdraw) cannot be written
       down at all; audit entry ids hash four identifiers and none of the entry's content, and
       caller-supplied entries are appended and re-sorted so an entry dated 1990 becomes the first
       thing that ever happened; an automatic expiry is recorded with `actor: approver`; and
       `crypto.randomUUID()` plus `new Date()` defaults mean the same input never produces the same
       record twice. Two more, from the operator's side: a PENDING exception reports
       `blocking: false` (blocking is `decision === 'deny'` and pending takes the warn branch), so
       the natural `if (evaluation.blocking) reject()` lets an unapproved exception through and the
       UI offers 'Acknowledge and renew exception review' rather than a wall — while a merely
       out-of-scope request DOES block, so the unapproved case is treated more leniently than the
       mis-scoped one; and the badge tones are inverted with respect to risk, with the scoped
       approval rendering 'warn' and the unscoped 'Global approval' rendering 'info'. Said plainly: within what it is the module is careful — it normalizes, dedupes,
       refuses an approval with no approver and a revocation before its approval, and renders an
       honest scoped-versus-global badge. The finding is that it is a RECORD of an exception rather
       than a CONTROL over one.
       NOT CLAIMED: nothing talks to a database, a queue, a mail server or an identity provider — the
       reporting line, subject owner and current digest arrive as three maps a deployment fills in.
       The hash chain is tamper-EVIDENT, not tamper-PROOF: defeating a rewriter who can reach every
       entry needs an external anchor at the seam where verifyChain is called. The floor assignment
       is this reference's decision, recorded rather than inferred. Full detail in
       exception-workflow.json. -->
  - [x] I5.16.a Define exception request, exact subject/version/gate/policy,
        requested bounded waiver, rationale, evidence, risk, compensating
        controls, requester, approver roles, term, and status. Evidence:
        exception schema.
  - [x] I5.16.b Declare nonwaivable child safety, legal/rights/consent,
        security, tenant isolation, human-authority, and other code-floor
        conditions. Evidence: nonwaivable policy tests.
  - [x] I5.16.c Validate requester/approver separation, role/scope, conflict of
        interest, evidence completeness, subject freshness, and approval quorum.
        Evidence: authorization tests.
  - [x] I5.16.d Record approve/reject/request-changes/withdraw/expire/revoke as
        immutable decisions linked to original measurements/verdict/evidence.
        Evidence: decision history tests.
  - [x] I5.16.e Apply an approved exception only to exact subject revisions,
        gates/targets/use/audience/territory and validity window; revalidate at
        each governed action. Evidence: scope escalation tests.
  - [x] I5.16.f Surface active/expired/revoked exception and compensating
        controls without changing the underlying failed/raw result. Evidence:
        projection tests.
  - [x] I5.16.g Trigger review on subject/policy/evidence change and automatic
        expiry/revocation actions plus blast-radius/notification. Evidence:
        lifecycle tests.

### I6 — Persistence, service API, and integration

- [ ] I6.1 Decide which existing Isis databases own briefs, plans, runs,
      variants, gates, rights, and releases. Evidence: ownership ADR prevents a
      duplicate shadow store.
      <!-- partial: a-d DONE. The decision is made and the prevention is live —
      ADR-I1 names one owner per record type and the shadow-table gate fails CI
      on any new duplicate. The headline: schema.prisma is NOT the whole Isis
      database (2 schema authorities, 39 physical tables, 8 declared by both with
      incompatible columns, 18/24 Prisma models with no call site, 10 connection
      pools so no transaction spans the split). Parent stays unchecked only
      because ADR-I1 is Proposed pending the named service owners' ratification
      (I6.1.e), matching how S0.6 was left. -->
  - [x] I6.1.a Inventory existing stores, schemas, repositories, services,
        tenancy models, transactions, retention, backup, and production data for
        each Isis aggregate. Evidence: current-state inventory.
        <!-- isis-persistence-inventory.json: current-state persistence inventory.
                                                                                                                                                                                                                                                                        24 Prisma aggregates over 1 schema (libs/isis/database/prisma/schema.prisma),
                                                                                                                                                                                                                                                                        accessed by 9 persistence services. ARCHITECTURE FINDING: 0 repository
                                                                                                                                                                                                                                                                        classes — Isis uses SERVICE-DIRECT Prisma access (no repository layer); 3
                                                                                                                                                                                                                                                                        PrismaClient instantiations in the persistence layer — directly relevant to
                                                                                                                                                                                                                                                                        I6.1's goal (prevent a duplicate shadow store; the ownership ADR must
                                                                                                                                                                                                                                                                        reconcile these 3 clients). 4 persistence files use transactions ($transaction/
                                                                                                                                                                                                                                                                        withTransaction), 7/24 aggregates are tenant-scoped (organizationId/tenantId
                                                                                                                                                                                                                                                                        columns: CanonicalAuditEvent/GeneratedOutput/GenerationJob/ModelRegistry/
                                                                                                                                                                                                                                                                        Pipeline/...), 4 aggregates carry soft-delete (deletedAt) + a RetentionPolicy
                                                                                                                                                                                                                                                                        aggregate exists for retention; backup is ops/infra-level (not modeled in
                                                                                                                                                                                                                                                                        application code, recorded honestly). PERF: scoped greps to the DB+services
                                                                                                                                                                                                                                                                        layer (not the whole huge Isis tree) — 12.7s → 0.57s; both-globs fix for
                                                                                                                                                                                                                                                                        top-level services/*.ts; tenancy counted from MODELS (organizationId columns)
                                                                                                                                                                                                                                                                        not filtered files. generate-isis-persistence-inventory.mjs + 6 tests, wired
                                                                                                                                                                                                                                                                        into inventory/verify + CI. I6.1.b (aggregate→store map) is now DONE and
                                                                                                                                                                                                                                                                        supersedes this note's Prisma-only view of the database: the physical census
                                                                                                                                                                                                                                                                        is 39 tables under two schema authorities, not 24 aggregates. -->
  - [x] I6.1.b Map brief, plan, run/job/stage, variant/output, gate measurement/
        verdict, rights/consent/provenance, and release records to exactly one
        system of record. Evidence: aggregate ownership matrix.
        <!-- isis-aggregate-ownership.json: the matrix, computed over PHYSICAL
        tables rather than Prisma models — because schema.prisma is not the whole
        database. Isis has TWO schema authorities pointed at ONE connection string
        (ISIS_DATABASE_URL ?? DATABASE_URL, 10 resolvers): Prisma (24 tables) and
        CREATE TABLE IF NOT EXISTS run by the services (23 tables), 39 physical
        tables in total. 8 table names are declared by BOTH — lineage_edges,
        model_registry, provenance, retention_policies, workflows,
        workflow_versions, workflow_templates, workflow_stars — where IF NOT
        EXISTS is a silent no-op, so the loser queries columns that do not exist:
        provenance is outputId/generatedAt vs output_id/generated_at with FKs to
        two different tables, retention_policies is keyed on id vs name, and
        workflow_stars shares not one column NAME across 6 vs 4 columns.
        13/23 service tables are created at runtime, not by a migration. 18/24
        Prisma models have no call site at all; the only live Prisma family is
        pipeline_state_* — which is also the only family missing from the
        committed generated/schema.sql. Ownership is assigned by a recorded rule
        ladder (R1 live access path, R2 cascade-durability as a FILTER, R3
        canonical-field coverage, R4 external, R5 unowned): 8/12 record types
        resolve to exactly one system of record, 0 contested, 4 are gaps (brief,
        variant, gate-measurement have no table anywhere; rights has three
        claimants and no access path). Costs are published, not hidden: the live
        run store (jobs) carries NO tenancy at all, losing userId/organizationId/
        projectId to the declared-only rival. Consent has no Isis home — the owner
        is iris ConsentRecord in a different database, referenced by consent_id
        with no FK. generate-isis-aggregate-ownership.mjs + 38 tests, wired into
        inventory/verify + CI. Mutation-tested 3x (135/163 killed); it found 4
        real defects in my own code — an identical-branch ternary, an unreachable
        R2 decision branch, an untested invariant gate, a vacuous single-database
        claim — all fixed. Surviving mutants are internal .sort() calls on
        already-ordered git output and one deliberately overlapping guard pair;
        each says so where it lives. I6.1.c/d and the I6.1.e ADR build on this. -->
  - [x] I6.1.c Define read model, derived index/cache, blob, audit, outbox, and
        analytics ownership separately from canonical mutation ownership.
        Evidence: data-flow diagram.
        <!-- isis-derived-ownership.json: role ownership scored against the SAME
        census I6.1.b resolves owners from (imported, not re-derived, and asserted
        equal), so the two artifacts cannot drift. Per role and per owner the
        question asked is whether the role has its own store, is carried INSIDE
        the canonical row, or is absent. 3/6 are separated (audit, outbox,
        read-model); 2 are embedded in the canonical row — derived-index-cache
        (stats_runs/stats_stars/total_versions in workflows, access_count in
        outputs, and a searchVector) and analytics (durationMs in the stage row) —
        so they cannot be rebuilt or invalidated without writing to the system of
        record. Blob is scored pointer-vs-payload rather than lumped: storage_key
        in a canonical row is correct design and is NOT flagged; the one real
        defect is pipeline_state_phase_artifacts.inlineData holding payload in the
        row. Coverage is the sharp part: 7 audit stores exist yet 4/7 owners
        (gate-verdict, release, run, stage) have no audit trail reachable from
        their table, and there is exactly ONE outbox (job_outbox) covering ONE
        owner, so 6 systems of record mutate with no transactional publication
        path — I6.4 builds that, it does not extend it. Links are labelled
        foreign-key vs name-convention because outputs→output_audit_logs is
        enforced by nobody. The diagram is emitted as mermaid generated from the
        12 edges. generate-isis-derived-ownership.mjs + 18 tests, wired into
        inventory/verify + CI. Mutation-tested: 36/51 killed, then the 9 surviving
        logic mutants were killed by extracting the verdict and the two invariant
        predicates as pure functions and exercising every branch — including
        pointer-only and absent, which today's schema never reaches. -->
        <!-- I6.1.d (consistency seams) and the I6.1.e ADR remain. -->
  - [x] I6.1.d Resolve cross-store transaction/consistency, identity/version,
        tenancy, retention/deletion, migration, failure, and recovery seams.
        Evidence: consistency decision record.
        <!-- isis-consistency-seams.json: all seven named seams, each with what
        was observed, the conflicts actually unresolved (13), and a decision
        recorded against them. The decisions follow from one measured fact: Isis
        opens 10 independent connection pools at the one database (9
        createPostgresClientFromUrl sites + 1 Prisma client), and a transaction
        belongs to a pool — so 10 of the owner pairs I6.1.b resolved CANNOT be
        written atomically together however much they share a database. Inside a
        pool the binding is opt-in: recordAuditLog() writes output_audit_logs
        through an executor DEFAULTING to this.db, so a caller who forgets the
        handle gets a row that survives the surrounding rollback. Identity: same
        physical table keyed two ways (retention_policies id vs name;
        workflow_stars id vs the (user_id, workflow_id) pair), and 4 owners carry
        no version/updated-at column so a lost update cannot be detected.
        Tenancy: FOUR shapes across 7 owners and 4 owners (gate-verdict, release,
        run, stage) with none at all. Retention: 6 owners cannot distinguish a
        deleted row from one that never existed, and the retention policy itself
        lives in a dual-authority table keyed two ways. Migration: 3/7 owners are
        created by a running service, 8 tables have two declaring authorities.
        Recovery: only `stage` can resume (ordinal); job_outbox's replay columns
        belong to the OUTBOX, not to the run, and the artifact does not credit
        them to it. generate-isis-consistency-seams.mjs + 21 tests, wired into
        inventory/verify + CI. Mutation-tested (41/77, then 5/5 and 6/10-with-3-
        sites-removed on targeted re-checks). It caught me FABRICATING a finding:
        a name filter reported `rowToOutboxEntry` — a row-to-object mapper — as a
        write escaping a transaction. A helper is now only a writer if its own
        body contains an INSERT/UPDATE/DELETE, and the same fix surfaced the REAL
        opt-in-executor hazard the name filter had missed. Also removed a dead
        `authorityOf` helper and an untestable `x && seen.add(x)` idiom. -->
  - [ ] I6.1.e Ratify an ADR naming owner/service/store per aggregate and
        explicitly prohibiting shadow canonical tables. Evidence: approved ADR
        and schema inventory check.
        <!-- partial: BOTH halves of the evidence exist; only the RATIFICATION is
        open. (1) The ADR is written: docs/adr/ADR-I1-isis-aggregate-ownership.md
        (Status: Proposed) names an owner per record type with the rule that
        chose it, records the derived-role split and the seam decisions, states
        the prohibition verbatim, and gives the four options considered with why
        each was rejected — every quantitative claim drawn from the committed
        evidence. (2) The schema inventory check is ENFORCING, not descriptive:
        scripts/v1-workbenches/isis-shadow-table-gate.mjs is a two-way ratchet
        wired into verify:inventory:v1-workbenches + CI. A NEW dual-authority
        table fails the build; a NEW runtime-DDL table fails; a system of record
        that loses its live access path fails; and PAYING DOWN a baseline entry
        without tightening the list fails too, so the ceiling cannot quietly
        become permission. The 8 dual-authority and 13 runtime-DDL tables are
        recorded as measured debt (I6.2 unwinds them; a gate cannot delete a
        table that has data in it). 11 tests, four of which force each violation
        class and one of which asserts the PROCESS exits non-zero — a report
        nothing acts on is not a gate. Unchecked because "ratify" is the named
        service owners' act, per the S0.6.f precedent. -->
- [ ] I6.2 Add only missing schema through expand/backfill/verify/contract
      migrations. Evidence: production-size rehearsal.
  - [x] I6.2.a Diff required contracts against ratified owner schemas and record
        each reusable field/table/index versus genuine gap. Evidence: schema gap
        report.
        <!-- isis-schema-gap.json: 131 fields across 14 COMMITTED contracts —
        the Zod payloads Isis actually PUBLISHES (libs/contracts/src/events/
        isis.ts) and the brief record the I2 workbench validates against —
        diffed field by field against the systems of record I6.1.b resolved
        (imported, not restated; the suite asserts the two agree). Four buckets,
        and the middle two are the point: 15 reusable-exact (add nothing), 19
        reusable-ELSEWHERE (the owner lacks it but a DECLARED-ONLY rival already
        models it, so it is a migration out of a table nothing reads, not new
        design), 23 genuine gaps, 74 belonging to a record type with no table at
        all. HEADLINE: Isis publishes projectId and userId on EVERY job event and
        the live owner (service:jobs) can store neither — both sit in
        prisma:generation_jobs, which nothing reads. Gaps carry a `nearestOnOwner`
        HINT (sizeBytes~size, filename~name) that deliberately does NOT equate
        them: a rename is still a column that does not exist, and the judgement
        belongs to whoever writes the migration. Index half is scored on the axis
        I6.3 makes mandatory — a tenant-LEADING index — and 4 owners are
        `blocked-on-missing-tenancy-column`, where adding an index is not the fix
        because the column does not exist. 4 record types need a table.
        generate-isis-schema-gap.mjs + 20 tests, wired into inventory/verify +
        CI. Mutation-tested 65/81, then 11/11 on a targeted re-check after
        extracting isServiceSourceFile/ownerStoreFor/isTenantLeading/indexStatus
        and the two meaningfulness predicates as pure functions. One survivor
        needed a better FIXTURE, not more code: the cross-database key had to be
        PRESENT in the census, or dropping the crossDatabase check still returned
        null through the lookup miss. -->
        <!-- I6.2.b-g (expand/backfill/verify/cutover/contract + a
        production-size rehearsal) need a live database and stay unchecked. -->
  - [ ] I6.2.b Add backward-compatible nullable/default-safe columns, tables,
        constraints, enum representations, and dual-read/write support without
        destructive rename/drop. Evidence: expand migration tests.
  - [ ] I6.2.c Backfill in resumable bounded batches with checkpoints,
        idempotency, throttling, tenant isolation, error quarantine, and
        observable progress. Evidence: backfill integration tests.
  - [ ] I6.2.d Verify row counts, null/uniqueness/FK/check invariants, sampled
        semantic equality, hashes, read paths, and lag before cutover. Evidence:
        verification report.
  - [ ] I6.2.e Switch readers/writers through a reversible compatibility phase
        and monitor errors/latency/data divergence. Evidence: cutover rehearsal.
  - [ ] I6.2.f Contract old schema only after rollback window and dependent
        version inventory prove no consumers remain. Evidence: contract gate.
  - [ ] I6.2.g Rehearse expand/backfill/verify/cutover/rollback/contract against
        production-size/sparsity/skew with resource and lock measurements.
        Evidence: migration rehearsal report.
- [ ] I6.3 Add indexes for actor, tenant, project, workflow, model, provider,
      status, asset kind, dates, gate state, and lineage. Evidence: query plans.
  - [ ] I6.3.a Capture representative list/detail/search/filter/lineage/blast-
        radius/release query shapes, cardinalities, sort orders, and latency
        targets from production-scale fixtures. Evidence: workload catalog.
        <!-- partial: isis-workload-catalog.json covers SHAPES and SORT ORDERS
        (and more) but NOT cardinalities or latency targets, so the cell stays
        unchecked. 73 read paths across 8 service files and 18 tables in 7 shape
        classes, with filter columns, sort columns, pagination style and index
        support, extracted per SQL STATEMENT (not per method) so a method issuing
        two queries cannot hand both tables every predicate either uses. Index
        support is three-state: leading-index, partial-index-predicate (a
        `WHERE deleted_at IS NULL` on the index serves that filter), unindexed;
        sorts use the COMPOSITE rule (an index on (tenant_id, sequence) walks
        ORDER BY sequence when tenant_id is filtered). Predicates are split
        MANDATORY (inline in the statement, or seeded into the `conditions` array
        literal — applied by every execution) vs OPTIONAL (`conditions.push`, or
        pushed by a scope helper the method hands its array to — applied per
        request), because only the mandatory half belongs in a fixed index.
        Result: 38 filter columns, 24 led by an index, 1 partial, 13 leading none
        of their own, and exactly 1/55 filtered reads has NO indexed entry point:
        `queryProvenance` on provenance(model, seed, workflow), the blast-radius
        question, which scans. 1 unserved sort and 1 deep-page risk, both the
        webhook list. Cardinalities and latency targets are DECLARED ABSENT with
        the reason: they are properties of data and of product commitments, not
        of query text, and a guessed row count would justify an index nobody
        measured. Both need I6.3.e (a database). generate-isis-workload-catalog.mjs
        + 31 tests, wired into inventory/verify + CI. Mutation 55/86 then 10/11
        targeted. It caught FIVE false findings in my own first draft, each
        removed: `FROM unnest(tags)` and `FROM avg(x)` catalogued as TABLES; a
        GIN index and every partial-index predicate unread, so indexed columns
        read as unindexed; PRIMARY KEY and UNIQUE-constraint implicit indexes
        unread; `ORDER BY jobs.created_at` recorded as a sort on a column called
        `jobs`; and `(?:=|...)\b` matching nothing for `WHERE x = $1` — no word
        boundary after `=` — which silently dropped most predicates in the
        codebase. -->
        <!-- CORRECTED while building I6.3.c, which is what found the bugs: the
        catalogue went 53 -> 73 queries and 16 -> 18 tables. THREE more
        extraction faults, all silent, all of which made a real read path look
        like a table nobody queries. (1) The method-body scanner took the FIRST
        `{` after the signature, which is the one in `query: Q = {}` — so 19
        method bodies parsed as an empty object literal and vanished, including
        every paginated `list`, the outbox claim loop `claimOutboxEntries`, the
        dead-letter feed, and BOTH audit-log readers. The body brace is now found
        by skipping the parameter list with balanced parens and then the return
        annotation, with `;` meaning a declaration that must not adopt the next
        method's body. (2) Predicates SEEDED into the conditions array —
        `const conditions = ['project_id = $1', 'deleted_at IS NULL']` — were
        invisible because only `conditions.push` was read, and the seeded ones
        are exactly the mandatory scoping predicates; that reported the webhook
        subscription list as an unscoped unindexed status scan when it is
        project-scoped and served by a partial index on that pair. (3) Conditions
        a method DELEGATES to a helper (`this.appendAccessScopeConditions(
        conditions, ...)`) were unread, which is the whole of tenancy on the
        output reads. (4) A plain `WITH x AS (...)` was classified `lineage`,
        filing the queue dispatcher under a shape class meant for graph walks;
        only `WITH RECURSIVE` is lineage now. (5) Found while building I6.3.d:
        predicates introduced by `OR` were invisible, because the extractor
        anchored on WHERE/AND only. That hid `target_id` in the one bidirectional
        lineage read — `source_id = ANY(...) OR target_id = ANY(...)` — so the
        REVERSE lineage column read as one no query ever touches. Disjuncts are
        now a class of their own and deliberately NOT mandatory: a composite
        spanning both branches of an OR serves neither, since the row may satisfy
        the other branch. -->
        <!-- I6.3.b-d are index DESIGN cells that build on this catalog;
        I6.3.e-f need a database and migrations. -->
  - [x] I6.3.b Design tenant-leading composite/partial/covering indexes for
        actor/project/status/kind/date queue and pagination access paths.
        Evidence: index rationale.
        <!-- isis-index-rationale.json: 50 distinct access paths from the I6.3.a
        catalogue (one per (table, filters, sorts) triple — the same shape read
        from three methods is ONE index question), each with a status and a
        rationale that says why. Candidates are built tenancy → equality filters
        → sort column, because that is the order an index can be walked, and
        capped at 4 columns with the overflow REPORTED rather than truncated.
        Only MANDATORY filters enter a composite: a list endpoint that MAY filter
        by eight things does not want an eight-column index, so builder-supplied
        (per-request) predicates are excluded — that alone turned an 8-column
        proposal into (tenant_id, created_at). Coverage is by PREFIX not set
        membership, since an index on (status, tenant_id) does NOT serve a query
        leading with tenant_id. Result: 3 proposals, 20 already covered — most of
        them by a PRIMARY KEY, because a read filtering the whole PK resolves one
        row and a tenant-leading composite in front of that adds nothing — 19
        blocked on 10 tables with NO tenancy column, where the fix is the column
        (I6.2), and 8 on 5 tables (model_registry, model_registry_audit_log,
        outputs, provenance, workflows) in a status this pass ADDED:
        `blocked-on-untenanted-query`. generate-isis-index-rationale.mjs + 22
        tests, wired into inventory/verify + CI. Mutation 35/50 then 9/9 targeted
        after extracting preferStore / isPaginated / filtersPrimaryKey and the
        invariant predicates. Two of my own errors caught by reading the output:
        the first draft picked whichever authority came first for a
        dual-authority table, reporting `provenance` as having no tenancy column
        when its LIVE half has tenant_id, project_id and owner_id; and it
        proposed (tenant_id, id) composites in front of primary-key lookups. -->
        <!-- CORRECTED on the I6.3.a re-extraction, and the correction is the
        finding: on the richer catalogue this cell proposed NINE indexes and all
        nine led with a tenancy column the read never constrains — not on every
        execution, not per request, not even as a sort. A B-tree is entered at
        its leading column, so every one of them would have served its query not
        at all; four of that class had already shipped in the previous pass. The
        rule is now an invariant (`everyProposalIsUsableByItsQuery`) and the
        paths are reported `blocked-on-untenanted-query`: the table HAS tenancy,
        this read crosses tenants, and the fix is the query rather than an index
        that would read as a fix while changing nothing. The
        `somethingIsProposed` invariant went with them — requiring at least one
        proposal was an assumption about the answer, replaced by
        `statusesDiscriminate` (>=3 buckets populated), which checks the method
        instead. A surviving proposal now also records `usableOnlyWhenScoped`
        when its tenancy predicate is per-request: that index serves the SCOPED
        call, and the same endpoint called without a scope still reads globally.
        1 of the 3 is in that state. -->
  - [x] I6.3.c Design workflow/model/provider/version/status/time indexes for
        routing, operations, blast radius, and cost analysis. Evidence: query
        plan fixtures.
        <!-- isis-plan-fixtures.json: 30 access paths on the OPERATOR axis — one
        per (table, MANDATORY filters, sort) triple, since the plan depends on
        what every execution constrains — classified into the four purposes by
        the columns they constrain, never by method name. I6.3.b was the
        tenant-leading axis; these are asked ACROSS tenants and lead with
        workflow/model/provider/version/status/time.
        THE EVIDENCE IS A DERIVED PLAN, NOT A MEASURED ONE, and the artifact says
        so in every row: `measured: false`, no EXPLAIN text, no cost, no row
        estimate, no timing, and a test that fails if any of those words appear.
        The rule is stated once and applied everywhere: a B-tree is entered at
        its leading column and walked while each successive column is pinned by
        equality; the longest such prefix is the entry point; an ORDER BY is free
        only when its column follows a fully-pinned prefix; a PARTIAL index
        applies only when the read asserts its predicate, and the predicate it
        enforces filters exactly as a key column would. Where more than one index
        is viable, ALL of them are published with `chosenBy` saying the winner is
        a structural tie-break and that the real choice is a cost decision for
        I6.3.e — publishing only the winner would present a guess as a
        derivation. Result: 14 already-planned, 6 proposed (each with runnable
        `CREATE INDEX CONCURRENTLY` DDL, kept only when it lengthens the usable
        prefix or removes the sort step), 10 that no index can help, 4 sequential
        scans. Equality columns are ordered ALPHABETICALLY and the artifact says
        why: every order gives the same usable prefix, so all serve the query,
        and which is CHEAPEST needs selectivity nobody has measured — ordering by
        a guessed selectivity would publish a decision nobody made. Soft-delete
        flags are hoisted into the index PREDICATE rather than the key, because a
        read that always asserts `deleted_at IS NULL` gets one value on every
        entry it can use: it discriminates nothing there and only widens the
        tree. That is a structural argument, not a claim about how many rows are
        deleted, and it is what the webhook and workflow indexes already do.
        TWO HEADLINES, both about questions Isis cannot answer rather than
        indexes it is missing. (1) cost-analysis has ZERO read paths, and the
        live `provenance` table records `cost` and `generation_duration` next to
        provider, model, workflow, tenant_id and generated_at. The data is
        written on every generation and never queried, so there is nothing to
        plan and no index to design: an index serves a query, and this question
        has none. (2) the provider dimension has zero access paths while
        `provenance.provider` and `isis_provenance_bundles.{endpoint_id, region}`
        are written on every row — no failover query, no provider blast radius,
        nothing to index. Coverage is reported per purpose AND per dimension, and
        "recorded but never constrained" is computed per TABLE, because pooling
        columns across tables would report a dimension as covered on the strength
        of a different table using the same word. 9 of the 30 reads already cross
        tenants; the artifact states that the proposed indexes make an existing
        global read cheaper rather than opening a path that did not exist, which
        is the distinction I6.3.d has to keep true. The one persistence path this
        extraction cannot see — the Prisma-delegate pipeline_state_* repository —
        is DECLARED, so its absence does not read as a table nobody queries.
        generate-isis-plan-fixtures.mjs + 30 tests, wired into inventory/verify +
        CI. Building this cell is what found the four I6.3.a extraction faults
        and the nine unusable I6.3.b proposals recorded above. -->
        <!-- I6.3.e-f need a database and stay unchecked. -->
  - [x] I6.3.d Design gate subject/profile/state/freshness and forward/reverse
        lineage indexes without introducing cross-tenant paths. Evidence:
        gate/lineage plans.
        <!-- isis-gate-lineage-plans.json: 22 access paths (21 gate, 1 lineage),
        each with a DERIVED plan (measured=false, no EXPLAIN, no cost or row
        estimate) and 3 proposals. The cross-tenant clause is taken literally
        first and then stated properly: an index never changes WHICH rows a query
        returns, only how they are found, so no index can introduce a
        cross-tenant read. What it can do is make a cross-tenant path cheap
        enough to be taken. So every proposal is held to two branches — lead with
        tenancy where the table has it (asserted by `noProposalWidensScope`), and
        where it has none, say `unavoidablyCrossTenant` and NAME what scopes the
        read instead. Four scope states, and the middle two are the point:
        tenant-column (7 paths), guarded-in-sql, guarded-in-application-code (1),
        unscoped (14). A predicate in the SQL and a filter applied after the rows
        come back are both "scoped" and are not equally scoped.
        THE LINEAGE FINDING: the cross-project lineage policy
        (`assertCrossProjectLineageAllowed`) is called from 2 sites and BOTH are
        write-path — the guard runs when an edge is CREATED, never when the graph
        is READ. On the read side `getLineage` walks parent/child ids with the
        caller's OPTIONAL scope, and the `lineage_edges` query itself carries no
        tenancy predicate at all: it reads edges across every tenant and the
        process discards the out-of-scope ones in JS afterwards. lineage_edges
        has no tenancy column, so that cannot be fixed with an index — an index
        there makes more out-of-scope rows arrive faster. Guard call sites are
        LOCATED in the source (not asserted), classified read/write-path by
        whether the enclosing method issues writes, and — the correction that
        mattered — attached only to tables that method actually reads. The first
        draft listed the output-lineage guards as protecting the outbox
        dead-letter feed, which is a safety claim about code that never runs
        there.
        THE GATE FINDING: all four gate dimensions have read paths, and that is
        the generous reading. 48 gate columns on live tables are constrained by
        NO read — including every purpose-built gate and profile column Isis has
        (gates, gate_ids, consistency_profile, lens_profile, asset_family,
        pipeline_stage, output_package_schema) and the promotion state machine's
        own status/from_status/to_status. The single profile column any read
        constrains is the staging `environment`. 60 indexes on tables Isis reads
        are entered by no catalogued read, 17 of them on gate columns — the
        mirror image of a missing index, write cost paid for reads that do not
        exist. The pipeline_state_* entries in that list are DECLARED as a limit
        of the extraction (they are read through Prisma delegates, which a
        SQL-statement catalogue cannot see) rather than left to read as a finding.
        A fifth I6.3.a extraction fault fell out of this cell: predicates
        introduced by `OR` were invisible, so the ONLY bidirectional lineage read
        in Isis — `source_id = ANY(...) OR target_id = ANY(...)` — was catalogued
        as forward-only and the REVERSE lineage column read as one no query
        touches. Disjuncts are now extracted into their own class and kept OUT of
        `mandatoryFilterColumns`, because a composite spanning both branches of
        an OR serves neither; Postgres combines separate indexes instead.
        generate-isis-gate-lineage-plans.mjs + 17 tests, wired into
        inventory/verify + CI. -->
        <!-- I6.3.e-f need a database and stay unchecked. -->
  - [ ] I6.3.e Measure read latency, rows scanned, index size, write
        amplification, lock/build impact, and redundant-index overlap at
        production scale. Evidence: before/after benchmark.
  - [ ] I6.3.f Create/drop indexes with online/concurrent safe migrations and
        regression-plan assertions. Evidence: migration/query-plan tests.
- [ ] I6.4 Implement transaction-bound audit and outbox writes for mutations.
      Evidence: rollback and retry integration tests.
  - [x] I6.4.a Define canonical mutation/audit/outbox envelope with actor/
        authority, tenant/project, request/correlation/idempotency, aggregate/
        revision, operation, before/after references, reason, policy, and time.
        Evidence: event schemas.
        <!-- isis-mutation-envelope.json: the ledger's own nine groups taken
        verbatim as the field list (15 fields, 12 required), each carrying the
        specific question that becomes unanswerable without it — a field nobody
        can name a failure for is decoration. Diffed against every persisted
        mutation-record surface, and the first finding is how many there are:
        NINE tables plus the JSON payload the outbox stores. audit_log and
        canonical_audit_events on the Prisma authority; workflow_audit_logs,
        model_registry_audit_log, output_audit_logs, generation_event_ledger,
        isis_provenance_ledger, workflow_promotion_transitions and job_outbox on
        the service-DDL one. NONE of the ten carries all 12 required fields.
        Coverage is four-valued and the middle two are the whole argument:
        `carried` (the column exists and is NOT NULL), `optional` (the same
        column, nullable — the schema's PERMISSION to omit it), `partial` (a
        near-miss, published with the reason it is not the thing), `absent`.
        71 of 150 cells are absent and 26 more are near-misses, which is why the
        gap has stayed invisible: every uncarried field has a lookalike column
        somewhere, so the schemas read as though they cover it.
        THE FIELDS NOTHING GUARANTEES: `authority` appears nowhere at all except
        as `actorRole` on one surface — a role is a property of the actor, not
        the grant the action was taken under, so when a grant is revoked nothing
        identifies what it authorised. `tenant` exists on four surfaces and is
        NULLABLE ON ALL FOUR: there is no surface in Isis on which a mutation
        record is guaranteed to say which tenant it belongs to. `policy` exists
        on exactly one surface, nullable. `revision` is carried by none — the two
        `sequence` columns are per-parent ordinals that order the records without
        ever naming the version of the aggregate. `idempotency` is carried ONLY
        by the outbox; all eight audit surfaces have nothing but a per-row id,
        which is the opposite of an idempotency key, so one retried command
        writes one outbox row and N audit rows.
        AND THEY CANNOT BE REASSEMBLED: 0 of 44 pairs of distinct surfaces share
        a correlation column that is non-null on both sides. The three writes
        I6.4.b must make atomic could not afterwards be recognised as having
        belonged to one act even if they were.
        Two nominated surfaces fail the structural definition of an immutable
        mutation record, and they fail it differently. isis_provenance_ledger has
        no actor column and no operation column: it records that a bundle was
        appended, never who changed what. job_outbox is revised in place as
        delivery progresses — correct for a delivery work-item, and precisely why
        it cannot stand in for the audit write — and its actor and operation live
        inside the JSONB payload, so no SQL predicate can select the outbox by
        who acted or by what operation is pending.
        Sharpest single column: canonical_audit_events.resourceType is NOT NULL
        while resourceId is NULLABLE, so the canonical audit event is guaranteed
        to say a workflow changed and permitted to stay silent about which one.
        Surfaces are DISCOVERED — nominated by name, then confirmed against five
        structural checks — and a reverse sweep lists every table meeting the
        definition WITHOUT being nominated (0), so the roster cannot be quietly
        short. No database was read: every column, type and nullability is parsed
        from committed DDL, and the JSON envelope from its zod schema.
        generate-isis-mutation-envelope.mjs + 53 tests, wired into
        inventory/verify + CI. Mutation-tested (304 mutants); fixing survivors
        removed a VACUOUS invariant — `optionalIsNeverSatisfied` was
        `state === 'optional'` implies `not state === 'carried'`, a tautology no
        input could falsify — replaced with one that checks each state against
        the nullability of the columns it names. The sweep also caught the type
        parser swallowing constraint text (`INTEGER NOT NULL DEFAULT 0` as a
        type), which matters because the JSON-copy-vs-reference call is made on
        the type. -->
        <!-- Two extraction faults this cell shipped and then caught in its own
        spot-check, both of which had made Isis look BETTER than it is: (1) the
        envelope's fields are published under their full path, so `metadata.userId`
        normalised to `metadatauserid` and matched no vocabulary — the outbox
        envelope was reported as carrying no actor, no project and no correlation
        id when it declares all three; vocabularies now match the LEAF. (2) A
        field with a type column and an id column was read as `carried` whenever
        the TYPE was NOT NULL, so canonical_audit_events counted as carrying
        `aggregate` and `actor` on every row while both identifiers are nullable.
        Fields now declare which of their columns IDENTIFY, and only a required
        identifying column earns `carried`. -->
        <!-- I6.4.b-f are write-path and integration cells: they need the audit
        and outbox writes located at their call sites, and a database. -->
  - [ ] I6.4.b Write aggregate mutation, immutable audit record, and unpublished
        outbox row in the same owner-database transaction. Evidence: atomicity
        tests.
  - [ ] I6.4.c Ensure rollback leaves none of the three writes and retry with
        the same idempotency key creates no duplicates. Evidence: rollback/retry
        integration tests.
  - [ ] I6.4.d Publish outbox rows with leases/fencing, at-least-once delivery,
        stable event IDs/order keys, retry/backoff, and dead-letter handling.
        Evidence: publisher crash tests.
  - [ ] I6.4.e Make consumers idempotent against duplicate/out-of-order events
        and prevent stale projections from authorizing action. Evidence:
        consumer ordering tests.
  - [ ] I6.4.f Apply property classification/redaction to event projections and
        audit access while preserving authorized forensic evidence. Evidence:
        event leak tests.
- [ ] I6.5 Implement brief/plan/run/variant read APIs with deterministic
      pagination. Evidence: contract tests.
  - [x] I6.5.a Define versioned list/detail/filter/sort/include/projection
        contracts for briefs, plans, runs/stages/attempts, variant sets,
        outputs, decisions, and lineage. Evidence: API schemas.
        <!-- isis-read-contracts.json: the ledger's own seven dimensions
        (version, list, detail, filter, sort, include, projection) across its own
        nine record kinds, each dimension carrying the question that becomes
        unanswerable without it, diffed against 127 GET routes extracted from
        committed source — 51 on the Isis services (Hono) and 76 on the operator
        BFF (Fastify). 14 of 63 cells are served.
        THE HEADLINE: SIX of the nine record kinds have no GET route of any kind
        on either family — brief, plan, stage, attempt, variantSet, decision.
        Not an unwired API in front of a working store: unimplemented end to end.
        Only run, output and lineage are readable at all, and even there the
        ledger's "runs/stages/attempts" triple collapses to one flat record — the
        served run is a Job, renamed by toWorkflowExecution, with no stage or
        attempt substructure beneath it.
        A GET ROUTE EXISTING IS NOT A RECORD BEING READABLE, so every route is
        classified by what its HANDLER does, four ways: reads-store (59),
        static-context (40 — returns a module-level constant, the same answer for
        every caller), not-configured (8 — fail-closed with a machine-readable
        reason) and other (20). Only the first returns a record, and an invariant
        enforces that only a store-backed route can make a dimension `served`.
        The not-configured four are counted APART from absence deliberately: a
        route that refuses to fabricate a record it cannot produce is an honest
        seam, not a defect and not a stub, and the shape being decided while only
        the authority is unbound is a different amount of remaining work.
        Collapsing all four into "has an endpoint" is how this surface came to
        look finished.
        NO record kind serves `projection` — not one route declares a field
        selection anywhere, so every read returns the whole record and field-level
        authorization (I6.5.b) has nothing to hook into but post-hoc removal.
        Only lineage serves `include`, and only via the lineage read's bounded
        `depth`. And 15 of 127 routes validate a declared query contract at all;
        the other 112 accept whatever they are given.
        generate-isis-read-contracts.mjs + 42 tests, wired into inventory/verify
        + CI. Mutation-tested (208 mutants, 190 killed); fixing survivors added
        the version-dimension cases, which had no test at all. -->
        <!-- Cross-checked against an INDEPENDENT survey of the same surface
        before publication, which is what caught four silent extraction faults,
        every one of which had made the surface look better or larger than it is:
        (1) `PROVIDERS.get('runpod')` was extracted as a route — the GET pattern
        now requires a leading `/`, and the service route count fell to 51,
        matching the independent count exactly; (2) Hono sub-routers write leaf
        paths (`outputs.get('/')`) and their noun lives in the app.ts mount chain
        (`apiV1.route('/outputs', outputs)` under `app.route('/api/v1', apiV1)`),
        so before resolving mounts `output` had ONE route instead of eight and
        `run` two instead of sixteen; (3) the BFF's `output-lineage` segment
        matched neither `output` nor `lineage` until segments were split on their
        hyphens, dropping both routes of that module; (4) `direction`
        (ancestors/descendants/both) was classified a SORT key, which reported
        lineage as having an ordering contract when no lineage read accepts one —
        a generous vocabulary manufacturing the coverage it is meant to
        measure. -->
        <!-- This cell judges neither pagination determinism (I6.5.c) nor
        authorization (I6.5.b), and says so in notDerivableFromSource rather than
        letting silence read as approval. -->
        <!-- CORRECTED while building I6.5.b, which is what found the fault: the
        BFF was swept as `src/routes/admin-isis-*.ts`, which is a FILENAME
        CONVENTION and not a boundary. Six Isis-record GET routes register
        outside it — `src/isis/*.ts` and `src/generation/*.ts` — and the sweep
        counted 121 routes where there are 127. The 6 are not filler: two of them
        (`GET /v1/generation/jobs`, `GET /v1/generation/jobs/:jobId`) are the ONLY
        run reads in the repository that filter by tenant at all, so the omission
        made the surface look emptier than it is — the same fault as making it
        look fuller, in the other direction. The existing family invariant could
        not catch it, because it only asks that both families be reached and one
        glob per family satisfies that; `everyRouteGlobMatchedAFile` is new and
        asserts each declared glob individually reached a committed file. Totals
        moved: static-context 39→40, not-configured 4→8, other 19→20, operator-BFF
        70→76. reads-store stayed at 59 and the served-cell count stayed at 14 —
        the six new routes serve no dimension, because none of them reads a store
        under this cell's definition (they read an in-process snapshot-backed
        collection, which I6.5.b resolves separately and publishes as a disagreement
        with these dispositions rather than editing them here). -->
  - [ ] I6.5.b Enforce tenant/project/actor/audience/object/property
        authorization before query and after joins/includes. Evidence:
        authorization tests.
        <!-- isis-read-authorization.json: the ledger's six subjects against the
        READ PATH of every GET route in the I6.5.a sweep — the handler slice plus
        every method body reachable from it through the file's own imports, to
        depth 3. A route is only in the matrix if its call chain terminates in a
        record source: 56 of the 127 swept routes do, 47 on the Isis services and
        9 on the operator BFF, for 336 cells.
        THE HEADLINE: NOT ONE route in Isis binds tenant. Every tenant predicate
        on the surface is CONDITIONAL — `if (scope?.tenantId)` in the output
        registry, `tenantId === null ||` in the operator BFF — which is isolation
        for callers who carry the claim and unrestricted access for callers who
        do not. Both read as tenant scoping in a diff, which is why this cell
        counts `conditional` apart from `bound` rather than with it, and why a
        subject can be everywhere in the code and nowhere in force.
        Worse than absence is `caller-supplied`: 2 cells where the predicate
        exists and its value comes from the REQUEST (`if (query.organizationId)`,
        `if (query.publicOnly) → is_public = true` on the model list), so the
        caller chooses the scope and omitting the parameter WIDENS the read.
        44 of the 56 record reads carry no credential-derived tenant, project,
        actor or audience predicate at all — including reads of run, output and
        lineage. The run list is `SELECT * FROM jobs` with a WHERE clause built
        only from status, type and workflowId — all supplied by the caller — and
        the `jobs` table those routes query has no tenant_id, project_id or
        owner_id COLUMN to bind even if one wanted to: `JobListOptions`, the type
        the query is built from, has no field for one either. The Prisma
        authority declares a DIFFERENT physical table, `generation_jobs`, WITH
        userId/organizationId/projectId and indexes on all three — the scoped
        twin is the one nothing reads, which is the I6.1 two-schema finding
        arriving as an authorization hole.
        The second enforcement point is the surface's one good result: of the 3
        reads that follow edges out of the rows they selected, all 3 re-apply the
        scope to what they reached (the lineage BFS re-scopes per node and
        filters expanded parent/child ids), and 0 do not.
        38 of 56 reads select every column, so property-level authorization has
        nothing to hook into — no route binds it, and none binds audience either.
        generate-isis-read-authorization.mjs + 56 tests, wired into inventory/
        verify + CI. Mutation-tested: 260 mutants, 229 killed. Fixing survivors
        is what added the mixed-list cases this cell most needed — that ONE
        unconditional predicate makes a subject `bound` however many conditional
        ones sit beside it (`.every`, not `.some`), that a filter must not demote
        a bound predicate, that a guard-only cell says something different for
        the actor than for a scope, and that MIN_WHY_CHARS is an inclusive bound.
        The 31 survivors are equivalent mutants of three shapes, each checked by
        hand: `?? → ||` where the left operand is a regex match or a non-empty
        string and can never be falsy-but-defined; `>= → >` on caps the current
        data never reaches (400-line bodies, 60 bodies per route, depth 3); and
        `> 0 → >= 0` inside finding-string ternaries whose counts are non-zero. -->
        <!-- Mechanism matters and is published per cell: query-predicate (the
        value is in the statement that selects rows), post-fetch-filter (the rows
        are fetched and then narrowed or 404'd — real, but it protects what is
        RETURNED, not what was read, counted or logged), handler-guard (decides
        WHETHER you may call, never WHICH rows come back), none. A guard is the
        right answer for `actor` and never for `tenant`. -->
        <!-- Building this cell found faults in its own classifiers, every one of
        which made the surface look BETTER than it is, and each is now a fixture
        test: `AND (` inside a SQL string extracted as a call to a function named
        AND, whose five same-named "declarations" dragged a provenance WRITER
        onto the read path of routes that never call it; `async (c) => {` matched
        as a declaration, pulling every other handler in a route file onto every
        route; a bare `resolveOwnerId(c);` statement read as a second declaration;
        `): Promise<{ items: Job[] }> {` rejected as not opening a body, which
        hid the job store and with it the largest finding; `SELECT id, project_id
        … WHERE` counted as a project predicate when the column was in the SELECT
        list; `if (!ownerId) throw` counted as a row filter rather than a
        credential guard; and a POST route registered between two GETs read as
        part of the GET above it (I6.5.a cuts handler slices at the next GET,
        which is right for counting routes and wrong for reading one) — that last
        one alone had inflated the read surface from 56 routes to 91.
        One fault ran the other way and is recorded because under-reporting is
        also wrong: `this.store.get(id)` was unresolved, because the receiver is
        a class FIELD and the probe only looked for a `const`. That made the
        Postgres job store unreachable from `JobService.getStatus` and dropped
        real unscoped reads out of the matrix entirely — 53 routes where there
        are 56.
        The fail-closed rule needed the same care in the other direction: a
        predicate that is conditional in the callee is unconditional at the route
        when the handler gets the value from a resolver that THROWS, so
        `resolveOwnerId` makes actor `bound` even though the store method is
        written defensively. Scoping that check to the subject it resolves is
        load-bearing — asking whether ANY body on the path throws erased every
        conditional on the surface, since all of them reach `requireAuth`. -->
        <!-- Two invariants were written and deleted: one asserting some cell is
        `conditional` alongside some `bound`, one asserting some route expands.
        Both are assumptions about the ANSWER — a surface where nothing is
        conditional is a finding, not a broken artifact. What must hold is that
        the classifier CAN produce each state, which is a property of the code
        and is tested against fixtures instead. -->
        <!-- 21 routes are classified differently here than by I6.5.a's handler
        disposition, and each is published with both verdicts rather than
        reconciled: I6.5.a asks what a handler AWAITS, this cell asks what its
        chain terminates in, so a handler awaiting a service whose method returns
        a constant is reads-store there and `constant` here. This cell judges
        neither pagination (I6.5.c) nor whether the value bound is the CALLER'S
        own — a header-supplied tenant id and an authenticated claim are
        indistinguishable in source — and says so in notDerivableFromSource
        rather than letting silence read as approval. -->
        <!-- The committed authentication posture of each service is published
        beside the matrix (`allowAnonymous: NODE_ENV === 'development'`,
        `requireProjectScope: true`, middleware on `/api/*`), because a
        `conditional` cell reads differently depending on whether something in
        front already refuses credentials with no claim — and because that answer
        is not the same in every deployment. -->
        <!-- UNCHECKED, and it was briefly checked in error — recorded here
        because the reasoning is the point. This cell's verb is ENFORCE and its
        named evidence is AUTHORIZATION TESTS. What exists is the matrix above:
        a source-derived measurement of what is enforced today, which is the
        thing the enforcement will be written against and is NOT the enforcement.
        No production code changed and no authorization test was added, so the
        named evidence does not exist and the box does not flip. Publishing a
        thorough artifact is not the same as doing the task the artifact is about.
        What it would take, precisely: for the output registry the columns exist,
        so binding tenant/project unconditionally is a real but bounded change to
        `appendAccessScopeConditions` and its callers, plus a cross-tenant test
        per read. For the `run` kind it is BLOCKED — the `jobs` table those
        routes query has no tenant_id/project_id/owner_id column, so there is
        nothing to bind until the expand/backfill/contract work in I6.2.b-g runs,
        and I6.2.b-g need a database. The matrix names the 44 unscoped reads and
        the exact predicates a cross-tenant test should target, so the work is
        specified; it is not done. -->
  - [x] I6.5.c Implement allowlisted filters/sorts with stable unique tie-break,
        bounded page size, opaque signed cursor, and snapshot/continuation
        semantics. Evidence: pagination mutation tests.
        <!-- `@isis/read-pagination` (libs/isis/read-pagination) + 54 tests,
        mutation-tested: 194 mutants, 174 killed (90%). Unlike I6.5.b this cell's
        verb is satisfiable without a database — a cursor, an allowlist, a
        tie-break and a page bound are pure functions — so it is IMPLEMENTED, not
        measured, and wired into a real read path
        (`GET /api/v1/outputs/page`, 10 route tests).
        The five obligations, each with the failure it prevents:
        ALLOWLIST — the caller names a PARAMETER and the contract decides the
        column, so `sortBy=id;DROP TABLE outputs` is refused rather than reaching
        the query builder. An undeclared filter is REFUSED, not ignored: ignoring
        it is how a caller comes to believe a narrowing happened that did not.
        TIE-BREAK — every ORDER BY ends in the contract's unique column. Without
        it two rows sharing a `created_at` have no defined order between them, so
        LIMIT/OFFSET shows one twice and skips another; the walk test paginates a
        10-row table whose middle FIVE rows share one timestamp and asserts every
        row appears exactly once, at six page sizes, in both directions.
        BOUNDED PAGE — an over-large page is REFUSED, not clamped. A silently
        clamped page turns "there are no more rows" into a guess.
        SIGNED OPAQUE CURSOR — HMAC-SHA256 over a canonical body, constant-time
        compare, and the payload is re-validated after verification because a
        signature proves the bytes were not edited and not that they mean
        anything. FAIL-CLOSED with `not_configured` when no secret is bound: a
        default secret would issue cursors that verify in a service that cannot
        distinguish a forged one, which is a guarantee in name only.
        SNAPSHOT/CONTINUATION — the cursor pins version, resource, filters hash,
        sort, direction and page size, and a continuation disagreeing with any of
        them is refused rather than silently changing the scan. Every page,
        INCLUDING the first, is bounded by the snapshot the scan opened at, so a
        row written mid-scan cannot shift the pages; the walk test inserts one
        mid-scan and asserts it neither appears nor displaces anything. -->
        <!-- Two things are documented rather than hidden. A row BACKFILLED
        behind the cursor is never revisited by a forward keyset scan — that is
        the price of stable pages, and there is a test asserting it as expected
        behaviour rather than leaving it to be discovered. And a nullable sort
        column cannot use a row-value comparison: `(a, b) < (NULL, x)` is NULL,
        not false, so the page comes back empty forever. `SortSpec.nullable` is
        mandatory and the predicate is expanded, with the NULLS placement in the
        ORDER BY matching what the predicate assumed — the two disagreeing is how
        a keyset scan skips rows. -->
        <!-- Mutation survivors (20 of 194) are all equivalent and were each
        checked: 7 mutate TYPE annotations, erased at runtime; 6 are `?? → ||` on
        `spec.allowed`, where the fallback `[]` is truthy so both agree; 5 are
        `|| → &&` in guards whose second disjunct is unreachable once the first
        is false under the type system; 1 reorders a comparator that only has to
        be deterministic; 1 is a union's unreachable default branch. Fixing the
        REAL survivors is what added the wrong-TYPE cases — a query string can
        deliver an array or an object for a repeated parameter, and a guard that
        tests only emptiness passes it straight through — plus the falsy-value
        fingerprint cases, where `?? → ||` collapses `size = 0` and `size IS
        NULL` into the same scan. -->
        <!-- The route is `GET /outputs/page`, deliberately separate from
        `GET /outputs` rather than a mode flag on it: the two make different
        promises about page stability, and one endpoint that sometimes guarantees
        it is the harder thing to reason about. The follow-through below upgrades
        the existing route too without pretending the two cursor formats or
        consistency promises are interchangeable. The standard output-registry
        Vitest configuration now resolves the shared pagination/config packages,
        and the complete registry suite passes 116/116, including the 10 snapshot
        route tests and OpenAPI parity. -->
        <!-- 2026-07-29 CONSUMER-SURFACE FOLLOW-THROUGH — source commit
        ab10c989ea upgrades the existing `GET /api/v1/outputs` contract too:
        its former OFFSET scan now uses the allowlisted sort column plus unique
        `id`, fetches one row beyond a bounded 1..100 page, and emits an opaque
        HMAC-SHA-256 continuation bound to every filter, authorization-scope
        component, sort key, and direction. Malformed, tampered, cross-query,
        cross-scope, and cross-sort continuations fail before the database is
        queried; missing/short deployment keys fail closed. This endpoint
        advertises `consistency: continuation` and deliberately does not claim
        the stronger transaction-like snapshot bound of `/outputs/page`.
        Follow-ups a8e373dc26 and 1512613f0c make the source-derived workload
        classifier recognize both the local builder-style cursor and the shared
        `buildPageWhere` path, then lock both production queries to keyset
        classification. Integration commit 5949061dec gives both endpoints the
        same documented `ISIS_OUTPUT_CURSOR_HMAC_KEY`, adds the missing OpenAPI
        snapshot operation and schema, and makes the full package harness resolve
        the shared workspaces.

        OpenAPI, the TypeScript SDK, CLI, operator Outputs page, and Chromium
        flow now consume the continuation contract. The UI keeps prior opaque
        cursors for Previous and resets continuation on any filter/sort/page-
        size change. Final focused evidence: 54 shared pagination tests, 116
        output-registry tests, 8 SDK contract tests, 72 web page tests, one live
        Chromium continuation/reset test, 3 CLI tests, and 295 deterministic
        base/Isis generator tests; lint and the production registry build pass.
        The workload census now reports three keyset reads (both output paths
        plus the pre-existing jobs path), 11 remaining limit-offset reads, and
        one remaining deep-page risk. This widens the checked cell's production
        and consumer coverage; it does not close I6.5.a's missing record kinds
        or I6.5.f's deleted/changed-row and role-tier fixture obligations. -->
  - [x] I6.5.d Project explicit missing/redacted/unavailable/not-applicable/
        stale states and audience-appropriate details. Evidence: projection
        fixtures.
        <!-- 2026-07-29 OUTPUT PROJECTION CLOSURE — `@isis/read-projection`
        defines and runtime-enforces the six-state discriminated field contract:
        present/stale fields carry values, missing/redacted/unavailable/not-
        applicable fields cannot, stale fields carry pinned observation and
        freshness timestamps, and consumer records cannot carry privileged
        detail. Its clock is injected, so freshness is deterministic in fixtures
        and remains fixed across a snapshot scan.

        `GET /api/v1/outputs/projected-page` is the first production projection:
        it accepts only the declared `projection=stateful` shape, derives
        consumer/operator/auditor from authenticated roles or permissions, and
        binds each audience to a distinct signed cursor resource. Callers cannot
        request an audience, and a privileged cursor cannot be replayed through
        another tier. Every one of the 23 declared output fields is present with
        an explicit state. Integrity material, storage locators, subject
        identifiers, and producer metadata are redacted from consumers; missing
        and unavailable explanations differ for operator and auditor needs.
        The OpenAPI union and TypeScript SDK publish the same contract.

        The authoritative output fixture reaches all six states from one sparse,
        stale pending document and scans serialized consumer output for protected
        hash, bucket/key, owner, metadata, and internal explanation values.
        Route fixtures additionally prove authentication, all three audiences,
        caller-audience refusal, projection allowlisting, cross-audience cursor
        refusal, scan-pinned freshness, and fail-closed cursor configuration.
        Shared-library tests pass 11/11, the output-registry suite 130/130, and
        the SDK suite 337/337; strict TypeScript, Nx boundary lint, shared/
        registry/SDK builds, OpenAPI parity, and affected inventory generators
        pass. Source-derived I6.5.a evidence now records output projection as
        served (15/63 cells overall; 16/129 GET routes validate a query schema).
        This closes the reusable state contract and production output exemplar;
        the six record kinds already reported as lacking any store-backed read
        remain implementation gaps, and I6.5.f's broader role/page fixture
        matrix remains separately open. -->
  - [ ] I6.5.e Bound joins/lineage expansion, batch dependent reads, avoid N+1,
        and verify production-scale query plans. Evidence: performance tests.
  - [ ] I6.5.f Publish OpenAPI/consumer fixtures for first/middle/final/empty
        pages, invalid cursors, deleted/changed rows, and role tiers. Evidence:
        contract suite.
- [ ] I6.6 Implement guarded dispatch API with idempotency and budget
      reservation. Evidence: duplicate-submit and stale-policy tests.
  - [ ] I6.6.a Define dispatch command/result contracts with exact plan/version,
        expected state, estimate/confirmation, reservation, actor context,
        idempotency key, and policy/code versions. Evidence: API schema.
  - [ ] I6.6.b Authenticate and authorize route/function/object/property; load
        the canonical plan rather than trusting client-supplied workflow/model/
        cost/tool details. Evidence: mass-assignment tests.
  - [ ] I6.6.c Revalidate plan inputs, rights/consent, safety, dependencies,
        endpoint capability, approvals, quota, budget, and freshness in one
        guarded transition. Evidence: guard matrix.
  - [ ] I6.6.d Atomically bind/consume budget/resource/quota reservation and
        create durable job/outbox/audit state. Evidence: transaction tests.
  - [ ] I6.6.e Return the original command result for identical idempotent retry
        and conflict for key reuse with different canonical input. Evidence:
        duplicate-submit tests.
  - [ ] I6.6.f Reject changed policy/plan/evidence/reservation between confirm
        and dispatch with no provider call. Evidence: TOCTOU tests.
        <!-- MEASURED 2026-08-17 (I6.6.a–f). I6.6 SAYS IMPLEMENT AND MOST OF IT
        ALREADY IS, which is why this reads as three specific edges rather than an
        absence. `evidence/v1-workbenches/isis-dispatch-guard-contract.json`
        (writer + 14 tests, wired into `verify:inventory:v1-workbenches`) DRIVES
        the real `JobService` with an in-memory `JobStore` double — the store is
        the dependency and the service is the thing under test, which is the only
        direction a double may go.

        WHAT WORKS, EXECUTED AND CREDITED FIRST. Two identical submits under one
        idempotency key produce ONE job id, ONE stored job and ONE outbox entry.
        The job and its outbox row are written inside a single
        `this.db!.withTransaction` over `jobs` and `job_outbox`. A unique-index
        race is resolved by re-reading (`idx_jobs_idempotency_key` is a real
        UNIQUE partial index, and the double raises the same `23505` so that
        branch is reachable). `evaluateIsisDispatch` is fail-closed and correct.

        FIRST EDGE — A REPLAY AND A CONFLICT ARE DIFFERENT ANSWERS AND THIS PATH
        GIVES ONE. Executed: the SAME key with `prompt: 'A COMPLETELY DIFFERENT
        PROMPT'` returned THE ORIGINAL job id, `status: pending`, no error — and
        the stored prompt is still the first one. A caller who reuses a key with
        changed input is told their new request was accepted and it was not. THE
        ESTATE KNOWS HOW TO TELL THEM APART AND APPLIES IT ON ONE PATH ONLY:
        `hasRecordedTransition` compares a fingerprint, for STATE TRANSITIONS,
        and on a mismatch it logs "Ignoring transition replay with conflicting
        fingerprint" and then `return true` — so a conflicting replay is DETECTED
        AND THEN TREATED AS AN IDENTICAL ONE. The same asymmetry appears in the
        route's responses: it returns 409 `JOB_RETRY_CONFLICT` and has NO
        submit-conflict code at all.

        SECOND EDGE — THE ROUTE AUTHENTICATES INTO ONE FIELD AND THE SERVICE READS
        ANOTHER. `withSubmissionMetadata` spreads `...request.metadata` and then
        overwrites `submittedBy` (unconditionally, from auth), and `projectId`,
        `tenantId`, `organizationId` (conditionally, when the scope supplies
        them). It never sets `userId`. `JobService.submit` reads
        `metadata.userId` as the owner, falling back to a fresh
        `crypto.randomUUID()`, and the schema declares `metadata:
        z.record(z.string(), z.unknown())`. So the AUTHENTICATED PRINCIPAL LANDS
        IN A FIELD NOTHING READS AS OWNER, and the owner comes from a free-form
        client record. Executed: `metadata.userId:
        "A-USER-THE-CALLER-NAMED"` is stored as the owner. That is the field the
        subject-erasure path filters on (see I0.11). Also executed: a
        caller-named `model` is stored as the model, because the submit path
        contains NO canonical plan load of any kind — which is precisely what .b
        forbids.

        THIRD EDGE — THERE IS NO CONFIRM PHASE. Zero sites for
        `confirmationToken|estimateId|confirmDispatch|/confirm`, so .f's window
        between confirm and dispatch does not exist and A TOCTOU TEST CANNOT FAIL
        AGAINST A ONE-PHASE FLOW.

        AND THE GUARDED TRANSITION IS NOT ONE. Of the nine revalidations .c names,
        **0 happen inside the transaction that creates the job**, and 7 are absent
        from the submit path entirely (plan inputs, rights/consent, dependencies,
        endpoint capability, approvals, quota, freshness). Budget and safety DO
        exist on the path — so the zero is about the transition and not about a
        vocabulary that finds nothing. The budget reservation is
        `activeBudgetGate.reserve(job.jobId, …)` in a DIFFERENT application, keyed
        on a job id that must already exist, so it cannot be in that transaction;
        and `audit`, one of the three kinds .d names, is not written in it at all.

        (.a) ONE OF SEVEN CONTRACT MEMBERS IS CARRIED. `JobSubmitRequestSchema`
        carries `idempotencyKey`; `exact plan/version`, `expected state`,
        `estimate/confirmation`, `reservation` and `policy/code versions` are
        ABSENT, and `actor context` is carried ONLY by the free-form `metadata`
        record — which is not a contract carrying it, since that record can carry
        anything and therefore pins nothing. `IsisDispatchRequest`, the one real
        guard's input, carries `{measurement, admissions}` — none of the seven.

        TRAPS. (1) Unanchored, `409` matches `'nvidia rtx 4090': 0.5` in the
        route's GPU price table, and the first pattern therefore reported the
        route as returning a submit conflict; the pattern is now the RESPONSE
        shape and a control test pins the collision. (2) A CONDITIONAL SPREAD IS
        AN OVERWRITE — missing `...(projectId ? { projectId } : {})` reported
        `projectId` as never set by the route, which it is, just only when the
        scope supplies a value; that case has its own verdict rather than being
        folded into either extreme.

        CELLS LEFT UNCHECKED, ALL SIX. .a needs five absent members and an actor
        context that is a contract rather than a free-form record; .b needs the
        route to make `userId` authoritative and the service to load a canonical
        plan; .c needs seven checks that do not exist and all nine inside one
        transition; .d needs the reservation and an audit row in the same
        transaction as the job; .e needs a canonical-input comparison and a
        conflict response on submit — the fingerprint machinery to build it with
        already exists one method away; .f needs a confirm phase before it can
        have a TOCTOU test. -->

- [ ] I6.7 Implement job progress/cancel/retry/replay APIs. Evidence: state and
      role matrices.
  - [ ] I6.7.a Define snapshot and cursor-based event/progress contracts for
        job, stage, attempt, queue, reservation, cost, errors, and outputs.
        Evidence: progress API schemas.
  - [ ] I6.7.b Implement bounded authenticated progress reads/streams with event
        deduplication, reconnect, terminal closure, redaction, and no cross-
        tenant IDs/counts. Evidence: stream tests.
  - [ ] I6.7.c Implement cancel commands only for eligible states/roles with
        expected version, reason, idempotency, propagation status, and audit.
        Evidence: cancel state matrix.
  - [ ] I6.7.d Implement retry as a new attempt only for canonical retryable
        failures/stages under current policy/budget/dependency checks. Evidence:
        retry state matrix.
  - [ ] I6.7.e Implement replay as a new run identity with exact/current/fork
        mode, preview, confirmation, and immutable source linkage. Evidence:
        replay role/state tests.
  - [ ] I6.7.f Reject direct terminal-state rewrites, arbitrary
        retry-from-stage, hidden control changes, and stale expected versions.
        Evidence: adversarial command tests.
        <!-- MEASURED 2026-08-17 (I6.7.a–f). The bar is "state and role matrices".
        THE STATE HALF IS LARGELY BUILT AND WAS DRIVEN, NOT READ, by
        `evidence/v1-workbenches/isis-job-command-authority.json` (writer + 12
        tests, wired into `verify:inventory:v1-workbenches`) against the real
        `JobService` with an in-memory `JobStore` double: cancelling a completed
        job is an idempotent no-op that does not touch the outbox; cancelling a
        running one sets `status: cancelled` and records `cancellation.reason`
        plus `outboxEntriesCancelled: 2` — the PROPAGATION STATUS the cell asks
        for — and throws rather than reporting success when queue cancellation is
        unavailable; retry refuses a running job with `JobRetryConflictError`; and
        retry from `failed` produces a NEW job id carrying `retry.rootJobId`,
        `parentJobId` and `attempt: 1`, which is real immutable source linkage.
        Four of five probe cases are controls and all four pass.

        THE ROLE HALF IS THE FINDING AND IT IS ONE SENTENCE: THE JOB ROUTE FILE
        READS THE ACTOR 11 TIMES AND COMPARES IT 0. Every
        `auth.user?.sub ?? auth.apiKey?.ownerId` in `routes/jobs.ts` derives an id
        for ATTRIBUTION — `submittedBy`, `actorId`, `requestedBy` — and not one is
        compared to the job's owner. AN ACTOR ID YOU RECORD IS NOT AN ACTOR YOU
        CHECKED, so attribution sites and authorization sites are separate columns
        and are never summed. THE POSITIVE CONTROL IS THE NEXT FILE ALONG:
        `routes/models.ts`, same application, same helper, does
        `existing.uploaderId === userId` FIVE times. The estate knows how to
        authorize a command on a record somebody owns and does it on model routes
        and not on job routes — so any authenticated caller can cancel, retry or
        delete any job by id.

        THE ASYMMETRY RUNS ONE LEVEL DEEPER, INSIDE ONE SERVICE: `retry` accepts
        `requestedBy` and `authScheme`, and `cancel(id, reason?)` accepts NO ACTOR
        AT ALL. Retry is at least told who is asking and could compare; cancel
        could not enforce a role if it wanted to. Neither compares. And
        authentication itself is CONDITIONAL — `if (cfg.auth)` wrapped around
        `app.use('/api/*', authMiddleware(cfg.auth))` — so an unconfigured
        deployment authenticates nothing before any of this.

        (.f) IS SATISFIED BY ABSENCE RATHER THAN BY A CHECK, and that distinction
        is worth keeping: there is no `PATCH`/`PUT` on `/:id` and
        `store.update(id, Partial<Job>)` is not exposed on any route, so a direct
        terminal-state rewrite has NO PATH — a real positive, and a routing choice
        rather than a guard, since the store method exists. But nothing REJECTS a
        stale expected version because NO COMMAND ACCEPTS ONE:
        `expectedVersion|ifMatch|expectedStatus` appears nowhere in the job schema
        or routes, so optimistic concurrency is absent and .c's "expected version"
        has nothing to compare. No command writes an audit row either — the
        cancellation is recorded in the job's own metadata.

        (.a) THERE IS NO CURSOR CONTRACT ANYWHERE (0 sites) and four of the eight
        subjects the cell names — `stage`, `attempt`, `reservation`, `errors` —
        have no schema at all, while the other four do, so the four are not a
        pattern finding nothing. (.b) of five stream properties, two appear
        (`event deduplication`, `reconnect`). (.e) IS CITED RATHER THAN
        RE-DERIVED: I1.12.h already established that the only replay Isis has,
        `replayDeadLetterEntry`, UPDATEs the same outbox and job rows back to
        pending — so there is no new run identity for .e's exact/current/fork
        mode, preview, confirmation and source linkage to attach to. The
        instrument reads that finding out of the committed artifact by dotted path
        and throws if it moves.

        TRAPS. (1) The checklist wraps inside compounds and joining the lines
        leaves a space after the break: `cross-\ntenant` became "cross- tenant"
        and no token set matched it. A hyphen or slash before a space is always
        that wrap in these lists. (2) Deriving a method's parameters from the same
        regex that LOCATED its signature made the pattern consume its own closing
        paren, and `cancel(id, reason?)` came back with no parameters — which
        reads as "the service takes no arguments" rather than "the service takes
        no ACTOR". (3) The refusal control retried the job an earlier control had
        just cancelled, and A CANCELLED JOB IS RETRYABLE, so the control silently
        became an acceptance; it now has its own subject.

        CELLS LEFT UNCHECKED, ALL SIX. .a needs a cursor contract and four
        subject schemas; .b needs three of five stream properties; .c needs an
        expected version, a role comparison and an audit row; .d needs the
        policy/budget/dependency re-checks (the state guard is done); .e needs a
        replay that produces a new run at all; .f needs a command that accepts an
        expected version before it can reject a stale one. -->

- [ ] I6.8 Implement gate run/result/rerun APIs. Evidence: stale result cannot
      satisfy publish eligibility.
  - [ ] I6.8.a Define gate-plan/run/item/progress/measurement/verdict/aggregate/
        freshness/rerun contracts with exact subject and evaluator versions.
        Evidence: gate API schemas.
  - [ ] I6.8.b Resolve required gates server-side from asset
        kind/target/profile/ registry rather than accepting a client-declared
        passing set. Evidence: gate resolution tests.
  - [ ] I6.8.c Create/run/cancel gate batches with authorization, idempotency,
        resource limits, and immutable subject revision binding. Evidence: run
        command tests.
  - [ ] I6.8.d Read raw measurements/verdicts/regions/uncertainty with field-
        level authorization and deterministic pagination. Evidence: result API
        tests.
  - [ ] I6.8.e Compute freshness from subject/evaluator/gate-registry/profile/
        policy/dependency versions at read and release-check time. Evidence:
        stale-result tests.
  - [ ] I6.8.f Rerun creates new evaluation identities, preserves old results,
        states reason/scope, and cannot promote release state. Evidence: rerun
        history tests.
        <!-- MEASURED 2026-08-17 (I6.8.a–f). The parent's bar is "stale result
        cannot satisfy publish eligibility", so the first job is finding the
        record that decides publishing. The estate has exactly one and it is not
        called a gate: `ReviewPackage.releaseCheck`, which
        `contract-prisma-alignment.ts` declares `direct`-aligned to the ISIS
        Prisma schema, and which the rule "Approved review packages must have
        release-ready, release-valid checks" turns into publish eligibility.
        Instrument: `generate-isis-gate-result-freshness.mts` (+13 tests), wired
        into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-gate-result-freshness.json`.

        THE CONTRACT COMPARES INSTANTS FOURTEEN TIMES AND NEVER COMPARES THE ONE
        THAT DECIDES PUBLISHING. `compareTimestamps` runs all over
        `review-package.ts`: decisions must be chronological, audit events must
        be chronological, a delegation cannot resolve before it occurred, and —
        the same comparison this cell wants, made one field along — a stage graph
        cannot be stamped after the package was submitted. Not one of the
        fourteen sites names `lastValidatedAt`. The schema requires that field to
        EXIST when a check claims readiness and never requires it to be RECENT: A
        TIMESTAMP THAT IS ONLY CHECKED FOR EXISTENCE IS A FLAG, NOT A CLOCK.
        DRIVEN, NOT READ: the probe walked a package to `status: 'approved'`
        carrying a release check stamped `1970-01-01` against a stage graph
        stamped `2026-04-23`, and every rule passed. The parent's bar fails.

        WHAT IS BUILT IS CREDITED AND WAS ALSO DRIVEN. `ReleaseValidityService`
        resolves readiness server-side under 12 rules from stored state and reads
        nothing the caller supplied, separating merely-ready from strictly-valid
        with named reasons; the schema recomputes three counts and REFUSED to
        reopen an approved stage while the stored check still claimed none were
        blocking — though note which side gives way, since the new FACT is
        rejected rather than the stale CHECK invalidated; and a recompute writes
        the check and never the status, so .f's "cannot promote release state"
        holds on this path.

        THE ESTATE HAS ALREADY SOLVED THIS, IN ANOTHER LIBRARY, BETTER THAN THE
        CELL ASKS. `TransactionalGateRerunSchema` in `libs/contracts/run-protocol`
        carries `priorSubjectRef`, `subjectRef` and a `state: 'pending'` literal,
        and `rerunGates` re-opens every verdict whose subject was replaced as a
        NEW pending verdict derived from the NEW subject, appending rather than
        replacing and re-pointing the proof requirements — a new evaluation
        identity, the old result preserved, a rationale on the intervention, and
        no way to be born passing: four of .f's four properties, and it IS driven
        by an application (`autonomous-authoring-store.ts`). Driven here too: the
        schema accepted `state: 'pending'` and refused `state: 'pass'`. It never
        asks how OLD a verdict is because it asks what the verdict is ABOUT. A
        RESULT BOUND TO ITS SUBJECT BY REFERENCE CANNOT OUTLIVE THAT SUBJECT; A
        RESULT STAMPED WITH A TIME CAN ONLY BE CHECKED BY COMPARING THE TIME. The
        release check carries 7 members, 0 versions and NO SUBJECT REFERENCE, so
        there is nothing it can be invalidated BY.

        PER CELL. (.a) 0 of 9 named contracts exist on the eligibility path; 2
        (`verdict`, `rerun`) exist only in the run-protocol and 7 exist nowhere.
        (.b) the server-side resolution is real, and `UpdateReviewPackageRequest`
        ALSO accepts a client-declared `releaseCheck`, checked against three
        counts and never against the 12 rules — so the two strict gates only the
        service enforces (a populated evidence set, a governance retentionTag)
        are bypassable by declaring the passing set. (.c) 0 gate routes across
        four Isis HTTP surfaces, 0 gate aggregates among 24 Prisma models, and
        the subject revision the record does bind, `artifactVersion`, is nullable
        with no rule that ever requires it. (.d) the field-level authorization
        machinery exists and works — `@isis/read-projection`, 6 field states
        including `stale` — and projects exactly ONE resource, `output`, not a
        gate result; keyset pagination exists and paginates outputs; and all four
        things .d reads have 0 product sites against 48/116/16/290 sites in the
        reference implementation SOURCES, which is the positive control that
        keeps an all-zero column from being a broken matcher. (.e) 6 of 6 version
        inputs have no site anywhere on the eligibility path. (.f) on the
        eligibility path a recompute replaces the check in place, takes no reason
        or scope and keeps no prior result, so there is no evaluation identity for
        a rerun history to be a history OF.

        REACH AND WRITE PATH. 0 modules outside `@oshun/review-persistence`
        import it and its only repository implementation is in-memory. Two
        silently dropped fields were found by a RULE rather than by reading —
        every member of the package schema must be set by the assembler and every
        member of the update request merged by the store, both derived from the
        Zod schemas at runtime — and both are FIXED here: `assembleReviewPackage`
        never set `stageGraph`, so every `create()` threw and 97 of the library's
        137 tests were red; `mergeUpdate` never merged it, so no package could
        leave draft while the schema demanded a versioned binding to do so. A
        THIRD defect is REPORTED AND NOT PATCHED: `DecisionLifecycleService`
        stamps `fromState: stage.state` while the contract requires a history's
        first entry to come from `null` and forbids self-transitions, so the only
        service that records a review decision cannot record one (13 tests red).
        Both sides are coherent; choosing which is canonical changes what a review
        history MEANS in every domain on the shared contract, and that is a
        decision rather than a fix. 22 of the library's tests remain red from that
        cause and from specs that never stamp a stage graph.

        TRAPS. (1) The wrap has to be undone BEFORE the phrase regex, not just
        before the split: `field-\nlevel` joins to "field- level" and a pattern
        written `field-level authorization` matches nothing, reading as "the cell
        no longer lists its subjects". (2) Slash-lists and comma-lists cannot
        share a splitter — .f's "states reason/scope" is ONE property carrying a
        slash and splitting on it made four properties five. (3) An unanchored
        `gate` took `/v1/admin/studio/api-gateway-router` for a gate route twice.
        (4) `apps/oshun/bff/src/routes` is not an Isis surface — it holds every
        domain's operator API, and searching all of it reported Metis integrity
        verdicts as an Isis gate verdict contract; the surface is
        `admin-isis-*.ts`. (5) A COMMENT NAMING A THING IS NOT THE THING: three of
        four `verdict` hits were prose in a docblock, so every grep here is
        code-lines-only. (6) `id,` is shorthand for `id: id` and a matcher
        requiring the colon reported a field the assembler sets as dropped. (7)
        AN ARTIFACT THAT COUNTS ITSELF IS NOT A FIXED POINT: the .d positive
        control counted `evidence/v1-workbenches/` wholesale, and this report is
        written there, so every count grew by whatever the previous run wrote and
        `--check` failed the moment after it passed. The control reads the
        reference implementation SOURCES (`**/*.ts`), and a test pins the counts
        against the committed artifact.

        ALL SIX CELLS AND THE PARENT LEFT `[ ]`. .a needs the nine contracts on
        the surface that decides publishing; .b needs the declared passing set
        refused or re-derived; .c needs a gate run command and a required subject
        revision; .d needs a gate result to read before its authorization and
        pagination can apply to one; .e needs the six versions on the record, or —
        better, and already proven in this estate — a subject reference that makes
        the versions unnecessary; .f needs an evaluation identity on the
        eligibility path. -->

- [ ] I6.9 Implement rights, consent, and provenance APIs with field-level
      authorization. Evidence: protected-property tests.
  - [ ] I6.9.a Define versioned rights/attestation/consent/subject/source/
        ingredient/credential/ledger/blast-radius contracts with explicit
        redacted/unknown/expired/revoked states. Evidence: API schemas.
  - [ ] I6.9.b Separate metadata, evidence upload/retrieval, verification,
        grant/revoke, lineage query, and recovery operations by permission and
        host. Evidence: operation authorization matrix.
  - [ ] I6.9.c Enforce tenant/project/subject/purpose/object/property rights on
        every nested field, include, search, export, and error. Evidence:
        protected-property tests.
  - [ ] I6.9.d Accept evidence through immutable hashed storage references with
        content/size/scan/classification/retention validation. Evidence: hostile
        evidence upload tests.
  - [ ] I6.9.e Make consent/right changes versioned commands that trigger
        outbox/audit/blast-radius state without rewriting consumed evidence.
        Evidence: revocation integration tests.
  - [ ] I6.9.f Return privacy-safe counts/cursors and prevent enumeration
        through timing, not-found/forbidden differences, or identifiers.
        Evidence: enumeration tests.
        <!-- MEASURED 2026-08-17 (I6.9.a–f). The parent's bar is
        "protected-property tests", so the first job is finding the property and
        the record that holds it. Instrument:
        `generate-isis-rights-consent-provenance-api.mts` (+16 tests), wired
        into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-rights-consent-provenance-api.json`.

        THE ESTATE HOLDS THREE PROVENANCE RECORDS AND THE TWO THAT BIND CONSENT
        PROPERLY HAVE NO ROUTE. (1) `provenance`, output-registry runtime DDL,
        request/response shapes as Zod in `output.schema.ts` and NO contract:
        consent is `consentId: z.string().min(1).optional()` — one free string,
        no referent, no state, no version. (2) `ProvenanceBundle`, the canonical
        contract, `direct`-aligned to Prisma, whose `rights` member carries
        `rightsState`, `consentRequired`, `consentRecordIds`,
        `restrictedRegions` and a training-data evidence state. (3)
        `isis_provenance_bundles` + `isis_provenance_ledger` in
        `libs/isis/ai-providers`: hash-chained, tenant-sequenced, append-only by
        trigger, with `consent_id TEXT NOT NULL CHECK (length(btrim(...)) > 0)`
        — consent as a database-enforced precondition of recording a
        generation. Records 2 and 3 have 0 routes across all four Isis HTTP
        surfaces. Record 1 has three, and a PATCH on it can clear the consent
        id. 12 routes enumerated; the lineage family is the positive control
        that the enumerator finds routes when they exist.

        THREE LIVE DEFECTS, ALL DRIVEN AGAINST A REAL POSTGRESQL 16.14, ALL
        FIXED. The lane opened by pointing the real `OutputServiceImpl` at a
        real database and the FIRST CALL FAILED: `INSERT has more target columns
        than expressions` (SQLSTATE 42601). `register()` named thirty columns,
        passed thirty values and wrote twenty-nine placeholders, so EVERY
        `POST /api/v1/outputs` against a database raised 42601 — and no
        provenance record can exist without an output, so this cell's entire
        subject was unreachable. The route tests mock the service and the
        service had no test that reaches a database, which is exactly how a
        statement that can never execute survives review and a type-checker. A
        RULE OUTLIVES THE BUG IT WAS WRITTEN FOR: the instrument now checks
        every fully parameterised INSERT on the Isis surfaces for
        columns == placeholders == distinct placeholders (13 statements, 0
        violations), with the original defect reconstructed as a positive
        control so a silent matcher cannot pass for a clean estate.

        With that fixed the probe reached the subject and found two more. A
        SUPERSEDED PROVENANCE RECORD WAS UNREADABLE AND STILL WRITABLE:
        `getProvenance` returns the record through `o.provenance_id = p.id`
        while `updateProvenance` resolved one through `p.output_id = o.id`, and
        nothing forbids a second provenance row for an output — the service DDL
        declares no unique constraint on `output_id` though the Prisma model
        does. Driven: two rows registered, the read returned only the second,
        and a PATCH cleared the FIRST row's consent id and watermark hash where
        no read path could show it. And A LEDGER THAT CANNOT BE CORRECTED MUST
        NOT BE WRITTEN FROM THE PAYLOAD: the ledger event was stamped from
        `updated.*`, the row as it stands AFTER the mutation, so a
        caller-supplied `tenantId` decided which tenant an append-only row
        belonged to — permanently, since `generation_event_ledger` refuses
        UPDATE and DELETE by trigger (the probe's UPDATE was refused). Both are
        fixed and pinned by `output.service.provenance-scope.spec.ts`, whose
        three assertions were confirmed RED against the previous code before
        being confirmed green against the new.

        WHAT IS BUILT IS CREDITED AND WAS ALSO DRIVEN. The lineage half of the
        same router resolves a tenant scope on all five of its routes, filters
        parent and child ids through it, refuses cross-project edges by policy
        and audits inside the write transaction. The ledger really is
        append-only, and the consent id a PATCH clears SURVIVES in the register
        event that preceded it — so evidence is not destroyed, it is unreadable,
        because no route reads the ledger. `requireOutputAdminAccess` handles
        both auth schemes. Not-found and forbidden collapse to one 404 on every
        read because the owner filter is a clause of the lookup rather than a
        check after it, which is the strongest form of .f's property and the
        routes get it for free. The BFF's `POST /v1/admin/isis/3d-provenance`
        answering 503 `..._authority_not_configured` is an honest fail-loud
        seam, not a gap.

        PER CELL. (.a) 9 nouns; 7 have a contract and 1 reaches an Isis API
        surface — and the noun matcher is DELIBERATELY GENEROUS (any exported
        contract symbol naming the noun as a whole word, so "source" collects
        notification sources too), which makes that 1 an UPPER BOUND. The
        mapping is derived by scanning every contract, not listed: a hand-
        written table in the first draft called `attestation`, `blast-radius`,
        `subject` and `ledger` absent and all four have contracts. `credential`
        and `ingredient` genuinely have none. All four states
        (redacted/unknown/expired/revoked) are declared in contracts and 0 are
        expressible on the provenance API. (.b) 5 of 6 operation classes are
        populated and only `evidence upload/retrieval` has NO operation on any
        Isis surface. CORRECTED IN PLACE: the first version of this note said
        `grant/revoke` was empty too, because the instrument's BFF enumeration
        NAMED one module instead of listing them —
        `admin-isis-output-gallery-actions.ts` carries `revoke-consent` and
        `takedown` bulk actions, tier-gated to `operator-admin`, reversible
        inside an undo window, the whole time. ASK EVERY SERVICE, NOT THE
        INTENDED ONE. The enumerator now walks every `admin-isis-*` module and a
        test fails if it narrows to one again. Note where the revoke lives: the
        only consent revocation in the estate is a GALLERY BULK ACTION on the
        BFF, not an operation on the record that holds the consent binding, and
        it names a `consentRecordId` the provenance API cannot resolve. 5
        distinct authorization predicates across the populated five. (.c) 1 of 6
        rights dimensions is enforced on the
        provenance routes (object identity) against 3 of 6 on lineage; tenant
        and project are 0-of-3 on provenance and 5-of-5 on lineage, which is the
        differential with its own control. `subject` and `purpose` have no
        predicate anywhere. (.d) 3 of 5 validations present, and the content
        check FAILS OPEN — unreachable storage logs a warning and proceeds with
        the client's hash, an unavailable algorithm skips the comparison
        entirely. Driven incidentally: the probe registered an output with a
        declared hash over an object that does not exist, and it succeeded.
        `scan` and `classification` are absent. (.e) the one change command
        carries no expected version, rewrites in place, writes no outbox and no
        blast-radius, and silently DROPS an `expectedVersion` a caller sends
        because the schema is non-strict. (.f) 5 probe cases, 5 accepted.

        CAPABILITY REACH, SPLIT RATHER THAN FOLDED. Of 6 measured capabilities,
        2 are reached by a route, 3 are built and called but NOT through HTTP
        (`persistEntry` on every job completion, `loadLedger` and
        `verifyPersistedLedger` from the Studio web server), and 1
        (`queryProvenance`) has no caller anywhere. Folding "no HTTP door" into
        "unreached" would say something false about working code: the
        hash-chained ledger is written and verified — what it has no door for is
        an operator or an auditor.

        DECISIONS, REPORTED AND NOT TAKEN. (1) `UpdateProvenanceRequestSchema`
        lets a caller reassign the record's OWN `tenantId`/`projectId`/
        `ownerId`. Nothing filters on them today so it grants nothing today, and
        removing accepted fields is a contract change; the ledger poisoning it
        fed is fixed. (2) Adding the `output_id` unique index the Prisma model
        already declares would remove the superseded-record class at the root,
        but it fails on any database already holding duplicates, so it needs a
        data decision first. (3) Which of the three provenance records is
        canonical, and therefore which one the API should serve, is a product
        decision this cell cannot take.

        TRAPS. (1) Slash-lists and comma-lists cannot share a splitter: .b's six
        classes are comma-separated and TWO CARRY A SLASH, so splitting on
        slashes reports ten. (2) A substring matcher says `ResourceSchema` names
        the noun "source"; the matcher splits PascalCase into words instead. (3)
        `three-d-provenance-store` matches a grep for `provenance-store` and is
        a different thing. (4) A hand-authored noun→contract table made four
        false absences — A ROW'S COVERAGE MUST NOT DEPEND ON WHICH FILES A HUMAN
        LISTED. (5) A perl rewrite of `codeOnly(readRepo(x))` → `readCode(x)`
        rewrote `readCode`'s OWN BODY into infinite recursion. (6) The first
        draft of the superseded-record test returned `[]` regardless of the
        statement, so it passed against the defect as happily as against the
        fix; the stub now models the row the way PostgreSQL sees it.

        ALL SIX CELLS AND THE PARENT LEFT `[ ]`. .a needs the nine contracts on
        the surface that serves provenance — or, better and already built, the
        surface pointed at record 2 or 3; .b needs an evidence upload and
        retrieval, and needs the revoke that exists to act on the record that
        holds the consent binding rather than on a gallery selection; .c needs
        tenant,
        project, subject and purpose resolved on the three provenance routes,
        and the `@isis/read-projection` machinery — which already projects
        outputs by audience — applied to the record carrying prompt, consent and
        watermark; .d needs a scan and a classification, and a content check
        that fails closed; .e needs a consent change to be a command with a
        version rather than a field write; .f is the closest, and needs its
        counts and cursors measured rather than inferred from the read paths'
        shape. -->
- [ ] I6.10 Implement release plan/execute/verify/recall APIs. Evidence:
      time-of-check/time-of-use race test.
  - [ ] I6.10.a Define immutable release candidate/target plan, prerequisite
        snapshot, confirmation, execution step, verification, receipt, failure,
        rollback, and recall contracts. Evidence: release API schemas.
  - [ ] I6.10.b Plan server-side from exact asset/bundle revisions, target
        profiles, gates, rights/consent, provenance, approvals, destinations,
        capacity, cost, and policy. Evidence: planning tests.
  - [ ] I6.10.c Require authorized confirmation bound to the unchanged plan/
        prerequisite hashes and idempotency key. Evidence: stale-confirmation
        tests.
  - [ ] I6.10.d Revalidate every prerequisite immediately before atomic release
        transition and each external target write. Evidence: TOCTOU tests.
  - [ ] I6.10.e Execute target steps with durable checkpoints, idempotent
        writes, partial-failure visibility, compensation/rollback, and immutable
        receipts. Evidence: failure recovery tests.
  - [ ] I6.10.f Verify final external/registry/gallery bytes, hashes, manifests,
        metadata, visibility, and URLs before success. Evidence: post-write
        verification tests.
  - [ ] I6.10.g Recall by authorized case/scope with blast-radius preview,
        propagation, verification, and immutable history. Evidence: recall API
        tests.
        <!-- MEASURED 2026-08-17 (I6.10.a–g). The parent's bar is a
        time-of-check/time-of-use race test, so the first job is finding
        something to race. Instrument: `generate-isis-release-toctou.mts`
        (+10 tests), wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-release-toctou.json`.

        THERE IS NO ISIS RELEASE API. 0 route paths naming publish, release,
        recall or rollback across all four Isis HTTP surfaces — against a
        control of 11 provenance/lineage paths found by the same literal scan,
        so the zero is a measurement. 0 of the 9 contracts .a names exist. THE
        ESTATE HAS PLENTY OF RECEIPTS AND ROLLBACKS AND NONE OF THEM IS A
        RELEASE'S: 39 verification symbols, 27 receipts, 23 failures, 19
        rollbacks in `libs/contracts`, and not one is release-scoped. That
        unqualified column is the control for the qualified zero — and the
        qualifier is load-bearing, because dropping it reported 7 of 9 present
        when `SetPrecisionRecall` answered for "recall" and
        `AdminWebhookDeliveryReceiptOutcome` for "receipt". EVERY NOUN IN THE
        CELL IS GOVERNED BY ONE ADJECTIVE AND THE MATCHER MUST CARRY IT.

        FOUR VERBS LIVING IN THREE PLACES, NEVER JOINED. `plan`: nothing, so
        .b's ten planning inputs have nowhere to land. `execute`:
        `EditorialLifecyclePublisher.publish` — an autonomy binding wired into
        the plane, not a route on any Isis surface. `verify`:
        `POST /api/v1/outputs/:id/verify`, which verifies the stored BYTES
        against the recorded hash and is not the release verification .f asks
        for. `recall`: `withdraw`/`rollback` on the publisher AND a `takedown`
        bulk action on the BFF gallery — two recall paths that do not know about
        each other. 7 of 15 measured properties present.

        THE PARENT'S BAR, DRIVEN AGAINST THE ONE EXECUTOR THAT EXISTS, AND
        FIXED. A PREREQUISITE THAT IS NEVER RE-ASKED CANNOT BE WITHDRAWN:
        `publish` evaluated `gatesFor(item)` ONCE and walked four transitions on
        that single snapshot, committing afterwards. Driven: a rights clearance
        withdrawn between the check and the write, and the placement published
        anyway. It now re-asks the gates immediately before the commit. Two
        controls hold either side of the fix — a gate that never holds still
        refuses, and a gate that holds throughout still publishes — so the
        result is not a publisher that refuses everything.

        TWO MORE ON THE RECALL PATH, ALSO DRIVEN AND FIXED. A RECALL THAT
        RECORDS NEITHER WHO NOR WHY IS NOT A HISTORY: `withdraw(item,
        _actorId, _reason)` and `rollback` took both and discarded both, leaving
        a takedown transition carrying `from`, `to`, a timestamp and an empty
        gate list. Both are now stamped and survive a snapshot/restore round
        trip. And A HISTORY YOU CAN APPEND TO FROM OUTSIDE IS NOT AN IMMUTABLE
        HISTORY: `get` and `list` handed out the live record whose `history` is
        declared `readonly` and was a plain array, so a fabricated
        `takedown -> published` entry could be pushed onto a publication's
        record; both now clone, as `snapshot` already did. All three are pinned
        by `editorial-publisher-recall.spec.ts`, 7 of whose 8 assertions were
        confirmed RED against the previous code — the eighth is the control that
        must pass either way.

        THE ESTATE ALREADY BUILT THE BLAST RADIUS AND NOTHING CALLS IT.
        `buildTakedownCascade` in `@oshun/studio-authoring` returns the affected
        artifact ids for an entry plus its downstream, and REFUSES an empty
        reason or an empty user-visible note. 0 callers. The estate had already
        decided a takedown needs a reason, in a function no takedown path
        invokes — which is why the discard above could stand next to it.

        WHAT IS BUILT IS CREDITED. The publisher serializes every record
        mutation, walks the real studio-authoring state machine without skipping
        a state, restores the previous record and rethrows when a commit fails,
        short-circuits an unchanged candidate on a content digest (a real
        idempotency key), and records nothing at all for a refused publish.
        `artifactIdFor` refusing an item with no publication reference is a
        fail-closed guard, not a gap — checked before it was reported.

        TRAPS. (1) The checklist wraps after a SLASH in .c
        (`plan/\n prerequisite hashes`), and an `unwrap` that rejoins only
        hyphens leaves "plan/ prerequisite" — the cell then reads as though it
        no longer asks for the binding. (2) .a and .b are COMMA lists whose
        first items carry slashes (`release candidate/target plan`, `exact
        asset/bundle revisions`); a slash splitter reports eleven and twelve.
        (3) `tryEditorialAdvance` rejects an edge the state machine does not
        have BEFORE it looks at a gate, so revalidating as `to -> to` would have
        refused every publish for the wrong reason; the recheck uses the real
        edge.

        ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`. .a needs the nine contracts;
        .b needs a planning operation before its ten inputs have anywhere to
        land; .c needs a plan to hash before a confirmation can be bound to one;
        .d has its first half — the recheck before the atomic transition — and
        still needs revalidation before each external target write, which sits
        downstream inside `commitRecord`; .e needs a persisted receipt (one is
        computed and returned, none is stored); .f needs anything at all to
        re-read a written target before reporting success; .g needs
        `buildTakedownCascade` called by the path that performs a takedown, and
        needs the two recall paths to become one. -->

- [ ] I6.11 Implement gallery promotion from Yemaya to Oshun editorial.
      Evidence: original AAA output and curated derivative retain lineage.
  - [ ] I6.11.a Define promotion request/candidate/decision contracts linking
        exact Yemaya project/output/revision and intended Oshun editorial
        collection/use/audience. Evidence: promotion schemas.
  - [ ] I6.11.b Authorize source project/output access, promotion authority,
        destination editorial scope, and protected-field projection across
        service boundaries. Evidence: cross-host authorization tests.
  - [ ] I6.11.c Revalidate source integrity, asset kind/target compatibility,
        safety/rights/consent/provenance/gates, and source lifecycle before
        acceptance. Evidence: eligibility tests.
  - [ ] I6.11.d Create an Oshun curated derivative/revision with new identity,
        bounded editorial metadata/transforms, and immutable ingredient linkage
        to the original AAA output. Evidence: lineage tests.
  - [ ] I6.11.e Route the derivative through Oshun editorial review/release
        without granting Yemaya approval/publication authority. Evidence:
        authority boundary tests.
  - [ ] I6.11.f Propagate source supersession/quarantine/recall/rights changes
        through the derivative dependency edge. Evidence: lifecycle integration
        tests.
        <!-- MEASURED 2026-08-17 (I6.11.a–f). The parent's bar is "original AAA
        output and curated derivative retain lineage", which presupposes a
        derivative — so the first job is finding the operation that makes one.
        Instrument: `generate-isis-gallery-promotion.mts` (+17 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-gallery-promotion.json`.

        NO PROMOTION ROUTE, AND ONE PROMOTION VERB. 0 in-scope route paths name a
        promotion across the seven Isis/Yemaya/Oshun-editorial surfaces; the 2
        that match belong to the workflow-registry's ENVIRONMENT promotion, a
        staging recipe moving dev → staging → prod. Control: the same literal
        scan finds the 3 gallery paths, so the zero is a measurement. The
        estate's one operation whose name means "move this output into Oshun
        editorial" is the gallery bulk action `send-to-editorial`.

        0 OF 3 CONTRACTS, AND THE WORD IS EVERYWHERE. No promotion request,
        candidate or decision contract exists for these endpoints. THE CONTROL IS
        THE UNQUALIFIED COLUMN AND IT IS NOT EMPTY: `ModelCardPromotionDecision`
        and `ModelVersionPromotionDecision` are promotion decisions about a MODEL
        moving through a release lane. 58 exported symbols in `libs/contracts`
        name a promotion, across 13 subjects — model, model card, model version,
        policy bundle, source set, gold set, notebook, learning stage, autonomy,
        research comparative — and not one is an output or a gallery. "The
        vocabulary is missing" is refuted; the SUBJECT is missing. Request and
        candidate have no symbol under either reading: TWO SHAPES OF ABSENCE.

        5 OF 6 LINK FIELDS HAVE NOWHERE TO LAND. Measured against the three
        types that carry a promoted artifact end to end — `OutputRecord` (18
        fields), `EditorialRecord` (7), `ProvenanceChain` (12). Yemaya project,
        Yemaya revision, editorial collection, use and audience match no field on
        any of them; the one hit is `OutputRecord.outputId`, the gallery's own id
        rather than a reference to a Yemaya output. AN ORIGIN IS NOT A TIER:
        `OUTPUT_DOMAINS` has 7 members and no yemaya, so the seed AAA output is
        `domain: 'tara'` at the AAA tier — the gallery cannot say an output came
        from Yemaya, only that an AAA creator made it. And `ProvenanceChain`
        starts at an opportunity SIGNAL, so a promoted artifact's story would
        begin the moment Oshun noticed it.

        THE PARENT'S BAR, DRIVEN. `send-to-editorial` on a real output: 6 records
        before, 6 after, byte-identical — `applyEffect` has no branch for it, so
        it falls through to `return record`. 2 of the 4 bulk actions change a
        record field (takedown → `takenDownAtUnixSeconds`, re-tag → `tags`) and
        the promotion is not one of them; the 2 that do are the control. The
        store that performs the promotion has snapshot members `context` and
        `invocations` and nothing else, so there is nowhere to write the edge
        either. And the `EditorialRecord` the Oshun editorial release path writes
        has fields artifactId, categoryId, history, publicationSha256,
        reviewItemId, state, title — 0 naming a source, origin, ingredient or
        upstream, even when the review item is handed one.

        16 EDGE KINDS ACROSS 4 LINEAGE VOCABULARIES, 1 NAMES THE PROMOTION, AND
        IT HAS NO PRODUCER. `@isis/output-gallery` (6 kinds, nodes `outputId` +
        `job:<jobId>`), `@isis/outputs` (7, nodes keyed to an outputFileId within
        a manifestId), `@yemaya/asset-library` (3, nodes `artifactId` with an
        author and an attribution consent), and `buildTakedownCascade` (0 kinds —
        its downstream ids arrive as an ARGUMENT). Four node namespaces, none
        shared, so no single edge can span two graphs. THE SAME STRING IS A
        MEMBER OF TWO UNIONS: `send-to-editorial` is both a `LineageEdgeKind` and
        a `BulkAction` kind, 4 files construct the action and 0 construct the
        edge — a bare literal scan credits the promotion with writing its own
        lineage. 4 of the 6 gallery kinds appear nowhere in the estate at all.

        FIXED: A PROMOTION OF A SUBJECT THE GALLERY NEVER HELD SUCCEEDED.
        `applyBulkAction` walked the RECORDS and skipped ids it did not
        recognise, so `send-to-editorial` naming a Yemaya
        `project/output/revision` returned `ok:true` with a receipt naming it —
        and so did a takedown of outputs that do not exist. In production the
        gallery seeds EMPTY, so until a real output is cataloged every bulk
        action is in that state. `invalid-outputs` is the code the library
        defined for this and had only ever spent on the empty list. Every id must
        now resolve, the batch is refused whole rather than narrowed, and the
        check runs after the tier gate so it cannot become an existence oracle.
        4 of the 8 assertions in `output-gallery-promotion.spec.ts` were
        confirmed RED against the previous store; the other 4 are controls.

        FIXED: THE ONLY EDGE THE ESTATE PRODUCES WAS THE ONLY EDGE THE READER
        DROPPED. Both member-gallery readers admitted a lineage edge only when
        BOTH endpoints were visible RECORDS. Every edge the release write path
        emits ends at a `job:<jobId>` node, which is never a record — so the
        lineage panel was structurally empty for every real output. Driven: 1
        edge in the store, 0 reaching the client. Neither reader's tests noticed,
        because their fixtures used record→record edges of kinds no write path
        emits — A FIXTURE IN THE ONE SHAPE PRODUCTION CANNOT PRODUCE TESTS
        NOTHING. `buildRenderableLineage` now owns the rule for both: an edge
        must be ANCHORED in the visible set and must not REACH an output outside
        it, so a foreign tenant's output id is still withheld (pinned as a
        control at both altitudes).

        A DECISION, NOT A FIX: THE ARROW. `OutputLineage` names its traversals
        `parentsOf` (incoming) and `childrenOf` (outgoing), and `descendantsOf`
        walks outgoing and is documented "used by takedown cascade" — with 0
        product callers. The two producers of `derived-from` disagree about which
        way it points. `ledger-branch-bridge` writes parent → child and says so
        ("edges point parent -> child … so the takedown cascade of the parent
        reaches the retake") and has 0 product callers. `output-catalog` writes
        output → `job:<jobId>` — the plain reading of the kind's own name, the
        opposite arrow — and is the only producer that writes into a live tree.
        Driven: `descendantsOf(out_job7)` returns `[job:job7]`, so a cascade
        built on it sweeps the producing RUN as downstream, and the panel labels
        the run a Child. Both readings are defensible and the estate holds one of
        each, so settling it moves either a producer or a traversal name — plus a
        normalization for snapshots already written in the old direction. Left
        failing on purpose and recorded in the report's `edgeDirections`.

        MORE THAT IS MEASURED, NOT FIXED. (1) `validateBulkAction` branches on
        four tiers and the route hands it the literal `tier: 'operator-admin'`
        while never reading a tier off the auth context — which has none — so the
        gate that decides promotion authority has ONE reachable input. (2) The
        destination is a free string: `queueId` is validated against nothing, and
        no editorial queue or collection registry exists anywhere (the BFF does
        keep a moderation-queue registry, so the pattern is not foreign).
        (3) 2 of the 4 editorial transitions carry an `actorId` — the two recall
        paths I6.10 stamped — and none of the publish walk's, so a promoted
        derivative would be approved by nobody and recorded as nobody; .e's
        authority boundary has nothing to separate. (4) `takenDownAtUnixSeconds`
        is written by takedown, restored by undo, and COMPARED BY NOTHING, so a
        taken-down output can be promoted. (5) Nothing crosses a service
        boundary at all — the promotion never leaves the Oshun BFF process, so
        .b's cross-host projection has no boundary to project across.

        5 of 20 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`. .a
        needs the three contracts and five of its six link fields need a field to
        land in; .b needs a real tier, a resolvable destination and a boundary;
        .c needs four eligibility checks (only the precondition that the subject
        exists was added); .d needs a derivative before its identity, its bounded
        metadata or its ingredient link mean anything; .e has the review walk and
        needs the actor on the approval; .f needs `descendantsOf` bound to a
        takedown and a consent revocation that reaches a consent store. -->
- [ ] I6.12 Implement signed deep links among Isis, Yemaya, Oshun Studio, and
      output gallery. Evidence: tenant, expiry, permission, and replay tests.
  - [ ] I6.12.a Define allowlisted link intents/hosts/routes and minimal claims
        for opaque subject reference, tenant/project, audience, issuer, target,
        issued/expiry/not-before, nonce, and version. Evidence: link schema.
  - [ ] I6.12.b Issue links only after source authorization using externally
        managed rotating signing keys and canonical URL encoding. Evidence:
        issuance tests.
  - [ ] I6.12.c Validate signature/key/status, issuer/audience/host/route,
        timestamps/skew, nonce/replay policy, and claim limits before routing.
        Evidence: validation tests.
  - [ ] I6.12.d Reauthorize the current actor against the canonical referenced
        object/property/function after validation; never treat possession as
        access. Evidence: tenant/permission tests.
  - [ ] I6.12.e Avoid credentials, private metadata, raw IDs where opaque refs
        suffice, return safe expired/invalid/forbidden states, and scrub
        referrers/logs. Evidence: link leak tests.
  - [ ] I6.12.f Test forward/back navigation, logged-out continuation, consumed/
        replayed links, moved/deleted/quarantined objects, and host mismatch.
        Evidence: cross-host journey tests.
        <!-- MEASURED 2026-08-17 (I6.12.a–f). The parent's bar is tenant, expiry,
        permission and replay tests, so the first job is finding a signed deep
        link to test. Instrument: `generate-isis-deep-link-tokens.mts` (+11
        tests), wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-deep-link-tokens.json`.

        EVERY MINTED LINK IN THE FOUR HOSTS IS A PLAIN PATH CARRYING A RAW ID. 79
        distinct link templates across Isis (2,219 files), Yemaya (2,904), Oshun
        Studio (3,500) and the output gallery (12): 0 signed, 42 carrying a raw
        identifier — `/review?reviewId=rev-1`, `/models?modelId=…`,
        `/scene/<shortCode>` — 36 static, and exactly ONE opaque capability link.
        That the admin links are unsigned is not itself a defect: a navigation
        path behind a route that authorizes independently is safer than a
        capability link. It does mean .a–.c have no subject on those surfaces,
        and it makes .e's "raw IDs where opaque refs suffice" the standing state.

        SO THE BAR IS DRIVEN AGAINST THE ONE LINK THAT CARRIES ACCESS. A living
        scene share is a cross-host reference a person is GIVEN: a
        `/scene/<shortCode>` path plus a password, exchanged once for an
        HMAC-signed bearer token. 2 of .a's 8 claims are on it — an opaque
        subject and an expiry. Tenant/project, audience, issuer, target,
        not-before, nonce and version are not, so .c cannot check what the token
        does not carry.

        FIXED: A SIGNATURE OVER THE PARSED VALUE DOES NOT COVER THE TOKEN. The
        HMAC is derived from the DECODED short code and the PARSED expiry, and
        both decoders are lax — `Buffer.from(x, 'base64url')` ignores padding and
        stray bytes, `Number.parseInt` stops at the first non-digit. Driven:
        `…YQ=.…`, `…YQ==.…`, `…YQ\n.…`, `….<exp>abc.…`, `….0<exp>.…`,
        `….+<exp>.…` and `….<exp> .…` — seven distinct strings — all verified as
        the single token that had been issued, an unbounded family per issuance.
        Nothing consumes this token today, so nothing was bypassed; it matters
        because .c asks for a nonce/replay policy and A TOKEN THAT IS NOT
        CANONICAL CANNOT BE CONSUMED: a consumed-token set keyed on the string is
        defeated by re-encoding, and the same laxity breaks audit correlation.
        Both segments must now arrive in the encoding the signer emits — 7 of the
        12 assertions in `scene-unlock-token-canonical.spec.ts` were confirmed
        RED, and four controls (subject binding, expiry, wrong key, the canonical
        token itself) hold either side.

        FIXED: THE ONE SIGNATURE COMPARISON THAT WAS NOT CONSTANT-TIME. Of the
        seven HMAC-signed primitives in the four hosts, `signed-model-manifest`'s
        hmac-sha256 branch compared the MAC with `expected === signature`, which
        returns on the first differing character, while the ed25519 branch beside
        it went through `crypto.verify`. Replaced with a length-checked byte
        comparison; 7 of 7 primitives now compare in constant time. The spec is a
        REGRESSION GUARD, not red-to-green — a timing change alters no verdict,
        and the case it does guard is the one `timingSafeEqual` introduces:
        a wrong-length signature must be a refusal, never an exception.

        .b's ROTATING KEYS EXIST AND NOTHING OUTSIDE THEIR LIBRARY USES THEM.
        `@yemaya/auth` declares `KeyRotationService` with a key status, a stored
        key, `getActiveKey`, `getVerificationKey(kid)`, `getJWKS` and
        `checkAndRotate` — externally managed rotating signing keys, precisely
        what .b names. Consumers outside `libs/yemaya/auth`: 0. So the answer is
        not "the estate has no rotation"; it is that rotation is built and no
        signer reaches it. The scene token holds a single env secret and no kid;
        one primitive in the estate models key identity at all
        (`signed-model-manifest`), and it refuses on mismatch rather than trying
        a set.

        THE ESTATE WROTE THE DOCTRINE DOWN AND APPLIED IT TO A CURSOR.
        `libs/isis/read-pagination/src/cursor.ts` opens by naming the two
        obligations — OPAQUE so nothing downstream freezes the encoding, SIGNED
        because "the caller cannot WRITE one" — and refuses to sign without a
        secret rather than substituting a fallback. A deep link is the same
        problem shape: a caller-held reference the caller must not be able to
        write. 4 of the 7 primitives fail closed without a secret; no link does,
        because no link is signed.

        POSSESSION IS ACCESS, MEASURED. `VerifySceneUnlockTokenOptions` declares
        exactly `now, secret, shortCode, token` — no actor, tenant, audience or
        viewer — so verification asks nothing about who is presenting the token,
        and a verified token stands in for the password rather than being
        re-authorized against the referenced object. Driven: the same token was
        accepted on all 3 presentations inside its TTL. That is .d's whole
        subject and .c's nonce clause.

        .e, DRIVEN THROUGH THE REAL MINT. `librarySharePolicyStore.createShareLink`
        was called, and the deep link it returns carries 9 query parameters
        beside its opaque token — channel, collectionId, exp, flow, permission,
        recipient, role, source, target — including `recipient=…@…`, the
        recipient's email address, in a link meant to be forwarded. The token
        itself is `randomBytes(32)` with only its sha256 persisted and
        `resolveLink` reads the tier from the stored grant rather than the URL, so
        `permission` is decoration rather than an escalation — but the address is
        private metadata riding in the link.

        TRAPS. (1) THREE SEPARATORS IN ONE CELL FAMILY: .a's first list is
        slash-separated (`link intents/hosts/routes`), its second is comma-
        separated with slashes inside items (`tenant/project`), and .b is one
        comma-free sentence whose three requirements only separate on `using`
        and `and` — a comma splitter reports .b as a list of ONE. (2) A `**` PATH
        COMPONENT REQUIRES A DIRECTORY TO SPAN: `git ls-files
        'libs/isis/output-gallery/src/**/*.ts'` matches nothing against the flat
        `src/lineage.ts`, and the first version of this report gave the
        output-gallery host 0 files. (3) A COMPARISON NAMED `expected` IS NOT A
        DIGEST COMPARISON — `assertExactKeys` in the signup-verification store
        compares sorted OBJECT KEY NAMES, and a name-shaped test reported a token
        store as comparing a signature unsafely; the compared identifier must be
        assigned from a digest. (4) A CONSTANT IS NOT A VERIFIER:
        `SIGNUP_VERIFICATION_STORE_KEY` matches /verif/i.

        6 of 17 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`. .a
        needs a versioned claim set and an intent/host/route allowlist, neither of
        which exists; .b has issuance-after-authorization and an external key and
        needs the rotation the estate already built; .c has a constant-time
        signature check and needs issuer/audience/host/route, skew, a nonce
        policy and claim limits; .d needs the actor reauthorized after validation
        — today a verified token IS the authorization; .e needs opaque refs and
        the recipient's address out of the query; .f needs a journey that crosses
        two of the four hosts, and none exists. -->
- [ ] I6.13 Implement capability/health APIs per host audience. Evidence:
      unauthorized capability details do not leak.
  - [ ] I6.13.a Define separate public/curated/AAA/operator capability and
        health projections with stable
        available/degraded/unavailable/not-configured/ unauthorized semantics.
        Evidence: projection schemas.
  - [ ] I6.13.b Derive effective capability from canonical audience/host policy,
        workflow/model/provider admission, dependencies, quota/capacity, and
        current health. Evidence: derivation tests.
  - [ ] I6.13.c Expose only high-level asset/operation/control availability and
        remediation appropriate to curated/AAA users. Evidence: lower-tier
        contract fixtures.
  - [ ] I6.13.d Expose bounded endpoint/worker/model/workflow dependency detail
        only to authorized operators with field-level controls. Evidence:
        operator contract tests.
  - [ ] I6.13.e Apply freshness/cache/stale/timeout/partial aggregation rules
        and never translate unknown/not-configured into healthy. Evidence:
        failure state tests.
  - [ ] I6.13.f Scan response/error/headers/telemetry for provider URLs, model/
        node IDs, capacity, tenant, credentials, and hidden features by
        audience. Evidence: capability leak suite.
        <!-- MEASURED 2026-08-17 (I6.13.a–f). The parent's bar is "unauthorized
        capability details do not leak", so the job is to find every capability
        answer an unauthorized caller can get and read what is in it. Instrument:
        `generate-isis-capability-health-api.mts` (+11 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-capability-health-api.json`.

        THE SKIP LIST WAS FOR A ROUTE THE MIDDLEWARE NEVER SEES. All three Isis
        services mount `authMiddleware` on `/api/*` — measured, one pattern each
        — and all three ALSO carry `skipRoutes: ['/health', '/ready']`. Both
        entries are vacuous, 6 in total: the middleware was never on those paths,
        so the skip list reads as a deliberate exemption for routes that had no
        guard to exempt them from.

        FIXED, AND DRIVEN ON ALL THREE. `/ready` answered with its evidence.
        generation-api handed any caller the storage bucket, its region and
        provider, the queue name, the Redis status and latency, the primary model
        provider with per-provider statuses, and the raw error string from the
        database, queue, storage and provider probes — 39 fields. output-registry
        gave the database probe latency and its raw error. workflow-registry gave
        the storage backend, the latency and its raw error, and driven, that
        error is "DATABASE_URL or ISIS_DATABASE_URL environment variable is
        required" — an anonymous caller was told which environment variable the
        deployment is missing. A READINESS VERDICT IS PUBLIC AND ITS EVIDENCE IS
        NOT: all three now gate the detail on an authenticated caller and keep
        the verdict, the status code and the named dependency ROLES public,
        because that is the contract an orchestrator needs and it names no
        resource. Driven at three levels per service — anonymous (7 fields on
        generation-api, 1 on the others, 0 identifying), operator (39, 5, 6), and
        a FORGED credential (401, not a silent downgrade to anonymous).

        THE GATE WAS ALREADY IN EACH SERVICE AND NOTHING USED IT. `optionalAuth`
        — `authMiddleware` with `allowAnonymous: true`, i.e. anonymous allowed
        and a presented credential verified — exists in all three middleware
        modules with zero callers. Two details it needs: its own `skipRoutes`
        must be cleared or it skips itself, and `requireProjectScope` must be off
        or an operator without a project claim is refused a health read.

        THE FIVE-STATE VOCABULARY EXISTS AND NO ISIS SURFACE USES IT.
        `@oshun/workbench-kit`'s `capability-state` declares configured,
        not_configured, degraded, unavailable, failed and enforces SIX invariants
        over them — a `configured` whose dependencies are not configured is
        refused ("a capability is not more available than the things it needs"),
        a `configured` that skipped one of the four verifications "is not
        configured, it is unverified", a `degraded` must name what still WORKS,
        an `unavailable` carrying a cached/fallback/stale value is refused by
        name, every detail field is scanned for credentials, and the module
        exports no boolean over the union so `degraded` cannot be rounded to a
        neighbour. 12 consumers, 0 of them on an Isis surface. The Isis health
        answers are `ok`/`error` and a boolean.

        MEASURED, NOT FIXED. (1) .a asks for four audience projections; there are
        now two (anonymous verdict, authenticated detail) and curated, AAA and
        operator are not distinguished from each other. (2) `/health` answers
        `status: 'ok'` as a CONSTANT whatever the dependencies say, and names the
        service and its version to an anonymous caller — liveness legitimately
        means "the process responds", so narrowing it is a decision, but the
        version disclosure is recorded. (3) .b's derivation reads database,
        queue, storage and provider liveness only — no audience/host policy, no
        workflow/model/provider admission, no quota or capacity. (4) The operator
        projection is all-or-nothing; .d's field-level controls do not exist.
        (5) .f is pinned by response-body assertions only; headers and telemetry
        are unscanned.

        TRAPS. (1) A COMMENT STRIPPER THAT DOES NOT KNOW ABOUT STRINGS EATS CODE:
        every service mounts `app.use('/api/*', authMiddleware(...))`, and the
        `/*` inside that PATH LITERAL opened a block comment to a regex stripper,
        which ran to the next `*/` and deleted everything between. The mount
        census came back EMPTY and its conclusion was right for the wrong reason
        — the only reason it was caught. The stripper now walks string literals,
        and the same correction was applied to the I6.11 and I6.12 instruments,
        whose numbers are unchanged by it. (2) A PHRASE THAT CROSSES A STRING
        CONCATENATION IS NOT IN THE SOURCE: two of the six kit invariants were
        matched on phrases written across a `+` join and reported as absent.
        (3) The trailing noun a slash list qualifies is not a member —
        `not-configured/unauthorized semantics` splits to "unauthorized
        semantics". (4) `apps/isis/workflow-registry`'s vitest suite OOMs at 8 GB
        on this box, at HEAD as well; its readiness fix is driven by the
        instrument's own probe instead.

        4 of 12 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`. .a
        needs four projections and the state vocabulary the estate already built;
        .b needs policy, admission and quota in the derivation; .c has no
        lower-tier projection at all; .d has the operator boundary and needs
        field-level controls inside it; .e needs freshness/stale/partial
        aggregation rules and a liveness answer that is not a constant; .f needs
        the scan extended past the response body. -->

- [ ] I6.14 Add consumer-driven contracts for all three hosts. Evidence:
      incompatible API change fails CI.
  - [ ] I6.14.a Inventory Isis operator, Yemaya AAA, and Oshun curated/gallery
        consumers, versions, routes, events, fields, actions, and audience
        assumptions. Evidence: consumer inventory.
  - [ ] I6.14.b Capture provider contracts for success, empty, partial, stale,
        degraded, not-configured, unauthorized, validation, conflict, rate/
        quota, and server failure cases. Evidence: contract fixtures.
  - [ ] I6.14.c Capture property absence/redaction, enums/discriminators, units,
        pagination/cursors, idempotency, versions, and error codes rather than
        example-only shapes. Evidence: schema assertions.
  - [ ] I6.14.d Verify each consumer contract against the real provider/service
        implementation and ratified source-of-truth registries in CI. Evidence:
        provider verification.
  - [ ] I6.14.e Verify deployed/provider version compatibility before consumer
        release and publish contract artifacts with ownership. Evidence:
        deployment gate.
  - [ ] I6.14.f Fail breaking removal/type/semantic/auth/audience changes while
        allowing explicitly compatible additive evolution. Evidence: deliberate
        breaking-change test.
        <!-- MEASURED 2026-08-17 (I6.14.a–f). The parent's bar is "incompatible API
        change fails CI", so the job is to find every check standing between an Isis
        provider change and the three hosts that consume it, then break the provider and
        watch what the checks say. Instrument:
        `generate-isis-consumer-driven-contracts.mts` (+14 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-consumer-driven-contracts.json`.

        TWO ISIS API DESCRIPTIONS, AND THE BREAKING-CHANGE GATE WATCHED THE ONE NOBODY
        SERVES. The three services each generate a spec from their own route sources:
        104 paths, all `/api/v1/...`, version 0.1.0 — and 126 of their 132 operations
        answer with `JsonObject`, `additionalProperties: true`, so the payload every
        consumer reads is described as "an object". The hand-authored
        `libs/openapi/src/specs/isis/isis-api.yaml` describes 17 `/v1/...` paths at one
        origin (`localhost:3003`, which is the workflow registry's port) with 21 named
        response schemas, version 1.0.0, and an enum saying `processing` where the
        implementation says `running`. ZERO paths in common. It feeds
        `libs/openapi/src/generated/isis.ts`, which nothing imports. The CI step "Check
        OpenAPI breaking changes" (`openapi:diff:check`) read only that family.

        DRIVEN AT HEAD — the deliberate breaking-change test .f asks for, performed.
        (1) Renamed `jobs.get('/:id/status')` to `/:id/state` in the generation-api
        routes and regenerated the spec with the estate's own generator: the poll route
        of both consuming hosts, gone. `openapi:diff:check` reported "All specs pass";
        route parity passed; cross-service passed. One check caught it — schema
        compatibility, because the SDK still calls the literal. (2) Removed
        `GET /api/v1/jobs` the same way — the route `apps/isis/web/src/pages/JobsPage.tsx:325`
        lists jobs with. EVERY gate passed: 4 gate runs, 9 tests green. Its SDK call is
        `this.transport.get<PaginatedResponse<Job>>(…)` and the check's `(?:<[^>]+>)?`
        cannot cross the inner `>`, so it is invisible. (3) On the spec the gate DOES
        read: a required response property removed, a type changed integer→string, an
        enum narrowed 6→4 and an operation's `security` removed — "✅ No breaking
        changes", 0 detected. `schema-breaking` was declared in the change union and
        never constructed, under a docstring promising "Removed properties from required
        response schemas".

        FIXED. (1) The gate watches the three service specs beside the domain specs —
        10, was 7. (2) `schema-breaking` and `auth-changed` are implemented: response
        property removal, type change, enum narrowing, required→optional; request
        optional→required and enum narrowing; security added, removed or changed —
        resolved through `$ref` chains, so a change to `Job.status` is reported at every
        operation that returns a `Job`. Additive evolution (new response property,
        widened response enum, new path or operation, new optional request property) is
        reported and passes. Re-driven: mutation (2) now fails with "GET operation
        removed from /api/v1/jobs", and (3) with 16 breaking changes naming each JSON
        path. (3) `libs/openapi/scripts/diff.spec.ts` — 20 cases, one mutation per class
        plus the additive cases that must NOT fail; 9 of them go red when the new
        detection is disabled. Wired into CI's api-contracts job BEFORE the gate, so a
        hollowed-out detector fails there rather than approving a diff. (4) The AAA
        store's remote-status map is now `Record<RemoteJobStatus, IsisJobStatus>` keyed
        on the union `@isis/client` publishes: widening that union with `'timeout'` now
        fails `apps/yemaya/studio-web`'s typecheck (TS2741). It was a `switch` over
        `string` with `default: return 'pending'`, and `startPollingJob` re-arms for
        every non-terminal status — an unrecognised TERMINAL status polled the provider
        forever and displayed as pending. (5) The same store wrote
        `stage: mapRemoteProgressStage(undefined)` — the mapper's only argument, ever,
        was the literal `undefined`, so the stage was the constant `'generating'` — and
        `stageProgress: snapshot.progress % 25`. `JobStatusSnapshot` has no stage field;
        both are gone, with 10 consumer-contract tests of which 4 fail against the old
        behaviour.

        MEASURED, NOT FIXED. (1) .a's third host has no provider seam:
        `apps/oshun/web/src` + `apps/oshun/bff/src` is 4,640 files, 0 importing
        `@isis/client`, 0 provider origins configured — the gallery is seeded from
        `@isis/output-gallery` and a durable snapshot store, so there is nothing to
        drive a consumer contract against. The two that do consume are `apps/isis/web`
        (96 files, 117 SDK methods) and `apps/yemaya/studio-web/src/stores/isisStore.ts`
        (1 file, 4 methods). (2) 0 of the 5 CI contract specs name any consumer host;
        they are provider-authored. (3) .b: 2 of 11 cases pinned — success and stale,
        both by `apps/isis/output-registry/src/openapi.contract.spec.ts`. (4) .c: 6 of 7
        property classes asserted, five of them by that same output-registry spec,
        idempotency by none — and the payloads themselves are open objects, including
        `GET /api/v1/jobs/{id}/status`. (5) .d: 0 of 5 boot a provider app; every check
        reads a YAML document or regexes a source file. (6) .d: 21 of 112 SDK transport
        calls are invisible to the one consumer-facing check, and `models.ts` is not
        among the three resource files it reads. (7) .e: no consumer pins a provider
        version.

        TRAPS. (1) A NESTED GENERIC HIDES THE ROUTE: `[^>]+` stops at the first `>`, so
        every paginated SDK call is unprotected. (2) A WORD IS NOT AN ASSERTION —
        `Partial<IsisJob>` reported .b's "partial" case as pinned, and `missingFromSpec`
        reported absence/redaction as pinned by five specs; each recogniser now names
        the status code or literal state value a contract must mention, and an
        unrecognised subject throws instead of counting as unpinned. (3) EVERY ISIS
        CONTRACT SPEC CONTAINS THE WORD ISIS: a host marker built from the host slug
        reported the operator as named by artifacts carrying no consumer expectation.
        (4) `someRecord.outputs.map(…)` is not an SDK call — the receiver has to be a
        client. (5) `apps/oshun/web/.next/standalone` is TRACKED and holds committed
        copies of the app's own sources; a census under `apps/oshun/web/src` that
        forgets it double-counts every file. (6) A comment that quotes the code it
        removed matches a substring test for that code — the fix pins assert on
        assignment forms instead. (7) `apps/isis/workflow-registry`'s vitest OOMs on a
        single spec file on this box, at HEAD as well (same as I6.13); its route-parity
        spec was not run locally and nothing here touches that app.

        3 of 13 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`. .a needs a
        consumer inventory naming each host's routes and fields, and one of the three
        hosts has no seam to inventory; .b has 2 of 11 cases; .c cannot bind property
        assertions to 126 operations that describe no properties; .d needs a check that
        runs the provider and a comparison that can see every SDK route; .e needs a
        version pin and a published artifact that describes deployed routes; .f's
        detector is now real and proven, but the audience clause has no expression in
        these documents and the payload classes have almost nothing to bind to on the
        surface the consumers actually call. -->
- [ ] I6.15 Add two-tenant integration coverage across run, blob, search,
      lineage, export, and recall. Evidence: no cross-tenant result or count
      leakage.
  - [ ] I6.15.a Provision two tenants with colliding human names, projects,
        filenames, tags, timestamps, model/workflow use, and distinct actors/
        roles/data. Evidence: isolation fixture.
  - [ ] I6.15.b Exercise plan/dispatch/job/progress/cancel/retry/replay/output/
        variant reads and direct foreign IDs/cursors/idempotency keys. Evidence:
        run isolation tests.
  - [ ] I6.15.c Exercise blob upload/download/signed URL/hash/cache/derivative/
        temporary object and guessed-path access. Evidence: storage isolation
        tests.
  - [ ] I6.15.d Exercise search/filter/sort/autocomplete/facets/counts/
        suggestions/index lag and foreign identifiers. Evidence: search
        isolation tests.
  - [ ] I6.15.e Exercise forward/reverse lineage, provenance ingredients,
        blast-radius, comparison, gate, audit, and aggregate counts. Evidence:
        graph isolation tests.
  - [ ] I6.15.f Exercise JSON/CSV/package/portable-spec/evidence/bundle exports,
        jobs, download URLs, filenames, metadata, and hidden sheets/entries.
        Evidence: export isolation tests.
  - [ ] I6.15.g Exercise quarantine/recall/unpublish/notification propagation
        and prove one tenant’s action cannot mutate or disclose the other.
        Evidence: recall isolation tests.
  - [ ] I6.15.h Inspect API/UI/error/timing/log/trace/event/cache/CDN/queue
        channels for result, identifier, existence, and count leakage. Evidence:
        cross-channel isolation report.
        <!-- MEASURED 2026-08-17 (I6.15.a–h). The parent's bar is "no cross-tenant result
        or count leakage", so the first question is not what the tests cover but whether
        the thing they would test exists: a read has to be able to name whose rows it is
        for before anyone can check that it named them right. Instrument:
        `generate-isis-tenant-isolation.mts` (+13 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-tenant-isolation.json`.

        TWO OF THE THREE SERVICES CANNOT STATE A TENANT, SO THEIR READS CANNOT WITHHOLD
        ONE. The `jobs` table has no owner, tenant, organization, workspace or project
        column — the tenant a submission carries is written into `metadata JSONB` by
        `withSubmissionMetadata` and never read back — and `JobListOptions`, the only
        filter the list read accepts, offers status, type, workflowId, page, limit,
        sortBy and sortOrder. `workflows` has `owner_id` and `visibility`, and
        `WorkflowListQuery` offers neither (its `author` filter is free text a caller
        chooses). The output registry is the exception: five isolation columns, five
        indexes over them, and an `OutputAccessScope` that reaches the SQL.

        SO THE READ SURFACE IS UNSCOPED, NOT WEAKLY SCOPED. Of 48 GET routes across the
        three services, 10 constrain by a subject — all of them in the output registry's
        `outputs.ts` — 13 have a role gate and no subject, and 25 have neither. Named,
        because a count is not a finding: every job read
        (`GET /`, `/:id`, `/:id/status`, `/:id/output`, `/:id/audio`,
        `/:id/queue-position`, `/stats`), so the route both consuming hosts poll on every
        job and the queue counts beside it take an id from the path and hand back
        `jobService.get(id)`; every model read including `/:id/download-url`, which is
        .c's signed URL; and the workflow reads including `/:id/export`, which is .f's.

        FIXED, AND DRIVEN THROUGH THE REAL APP. A SCOPE THE CALLER SUPPLIES IS NOT A
        SCOPE. The output registry's `resolveTenantScope` read `X-Isis-Tenant-Id`,
        `-Organization-Id`, `-Workspace-Id` and `-Project-Id` BEFORE the credential's own
        claims and compared them to nothing, so a request header chose which tenant
        `appendAccessScopeConditions` filtered to on the list, the cursor page, the
        projected page, `/stats`, lineage, ancestors and descendants — while
        `authMiddleware` in all three services applies the opposite rule to project
        scope, refusing a header that contradicts the claim ("Project scope mismatch")
        and letting the claim win. The claim wins now, a contradicting header is a 403
        before any store call, and a header is honoured only where the credential is
        silent, where it can narrow an owner-anchored query and never widen one. Driven
        at three settings through the booted app: contradicted → 403, agreeing → past the
        rule, absent → past the rule. Blast radius before the fix was bounded, and the
        report says so: every one of those reads is also anchored to `resolveOwnerId`,
        which 401s without an owner claim, so the header mislabelled a query rather than
        opening another tenant's rows. The estate had already decided this on the
        provenance WRITE path —
        `output.service.provenance-scope.spec.ts` asserts a ledger event is attributed to
        `tenant_of_record` and `not.toContain('tenant_supplied_by_caller')`.

        DRIVEN, NOT FIXED. With an injected recording database, `getStats({ownerId,
        tenantId})` emits `WHERE owner_id = $1 AND tenant_id = $2`; `getStats()` emits
        `SELECT COUNT(*) as count, COALESCE(SUM(size), 0) as size FROM outputs` — no WHERE
        clause at all. `appendAccessScopeConditions` adds one condition per dimension
        PRESENT and nothing for a dimension absent, so an unstated scope is an unlimited
        one. Fixing that is a fail-closed decision with a wide blast radius, not a
        cleanup.

        TRAPS. (1) A CHECK CONSTRAINT CONTAINS PARENTHESES: `type TEXT NOT NULL CHECK
        (type IN (…))` closes a paren before the table does, and a non-balanced scan of
        `CREATE TABLE jobs (…)` reports two columns. (2) A COLUMN ADDED BY AN ALTER IS
        STILL A COLUMN — `outputs` is created without `tenant_id` and gains it, with
        organization, workspace and project, through `ALTER TABLE … ADD COLUMN IF NOT
        EXISTS`; a census reading only the CREATE reports the one service that HAS tenant
        columns as having none. (3) A NECESSARY CONDITION CAN EXCLUDE AND CANNOT CONFIRM:
        19 Isis specs name two distinct `tenant_*` identifiers and almost none is an
        isolation test — they are budget-scenario labels (`tenant-quota-warn`), two halves
        of one signing key (`tenant-alpha-signing-key`,
        `tenant-alpha-signing-secret-12345`) and governance fixtures that touch no read
        path; the survivors were read and the three real artifacts recorded with the
        assertion that pins each. (4) `GET /` and `GET /:id` occur in more than one
        router, so a route label without its file reports duplicates. (5) An `interface X
        extends Y` hides half the filter: `OutputAccessScope`'s four tenant dimensions are
        inherited from `TenantScope`.

        1 of 7 properties present — .h's scope-source clause, which the fix closed. ALL
        EIGHT CELLS AND THE PARENT LEFT `[ ]`, and the reason is not test coverage: .a
        cannot provision two tenants against `jobs` or `workflows` because neither can
        record one; .b's run reads accept no subject to compare; .c/.d/.e/.f/.g would be
        testing an isolation boundary that exists in one of three services; .h's ten
        channels have one report between them, the operator app's
        `buildOutputIsolationAuditReport`, which audits API response bodies it was handed
        (reached by `OutputsPage` and `OutputsParityPage`) and no other channel. Giving
        Isis a tenant boundary is a schema-and-product decision — a column, a migration, a
        backfill of `metadata->>'tenantId'`, a filter on every read — and it is not a
        session's cleanup. -->



### I7 — Operator, AAA, and curated user experience

- [ ] I7.1 Refactor the Isis operator dashboard around queue health, provider
      health, release gates, spend, incidents, and actionable exceptions.
      Evidence: live-data Playwright journey.
  - [ ] I7.1.a Define the operator’s primary triage questions, canonical live
        projections, refresh/staleness semantics, permissions, and action
        ownership for every dashboard datum. Evidence: dashboard view contract.
  - [ ] I7.1.b Compose a restrained operations workspace with queue and incident
        attention first, supporting health/spend/gate context second, and no
        decorative hero or repeated card grid. Evidence: approved responsive
        layout spec.
  - [ ] I7.1.c Present queue age/depth/capacity/fairness, provider/circuit/
        maintenance state, gate blockers/freshness, spend/variance, and incident
        severity with explicit units and timestamps. Evidence: projection tests.
  - [ ] I7.1.d Build actionable exception lists grouped by owner/severity/
        deadline with stable filters, URLs, drill-down context, and bounded live
        updates. Evidence: exception interaction tests.
  - [ ] I7.1.e Gate pause/drain/retry/quarantine/budget/incident actions by
        exact object state, permission, confirmation, reason, and optimistic
        concurrency. Evidence: action authorization tests.
  - [ ] I7.1.f Render loading/empty/stale/degraded/partial/not-configured/error/
        disconnected states without implying green health. Evidence: state
        matrix tests.
  - [ ] I7.1.g Verify live incident-to-detail-to-remediation-to-recovery
        journey, keyboard/screen reader/zoom/responsive behavior, and
        high-volume performance in Playwright. Evidence: dashboard E2E.
        <!-- MEASURED 2026-08-17 (I7.1.a–g). The parent asks the dashboard to be organised
        around six subjects — queue health, provider health, release gates, spend,
        incidents, actionable exceptions — so the measurement is which of the six it can
        show, which the API can feed it, and what the one datum it did show actually meant.
        Instrument: `generate-isis-operator-dashboard.mts` (+10 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-operator-dashboard.json`.

        LIVENESS IS NOT READINESS. `DashboardPage.tsx` was 118 lines rendering three cards
        and three stage lanes from `client.checkHealth()`, which reads `/health` — the
        route I6.13 already recorded as answering `status: 'ok'` as a CONSTANT whatever the
        dependencies say. DRIVEN on the booted generation-api with no database configured:
        `/health` answers **200 status=ok** and `/ready` answers **503 ready=false** in the
        same second, naming all five roles it probed — jobsDatabase, modelsDatabase,
        provider, queue, storage — every one of them down. The page called that service
        `Healthy`, put `3/3` beside it and set its stage lane to the green `complete`.

        FIXED. `checkReadiness()` in `@isis/client` reads `/ready` per service and keeps the
        verdict when the transport throws on the 503 the route answers with, so the
        dependency map and per-check detail in that body reach the caller instead of
        becoming a bare "unreachable". The dashboard renders the readiness verdict as the
        answer, names the failing dependencies, keeps liveness as the separate smaller
        claim it is ("Processes responding"), says "Not measured" instead of `0/3` for a
        count it does not have, and derives the green stage state from a named function
        that returns it only for a `ready` verdict. 6 dashboard tests and 7 SDK tests; the
        count assertion and the latency assertion both go red against the old code.

        A LATENCY THAT IS NOT TIMED IS NOT A LATENCY. `checkServiceHealth` returned
        `latencyMs: 0` under the comment "Could be calculated if needed", and
        `apps/isis/cli/src/commands/health.ts:339` printed it to an operator as
        `Latency: 0ms` for every service on every run. It is timed now, on the failure path
        too.

        THE THREE SERVICES DO NOT ANSWER READINESS THE SAME WAY, and finding out cost a
        crash: only generation-api sends `criticalDependencies`, and it sends a
        role→verdict MAP, not the list of role names the first version of this instrument
        (and of the SDK type) assumed — `.join` threw. output-registry and
        workflow-registry send `ready` plus `checks` alone, and workflow-registry spells a
        failed check `failed` where the other two spell it `error`. The SDK now normalises
        both shapes into one `failingDependencies` list and reads both spellings. Also
        recorded: I6.13's comment said the public part of `/ready` names the dependency
        ROLES; it also carries their per-role booleans, which is more than roles and still
        names no resource.

        MEASURED, NOT FIXED. (1) 0 of the 6 subjects reach the page, and only 2 have a
        producer anywhere in the three services: queue health (`/api/v1/queue/stats`,
        `/jobs/stats`, `/jobs/admin/queue/health`, exposed as `getQueueStats` and
        `getQueueHealth`) and provider health (`/jobs/providers/operations`,
        `/providers/compatibility`). Release gates, spend, incidents and actionable
        exceptions have NO route at all — the dashboard could not show them however it were
        composed. (2) .e's six action verbs: the page offers none; of the six, two have a
        route (`POST /jobs/admin/queue/drain`, `POST /jobs/:id/retry`). (3) .f's eight
        states: 3 rendered (loading, empty, error); stale, degraded, partial,
        not-configured and disconnected have no representation. (4) .g: 0 of 13 operator
        Playwright specs mention the dashboard. (5) .a has no view contract and .b no
        approved layout spec.

        A NUMBER DERIVED FROM A HASH OF THE PROMPT IS NOT A MEASUREMENT — 21 unmeasured
        measurement-shaped fields survive on two operator pages, and both are outside this
        cell's subject but inside the quality bar:
        `apps/isis/web/src/pages/InteractiveGenerationPage.tsx:103` computes
        `latencyMs: 430 + (seed % 390)`, `consistencyScore: 86 + (seed % 11)` and
        `topologyScore: 82 + ((seed >>> 6) % 14)` from `hashText(prompt|style)` and renders
        them as a preview's latency and quality scores; and
        `apps/isis/web/src/pages/MeshTransformerAbComparisonPage.tsx` carries baked-in
        literal `topologyScore`, `manifoldScore`, `watertightScore`, `symmetryScore`,
        `faceCount`, `vertexCount`, `latencySeconds`, `peakMemoryGb` and
        `throughputMeshesPerHour` for three named 3D models and presents them as an A/B
        comparison. Both need a decision about what the page should show when no
        measurement exists; neither is a cleanup.

        TRAPS. (1) A VERB IN PROSE IS NOT A CONTROL: matching .e's action names against the
        whole page reported `retry` as offered, because the error state advises "Retry the
        readiness probe after network stabilization" — the census now reads button labels,
        `aria-label`s and `onClick` targets only. (2) AN ARROW FUNCTION IN AN ATTRIBUTE
        CONTAINS A `>`: `<button onClick={() => refresh()}>` ends a naive `<button[^]*?>`
        match at the arrow, and the label comes back as `refresh()}>Drain queue`; the tag
        ends at the first `>` outside braces. (3) `apps/isis/web` has a vite AND a vitest
        alias pointing `@isis/client` at the library source but no matching tsconfig path,
        so `tsc` resolves it through node_modules to `dist/index.d.ts` — a new SDK method
        is "not on type IsisClient" until the library is rebuilt (`npx tsup` from the
        library dir). Adding the tsconfig mapping instead pulls the SDK's Node-typed source
        into a browser app's check and produces 20 `Cannot find name 'process'` errors, so
        the mapping is NOT the fix.

        2 of 8 properties present — .c's health-datum provenance and .f's no-green-by-
        default, both closed by the fix. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: four of
        the parent's six subjects have no producer to build against, .e's actions are
        mostly routes that do not exist, .f needs five more states, and .g needs a
        Playwright journey against a live stack. -->

- [ ] I7.2 Complete the operator workflow-template registry and lifecycle UI.
      Evidence: draft through archive with review and audit.
  - [ ] I7.2.a Build searchable/filterable registry list and detail projections
        for identity/version, lifecycle, asset/operation/audience, owner,
        dependencies, evaluations, environments, and usage. Evidence: registry
        UI tests.
  - [ ] I7.2.b Build typed create/edit/version authoring for metadata, graph/
        subgraph refs, controls, compatibility, rights, resource estimates, and
        target profiles without arbitrary JSON. Evidence: authoring tests.
  - [ ] I7.2.c Show canonical graph validation, dependency locks, security/
        license scans, dry runs, benchmarks, diffs, and missing evidence before
        review. Evidence: validation view tests.
  - [ ] I7.2.d Implement submit/request-changes/approve/reject with reviewer
        role, comments, exact version/evidence binding, and immutable decisions.
        Evidence: review journey.
  - [ ] I7.2.e Implement promote/deprecate/revoke/quarantine/archive/restore
        actions with blast-radius preview, environment rules, confirmation, and
        audit. Evidence: lifecycle journey.
  - [ ] I7.2.f Present version history, current/admitted selections, consumers,
        runs, decisions, audit, and rollback options without rewriting history.
        Evidence: history tests.
  - [ ] I7.2.g Verify concurrency, stale evidence, direct URL/field/function
        authorization, accessibility, and large-registry performance. Evidence:
        adversarial Playwright suite.
        <!-- MEASURED 2026-08-18 (I7.2.a–g). The parent asks for a template registry that runs
        "draft through archive with review and audit", so the measurement asks what a template
        IS here before asking what a UI does with it. Instrument:
        `generate-isis-template-registry.mts` (+11 tests, determinism included), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-template-registry.json`.

        A NOUN WITH NO LIFECYCLE COLUMN HAS NO LIFECYCLE. `workflow_templates` has 13 columns
        and none of them is a status, owner, reviewer, version or environment; the served
        `WorkflowTemplate` declares no such field; the template API is four routes —
        `GET /templates`, `GET /templates/featured`, `GET /templates/:id`,
        `POST /templates/:id/create` — and the SDK has the matching four methods. Of .a's eight
        facets, 3 are servable (identity/version, asset+operation, usage) and 5 have no field at
        all: lifecycle, owner, dependencies, evaluations, environments. Of .d's four review verbs
        and .e's six lifecycle verbs, ZERO have a template route. The estate's lifecycle
        machinery is real but one level down, on WORKFLOWS: `draft|published|deprecated|archived`
        with `POST /workflows/:id/restore`, `/versions/:version/deprecate`,
        `/versions/:version/restore`, and publish/archive as `PATCH /:id` with
        `expectedUpdatedAt`. None of it reaches templates. Meanwhile
        `libs/contracts/src/common/workflow-template.ts` (1466 lines) declares exactly the
        lifecycle the cell asks for — `draft/review/approved/published/deprecated/archived` plus
        a review state `not_required/pending/approved/rejected/changes_requested`, risk level and
        portability state — and **0 files** outside it name any of those symbols.

        A BAN EXPRESSED IN A VOCABULARY THE MATCHER DOES NOT SPEAK IS NOT A BAN. The one seeded
        template declares two `block`-severity graph bans, `external-code-execution` matched with
        `classTypePattern: '(?i)(exec|shell|python|subprocess)'` and `unbounded-remote-fetch`
        matched with `inputKeyPattern: '(?i)(url|uri)', requiresAllowlist: true`.
        `matchesDefinition` implemented `hasNode`, `maxSteps` and `forbiddenInputKey` — and
        nothing else. All three declared keys were unreadable, so both bans answered "no match"
        for every graph that has ever been checked. `match` is typed `Record<string, unknown>`,
        so the compiler could not see it; `validateWorkflowTemplateCoverage`, whose whole job is
        to certify that a governance block is complete, checked patternId, description and
        severity and never asked whether the ban could fire. (`(?i)` is not JS regex syntax
        either: `new RegExp('(?i)x')` throws.)

        FIXED. The matcher implements the declared vocabulary, translates the leading inline flag
        group, treats `requiresAllowlist` with a per-rule `allowlistedClassTypes` exception list
        (absent list = no exceptions), and reports a pattern it cannot apply as
        `DISALLOWED_PATTERN_NOT_EVALUABLE` at the pattern's own severity — fail closed, never a
        silent clean graph. Coverage rejects an unevaluable ban (`UNEVALUABLE_GRAPH_PATTERN`) and
        `summarizeWorkflowTemplateCoverage` reports the verdict per ban. Red-check: with the old
        three-key vocabulary restored, 7 tests fail, including the estate's own pre-existing
        "ships bundled templates with explicit governance coverage".

        AN OVERLAY THE CALLER WRITES IS NOT AN OVERLAY THE TEMPLATE APPROVED. `createFromTemplate`
        did `{...template.definition, ...request.customizations}` where `customizations` is
        `z.record(z.unknown())`, then wrote it; and `enforceTemplateGovernance` — 539 lines whose
        own doc comment says it applies the guardrails "immediately before execution" — had ZERO
        non-test callers. DRIVEN on the booted route before the fix: one request with a
        `PythonScriptExec` node running `os.system("id")`, an `HttpFetch` node pointed at
        `http://169.254.169.254/latest/meta-data/`, a `/opt/models/secret.safetensors` model
        reference, and `template_governance: {disallowedGraphPatterns: [], portabilityChecks: []}`
        returned **201 Created** with all of it stored — four `block`-severity rules violated in
        one call. FIXED: the merged graph is judged by the template's own governance before
        anything is created, the overlay may not rewrite the governance block that judges it, and
        the route answers 422 `TEMPLATE_GOVERNANCE_BLOCKED` carrying the findings. The same
        request now answers 422 naming seven findings; a clean instantiation and a benign overlay
        still answer 201; a refusal does not increment the template's usage count. Parameter
        guardrails are deliberately NOT applied at instantiation — they grade run values, which do
        not exist yet — so a definition-only entry point was added rather than misusing the
        run-time one.

        THE REGISTRY PAGE DROPPED THE ENTIRE GOVERNANCE BLOCK THE SERVICE COMPUTES, and the SDK
        type dropped it before that: `WorkflowTemplate` in `@isis/client` had no `governance`
        field, so the page could not have rendered it. Both carry it now, and the page shows
        template version, class, coverage verdict, each declared ban with whether the registry can
        enforce it, portability checks, guardrails, the declared version change, and the refusal
        findings when a customization is rejected. It also NAMES the facets this registry does not
        record instead of leaving blank columns to be read as "fine"; the instrument checks that
        list against the served shape.

        MEASURED, NOT FIXED. (1) .b: there is no create/update/version route for a template at
        all, and the only authoring affordance is the free-form JSON overlay the cell explicitly
        forbids. (2) .c: dependency locks, security/license scans, dry runs and benchmarks have no
        producer; the only "diff" is the template's own self-declared `versionDiff`. (3) .d/.e: no
        review or lifecycle verb has a route, so there is nothing for a UI to call. (4) .f: a
        template has no version history — `governance.templateVersion` is a string parsed out of
        the definition blob. (5) .g: 0 of 13 operator Playwright specs mention templates.
        (6) `enforceTemplateGovernance` — the ONLY function that applies a template's parameter
        guardrails — still has zero production call sites, so those guardrails are enforced
        nowhere.

        TRAPS. (1) A ROUTE MODULE CAPTURES THE SERVICE SINGLETON AT IMPORT: calling
        `resetWorkflowService()` after importing `routes/templates.js` leaves the route talking to
        one instance while the census reads another — the usage count read 0 after a successful
        create. (2) A COUNTER IS NOT DETERMINISTIC ACROSS TWO BUILDS: the drive records usage
        deltas, never absolute counts, so `build()` twice in one process serializes identically.
        (3) `vite-tsconfig-paths` in `apps/isis/workflow-registry/vitest.config.ts` OOMs this box
        on ANY spec in that project, including untouched ones, at a 6 GB heap; the suite runs in
        1.3s with an explicit alias list instead. (4) `apps/isis/web` typechecks `@isis/client`
        through `dist`, so `npx tsup` in `libs/isis/client` is required before a new SDK field
        exists for the page.

        3 of 12 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: five of .a's eight
        facets have no field to serve, .b has no typed authoring path, .c's scans and dry runs have
        no producer, .d and .e have no routes to call, .f has no version history, and .g has no
        Playwright coverage. -->
- [ ] I7.3 Complete the operator model/model-version intake and quarantine UI.
      Evidence: license, scan, evaluation, promotion, and revoke journey.
  - [ ] I7.3.a Build model/version intake for immutable source/artifact hash,
        architecture/format/runtime, files, dependencies, owner, license/model
        card, intended uses, and provider compatibility. Evidence: intake tests.
  - [ ] I7.3.b Present upload/remote retrieval progress and malware/signature/
        format/safe-parser scan results with quarantined-by-default state.
        Evidence: scan journey.
  - [ ] I7.3.c Present license, training-data provenance, rights restrictions,
        model-card completeness, security risks, and exact blocking gaps.
        Evidence: evidence view tests.
  - [ ] I7.3.d Configure and track calibrated quality/safety/performance/
        compatibility evaluations, fixtures, results, uncertainty, and failures.
        Evidence: evaluation journey.
  - [ ] I7.3.e Compare versions/models on authorized measurements, target fit,
        latency/cost/resources, rights, and risk without hiding unavailable
        data. Evidence: comparison tests.
  - [ ] I7.3.f Implement review/promote/deprecate/quarantine/revoke/archive with
        exact artifact/evidence binding, environment progression, blast-radius,
        reason, and confirmation. Evidence: lifecycle journey.
  - [ ] I7.3.g Show immutable provenance/decisions/audit/consumers/runs and
        verify stale-version, supply-chain, mass-assignment, accessibility, and
        performance cases. Evidence: model registry E2E.
        <!-- MEASURED 2026-08-18 (I7.3.a–g). The parent's evidence is a "license, scan,
        evaluation, promotion, and revoke journey", so the measurement asks which of those five
        nouns the registry can hold before asking what the console does with them. Instrument:
        `generate-isis-model-registry.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-model-registry.json`.

        A VERDICT FROM AN INSTRUMENT THAT CANNOT SEE THE THING IS NOT A VERDICT. Model upload
        scanning ships ENABLED, FAIL-CLOSED, on engine `builtin`. `BuiltinSecurityScanner` emits
        no `malware`-typed threat anywhere — the only `type: 'malware'` in the security library is
        inside `ClamAVScanner` — and the upload gate blocks on
        `threats.filter(t => t.type === 'malware')`, so on the default engine that predicate could
        never be true. Every upload recorded `malwareScan.status: 'clean'`. DRIVEN on the real
        scanner: a safetensors-shaped buffer (8-byte header length, JSON header, 256 KB of chained
        sha256 bytes standing in for tensors) comes back `clean: false, severity: high` with 21
        threats — 10 `sql_injection`, 10 `command_injection`, 1 `suspicious_pattern`, every one a
        false positive on binary content — while a pickle that calls `posix.system` comes back
        `clean: true` with ZERO threats. The engine is not a weak malware scanner; it is a
        source-text pattern matcher pointed at a binary, which is also why the original
        `type === 'malware'` narrowing was there: reading its verdict would refuse every real
        upload.

        FIXED. The gate now asks whether the configured engine can produce the verdict it is
        about to record. `MALWARE_CAPABLE_SCAN_ENGINES = {clamav}`; with `builtin` and
        fail-closed (the shipped default) the upload is REFUSED naming the engine and the two
        environment variables that resolve it, and with fail-closed off it completes recording
        `status: 'unsupported'` — never `clean`. A capable engine still blocks on a malware
        verdict. Driven: 5/5 cases hold. The two pre-existing specs that configured
        `ISIS_MODEL_MALWARE_SCAN_ENGINE: 'builtin'` while injecting a scanner returning
        ClamAV-shaped malware threats now name `clamav`: they were passing against a double that
        contradicted the engine they configured.

        A SCAN VERDICT LIVES IN EXACTLY ONE PLACE AND IT IS NOT THE MODEL. `model_registry` has 27
        columns and not one is a scan status, a quarantine, a license, a model card, an
        evaluation or an environment; the only dispositions are `is_active` and `is_verified`
        (and I1.2.c already recorded that `is_verified` has no writer). The verdict exists only
        inside the `model.upload.completed` audit entry's `metadata.malwareScan`. The console now
        reads it there — `ModelsPage` queries
        `getAuditLogs({modelId, action: 'model.upload.completed'})` and renders each verdict
        through `describeModelScanVerdict`, which names `skipped`, `unsupported` and `failed` as
        NON-verdicts rather than green — states plainly that the record itself carries no scan or
        quarantine state, and lists the intake facts this registry does not record.

        THE RICH TABLES EXIST AND NOTHING READS THEM. `libs/isis/database/prisma/schema.prisma`
        declares `model_cards` (32 fields: licenses, hashes, overview, usage, training,
        evaluation, limitations, ethics, safety, governance, citations, changelog) and
        `model_versions` (28 fields: source, lineage, files, licenses, hashes, runtime, telemetry,
        safety, governance, deployments) — exactly .a's files/license/model-card/intended-uses and
        .c's license/provenance/rights/model-card subjects. Product readers: **0 and 0**. No code
        outside the generated Prisma client calls `.modelCard.*` or `.modelVersion.*`.

        MEASURED, NOT FIXED. (1) .a: 5 of 8 intake subjects are servable; files, license/model
        card and intended uses have no field on the served record and live only in the unread
        tables. (2) .b: no quarantine state exists — there is no column, no route and no default;
        upload progress, signature, format and safe-parser scans have no producer at all. (3) .c:
        1 of 6 evidence subjects is servable (`trainedOn`, a free-text string). (4) .d: 0 of 3
        evaluation subjects servable; no evaluation route. (5) .e: no comparison route among the
        10 model routes. (6) .f: 0 of 6 lifecycle verbs — review, promote, deprecate, quarantine,
        revoke, archive — has a route or a control; the console offers Register, Save, Generate
        Upload URL, Complete Upload, Generate Download URL, Delete. (7) .g: 0 of 13 operator
        Playwright specs navigate to `/models`.

        TRAPS. (1) A WORD IS NOT A ROUTE: matching `/model/i` over the e2e specs reported 11 of 13
        as coverage, because the asset-generation specs say "model" in prose; only a `goto('/models`
        is coverage of this registry, and there are none. (2) A TEST DOUBLE THAT CONTRADICTS ITS
        OWN CONFIGURATION HIDES THE DEFECT — the malware-blocking specs configured the engine that
        cannot detect malware and injected one that can. (3) The drive must not use
        `randomBytes`: a chained-sha256 stream gives the same 21 threats every run, so the census
        is byte-stable.

        1 of 12 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: the substrate has no
        column for quarantine, license, model card, evaluation or environment; the lifecycle verbs
        have no routes; and the tables that do carry those facts have no reader. -->
- [ ] I7.4 Complete the provider endpoint and failover UI. Evidence: health,
      circuit state, maintenance, and routing reasons are live.
  - [ ] I7.4.a Build endpoint/pool/provider topology views with class, region,
        environment, capability versions, model/node images, GPU/resources,
        capacity, lifecycle, owner, and last update. Evidence: topology tests.
  - [ ] I7.4.b Present probe/queue/worker/provider health, freshness, partial/
        unknown/not-configured states, incidents, and SLO history separately.
        Evidence: health state tests.
  - [ ] I7.4.c Present per-family circuit closed/open/half-open state, counted
        failures, thresholds, cooldown/probes, and recent decisions. Evidence:
        circuit view tests.
  - [ ] I7.4.d Explain routing candidates, filters, selected endpoint, policy,
        capability/residency/cost/latency/capacity reasons, and audience-safe
        redactions. Evidence: routing inspector tests.
  - [ ] I7.4.e Implement authorized drain/restore/disable/probe/canary/failover
        actions with in-flight preview, deadlines, reason, expected version, and
        audit. Evidence: maintenance journey.
  - [ ] I7.4.f Stream live changes with cursor/reconnect/dedup/stale handling
        and verify failover, race, direct-action authorization, accessibility,
        and large-fleet performance. Evidence: controlled failover Playwright
        test.
        <!-- MEASURED 2026-08-18 (I7.4.a–f). The parent's evidence is "health, circuit state,
        maintenance, and routing reasons are live", so the measurement asks for each of the four
        whether a producer exists before asking what the console shows. Instrument:
        `generate-isis-provider-failover.mts` (+8 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-provider-failover.json`.

        AN ORDERING CONVENTION IS A DEFECT WAITING FOR A CALLER. `evaluateEndpointHealth`
        documented "Newest outcomes first" and implemented `.slice(-window.samples)` — the last N
        of an OLDEST-first array. DRIVEN: four recent failures and four older successes decide
        `unhealthy` when the caller hands them over newest-first and `healthy` when it hands the
        same eight over oldest-first. The one in-repo caller appends to the tail, so it was right
        by accident; anybody obeying the documentation was judging an endpoint on its stalest
        probes. FIXED: the window is the newest N by the outcomes' own `observedAtUnixSeconds`,
        with ties keeping the later array position, so the array order cannot change the verdict.
        Red-check: restoring `.slice(-window.samples)` fails the equivalence test.

        AN ENDPOINT MODEL WITH NO ROUTE IS NOT A TOPOLOGY. `libs/isis/ai-providers/src/
        provider-endpoints` models what .a asks for — provider, region, capabilities,
        modelClasses, cost, concurrency, retry, secretRotation, health, failoverPriority,
        failoverCompanions, lastHealthCheckAtUnixSeconds — and exports twelve entry points.
        Resolved BY IMPORT, ONE has a caller outside its own module: `planIsisRoute` in
        `libs/isis/study-adapter`, a package with ZERO importers. Of .a's ten topology subjects,
        2 are served (class, last update) and 5 more are modelled by `IsisProviderEndpoint` and
        reach no route; environment, GPU/resources and owner are modelled nowhere. The console's
        entire provider surface is one `GET /jobs/providers/operations` snapshot, polled every
        5s, with one control: Refresh snapshot.

        MEASURED, NOT FIXED. (1) .c: the only circuit state the API exposes is
        `RunPodCircuitBreakerState` — one provider, not "per family" — carrying state, failures,
        successes and nextAttempt; thresholds and recent decisions have no field. (2) .d: no
        routing inspector; `planIsisRoute` computes candidates, filters and a selected endpoint
        and no route serves it. (3) .e: 0 of 6 maintenance verbs — drain, restore, disable,
        probe, canary, failover — has a route or a control; the three provider-shaped routes are
        all GETs. (4) .f: the page polls, it does not stream, and 0 of 13 operator Playwright
        specs navigate to `/providers`.

        TRAPS. (1) A NAME IS NOT AN IMPORT: counting callers by function name reported three for
        `decideRetry` and one for `evaluateSecretRotation`, all in `@oshun/workbench-kit`,
        `@euterpe/elevenlabs`, `@euterpe/api` and `@neith/security`, which export their own
        functions of those names and never touch this module. Callers are resolved by import
        specifier. (2) A DOC COMMENT THAT QUOTES THE OLD CODE STILL CONTAINS IT — an instrument
        test asserting the absence of `.slice(-window.samples)` fails on the prose that explains
        why it was removed; assert on the code shape, not the substring.

        1 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: eight of .a's ten
        topology subjects reach no route, .c has thresholds and decisions nowhere, .d has no
        inspector, .e has no action routes at all, and .f neither streams nor has a failover
        Playwright test. -->
- [ ] I7.5 Complete the job/run explorer with stage logs, resource use, cost,
      output, retry, cancel, and replay. Evidence: injected failure journey.
  - [ ] I7.5.a Build deterministic search/filter/sort/pagination for request,
        plan, job, run/attempt, workflow/model/provider, actor/project, status,
        asset kind, cost, date, and incident. Evidence: explorer list tests.
  - [ ] I7.5.b Build run detail timeline/DAG with queue, reservation, stage,
        checkpoint, attempt, provider/tool, output, gate, decision, and terminal
        state identities. Evidence: journey projection tests.
  - [ ] I7.5.c Present structured redacted stage events/logs/errors with levels,
        timestamps, correlations, retries, downloadable evidence, and bounded
        streaming. Evidence: log view/security tests.
  - [ ] I7.5.d Present estimated/reserved/actual
        CPU/GPU/VRAM/storage/egress/time/ cost by stage and run, including
        pending/unbilled/variance. Evidence: resource/cost view tests.
  - [ ] I7.5.e Present output previews/manifests/hashes/provenance/gates/
        quarantine/partial/missing state appropriate to each asset kind.
        Evidence: output projection tests.
  - [ ] I7.5.f Implement cancel/retry/replay with state/role eligibility,
        impact/cost preview, reason/confirmation, concurrency, and immutable new
        attempt/run lineage. Evidence: command tests.
  - [ ] I7.5.g Verify injected provider refusal/timeout/worker loss/malformed
        result/late output/recovery plus cross-tenant, accessibility, and long-
        history performance behavior. Evidence: failure Playwright journey.
        <!-- MEASURED 2026-08-18 (I7.5.a–g). The parent's evidence is an injected failure journey
        over stage logs, resource use, cost, output, retry, cancel and replay, so the measurement
        asks which of those the served job has a field for. Instrument:
        `generate-isis-job-explorer.mts` (+7 tests), wired into
        `verify:inventory:v1-workbenches`, writing `evidence/v1-workbenches/isis-job-explorer.json`.

        A BUNDLE BUILT TO LEAVE THE CONSOLE IS THE ONE THAT MUST BE REDACTED. The explorer's
        "Support Bundle" and "Report Issue" buttons serialized the job VERBATIM into a block for
        an operator to copy into a ticket: `input` with the prompt and negativePrompt, the
        presigned `inputUrl`, the `callbackUrl`, `metadata` and the error string. The audit view
        one route away states "PII and sensitive prompt fields are automatically redacted in this
        view" and calls `redactSensitiveData` to do it — so the estate had both the policy and
        the function, and applied them to the surface that stays on screen and not to the artifact
        built to be exported. FIXED: the builder moved to `jobs-utils.ts` and redacts prompts,
        nested secrets, personal data and URL credentials while keeping ids, statuses, progress
        and timestamps. DRIVEN: a job whose prompt contains an email and a card number, whose
        callbackUrl carries `token=sk-live-…`, whose parameters carry an inline `apiKey` and whose
        inputUrl is presigned now yields a bundle containing none of the eight secrets and all
        four identifiers.

        A PRESIGNED URL CARRIES ITS CREDENTIAL UNDER A VENDOR PREFIX. `redactSensitiveExportUrl`
        compared raw query keys against a fixed list, so `X-Amz-Signature`, `X-Amz-Credential`,
        `X-Amz-Security-Token` and `X-Goog-Signature` — every credential MinIO and S3 actually
        mint — passed through untouched, in the output export centre's NINE call sites as well as
        here. Keys are now normalized (vendor prefix stripped, separators removed) before an exact
        comparison, so those match and `keyframe`/`monkey` still do not. Red-check: 4 tests fail
        against the old comparison.

        A JOB IS A REQUEST AND A STATUS, NOT A RUN. The served `Job` is id, type, status,
        priority, progress, input, output, error, callbackUrl, metadata, workerId and four
        timestamps. Of .b's ten run-detail identities 4 are served; reservation, stage,
        checkpoint, attempt, gate and decision have no field anywhere. `JobListOptions` is
        `status`, `type`, `sortBy`, `sortOrder` — so 3 of .a's eleven filter subjects are
        supported and request, plan, run/attempt, actor/project, cost and incident are not.
        0 of .d's five cost and resource subjects are served and the page does not mention cost.
        Of .f's three commands, cancel and retry have routes and REPLAY has none; retry lineage
        rides in the client-supplied metadata key `retriedFromJobId`, not a column, so nothing
        joins on it. 20 job routes exist and none serves events, logs or stages. 0 of 13 operator
        Playwright specs navigate to `/jobs`.

        NOT A DEFECT, CHECKED: the console's retry eligibility (`failed | cancelled`) is exactly
        the server's, and `retryJobWithFallback` falls back to a fresh submit only on 404/405 and
        does carry `retriedFromJobId`.

        TRAPS. (1) A page util that imports `@/…` is not importable by a plain `tsx` run — the
        instrument is pointed at the web app's tsconfig (`--tsconfig apps/isis/web/tsconfig.json`)
        so its drive can call the real builder. (2) Blanket-redacting the whole bundle would eat
        the job id through the credit-card pattern; the fix redacts the sensitive parts and keeps
        the identifiers, which is what makes a support bundle useful.

        2 of 9 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: the run identities,
        the cost subjects, the log producer and the replay verb do not exist, and there is no
        failure Playwright journey. -->
- [ ] I7.6 Complete the output comparison workspace for each supported asset
      kind. Evidence: synchronized, accessible comparison tests.
  - [ ] I7.6.a Resolve authorized exact candidate revisions and common typed
        comparison/provenance/gate/decision state with incompatible-kind
        handling. Evidence: workspace contract tests.
  - [ ] I7.6.b Implement synchronized image/portrait/texture pan/zoom/fit/pixel/
        alpha/diff and region annotation. Evidence: raster comparison tests.
  - [ ] I7.6.c Implement synchronized video/animation/sequence frame/timecode/
        playback/rate/loop/audio/caption and shot/pass alignment. Evidence:
        temporal comparison tests.
  - [ ] I7.6.d Implement synchronized audio/voice/music playback/waveform/
        loudness/channel/stem/transcript and time-region annotation. Evidence:
        audio comparison tests.
  - [ ] I7.6.e Implement synchronized mesh/rig/splat/scene
        camera/light/material/ animation/LOD/wireframe/bounds and object-region
        annotation. Evidence: 3D comparison tests.
  - [ ] I7.6.f Present raw gate values/units/thresholds/uncertainty, prompt/
        workflow/model/policy diffs, rights, provenance, cost, and target fit.
        Evidence: metadata comparison tests.
  - [ ] I7.6.g Implement select/reject/park/regenerate/comment decisions against
        exact revisions with stale/concurrent state handling. Evidence: decision
        tests.
  - [ ] I7.6.h Provide text/table alternatives to every canvas, full keyboard/
        screen-reader/zoom/reduced-motion parity, responsive inspection, and
        large-media performance. Evidence: synchronized accessible E2E.
        <!-- MEASURED 2026-08-18 (I7.6.a–h). The parent asks for a comparison workspace "for each
        supported asset kind" with SYNCHRONIZED inspection per modality, so the measurement is
        which synchronization affordance each of .b–.e names exists as a control. Instrument:
        `generate-isis-comparison-workspace.mts` (+7 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-comparison-workspace.json`.

        AN AVERAGE OF NOTHING IS NOT ZERO. `calculateAverageConsistencyScore` returns 0 for an
        empty metric set — the same number it returns when every metric scored zero — and the
        workspace's key-metric tile rendered that as "Overall Score 0.0%" before either side of
        the comparison had been chosen, which reads as the worst possible result rather than as
        no result. FIXED: `formatConsistencyScorePercent` answers "Not measured" for an empty set
        and the section states how many metrics were computed; the numeric function is unchanged
        because twenty-six comparison profiles fold it into their own report score. Driven: empty
        → "Not measured", one metric scoring 0 → "0.0%", two averaging 0.75 → "75.0%".

        A PRESET IS NOT A VIEWER. The workspace is 4969 lines carrying 32 "Apply <asset kind>
        Preset" buttons over 32 `*-comparison-utils` profiles whose consistency reports are real
        computations — jaccard over tags and name tokens, type equality, metadata identity
        comparison — and then it shows the two outputs as a plain `<img>`, `<video>` or
        `<audio>`. There is NO canvas anywhere in it. Of the 26 synchronization affordances
        .b–.e name — pan, zoom, fit, pixel, alpha, diff, frame, timecode, playback, rate, loop,
        caption, waveform, loudness, channel, stem, transcript, camera, light, material,
        animation, LOD, wireframe, bounds and the two annotation kinds — exactly ONE is a
        control: Add Annotation. Of .g's five decisions only comment has one; select, reject,
        park and regenerate do not. Of .f's twelve metadata subjects 3 are presented — prompt,
        workflow and model diffs, as "A vs B" lines — and gate values, units, thresholds,
        uncertainty, policy diffs, rights, provenance, cost and target fit are not. 0 of 13
        operator Playwright specs open the workspace.

        TRAPS. (1) AN ASSET-KIND PRESET IS NOT A VIEWER AFFORDANCE: counting the preset buttons
        as controls reported a temporal `frame` stepper (from "Apply Storyboard Frame Set
        Preset") and a 3D `material` toggle (from "Apply Material PBR Pack Preset"); the preset
        controls are excluded from the affordance vocabulary. (2) "Reset Selection" is not a
        select DECISION — decision verbs match whole words only. (3) A LOCAL VARIABLE IS NOT A
        PRESENTATION: testing the metadata markers against the module reported "unit" as
        presented because a byte-size formatter declares `const units = ['KB','MB','GB','TB']`
        two thousand lines above the surface. (4) A COMPARISON OPERATOR LOOKS LIKE A TAG
        BOUNDARY: extracting rendered text with `>([^<>{}]+)<` pulls in `>= 1024 && unitIndex <`
        from that same formatter, so the extractor is anchored on named text-bearing tags.
        (5) "Costume Outputs" is not a cost — whole-word matching again.

        3 of 10 properties present. ALL EIGHT CELLS AND THE PARENT LEFT `[ ]`: 25 of 26
        synchronization affordances, 4 of 5 decisions and 9 of 12 metadata subjects have no
        surface, and there is no accessible comparison E2E. -->
- [ ] I7.7 Complete the release-gate dashboard with freshness and remediation.
      Evidence: stale or unavailable evidence cannot look green.
  - [ ] I7.7.a Build candidate/target gate-set projection with mandatory/
        advisory/N/A/unavailable/error/manual states, raw measurements,
        uncertainty, overrides, and exact versions. Evidence: projection tests.
  - [ ] I7.7.b Present overall readiness as a derived explanation with counts
        and blockers, never as a standalone color or client-authored boolean.
        Evidence: readiness tests.
  - [ ] I7.7.c Calculate and display subject/evaluator/registry/profile/policy/
        rights/consent freshness and exact invalidating changes. Evidence:
        freshness fixtures.
  - [ ] I7.7.d Provide filters/grouping by
        category/owner/severity/target/status, accessible measurement details,
        evidence links, regions/timecodes, and comparison. Evidence: dashboard
        interaction tests.
  - [ ] I7.7.e Route each blocker to authorized remediation, rerun, review, or
        exception request with impact/cost, state eligibility, and immutable
        outcomes. Evidence: remediation journey.
  - [ ] I7.7.f Test stale/unavailable/error/unknown/cancelled/partial data,
        direct release attempt, concurrent evidence change, accessibility, and
        large gate matrices. Evidence: fail-closed Playwright suite.
        <!-- MEASURED 2026-08-18 (I7.7.a–f). The parent's evidence clause is "stale or
        unavailable evidence cannot look green", which presumes a surface on which evidence
        could look green. Instrument: `generate-isis-release-gate-dashboard.mts` (+5 tests),
        wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-release-gate-dashboard.json`.

        THERE IS NO RELEASE-GATE SURFACE. The operator console mounts 23 routes and NONE of them
        is a gate, release or readiness view, so .a's projection, .d's filters and grouping, .e's
        remediation routing and .f's fail-closed suite have nothing to attach to. Of .e's four
        remediation destinations — remediate, rerun, review, exception — none has an API route
        either, and 0 of 13 operator Playwright specs open a gate route.

        A REQUIREMENT NOBODY ASKED FOR IS NOT A REQUIREMENT THAT PASSED. The readiness model that
        does exist, `buildIsisReleaseReadiness` in `libs/oshun/generation-control-isis`, computed
        `const modelVerified = input.model ? input.model.releaseState === 'eligible' : true` — a
        request that names no model reported the SAME value as a verified, release-eligible one —
        and `provenanceReady: true` whenever a new output was expected. .a asks a gate projection
        to carry an N/A state distinct from a pass, and both of those were passes. FIXED
        additively: the readiness object carries `notApplicableRequirements`, populated with
        `model_not_named`, `provenance_pending_new_output` and `quality_gate_not_required`.
        DRIVEN: no model → `modelVerified=true` with `model_not_named` recorded; a named eligible
        model → `modelVerified=true` with nothing recorded. 20/20 test files in that library stay
        green.

        MEASURED, NOT FIXED. (1) .a: 5 of the 6 states are representable (mandatory via
        `missingRequirements`, N/A via the new list, manual via `humanReviewRequired`, plus
        unavailable and error in the type vocabulary) and ADVISORY is not — every requirement is
        a blocker or nothing. Raw measurements, uncertainty and exact versions do not travel with
        the verdict at all: the readiness object is booleans, reasons and requirement lists.
        (2) .c: 0 of the 7 freshness subjects — subject, evaluator, registry, profile, policy,
        rights, consent — has an instant compared anywhere in the readiness path; the gate-side
        measurement of the same absence is `evidence/v1-workbenches/isis-gate-result-freshness.json`
        (I6.8), which recorded that the contract compares instants 14 times and never the one
        that decides publishing. (3) The one gate that could pass, `modelVerified`, requires
        `isVerified && hash`, and I1.2.c recorded that `is_verified` has NO WRITER — so no model
        in the registry reaches `eligible` by any path.

        2 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: there is no dashboard,
        no freshness calculation, no advisory state, no remediation route and no fail-closed
        suite. -->
- [ ] I7.8 Complete the provenance inspector and recall workflow. Evidence:
      tamper, revoke, and blast-radius journey.
  - [ ] I7.8.a Build a typed lineage graph/list projection for sources/
        references, briefs/specs, workflows/nodes, models/adapters, runs/stages,
        outputs/edits, credentials, bundles, collections, and releases.
        Evidence: inspector contract tests.
  - [ ] I7.8.b Render forward/reverse paths with stable identities/versions/
        hashes, relationship reasons, redactions, cycles, pagination, and a
        complete noncanvas tree/table alternative. Evidence: lineage UI tests.
  - [ ] I7.8.c Verify artifact/manifest/credential signatures/hashes and show
        valid/invalid/missing/stripped/unknown status with tool/version/time.
        Evidence: tamper journey.
  - [ ] I7.8.d Build blast-radius query/preview for
        source/consent/license/model/ workflow/output changes with
        direct/transitive items, targets, owners, actions, and unknowns.
        Evidence: preview tests.
  - [ ] I7.8.e Implement authorized quarantine/recall case creation, scoped
        confirmation, progress, partial failures, target receipts, notification,
        and restoration review. Evidence: recall journey.
  - [ ] I7.8.f Enforce sensitive provenance field controls, tenant isolation,
        large-graph performance, keyboard/screen-reader navigation, and
        immutable case history. Evidence: security/accessibility tests.
        <!-- MEASURED 2026-08-18 (I7.8.a–f). The parent's evidence is a "tamper, revoke, and
        blast-radius journey", so the measurement asks for each of the three whether the operator
        console can reach it. Instrument: `generate-isis-provenance-inspector.mts` (+6 tests),
        wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-provenance-inspector.json`. No fix in this cell: everything
        that exists is correct, and everything else is absent.

        A LINEAGE OF OUTPUTS IS NOT A LINEAGE OF THE PIPELINE. `LineageNodeType` is
        `source | derived | composite` and every node carries an `outputId`, so of the ten
        families .a asks a lineage projection to carry — sources/references, briefs/specs,
        workflows/nodes, models/adapters, runs/stages, outputs/edits, credentials, bundles,
        collections, releases — exactly ONE is expressible. Five edge types and seven provenance
        routes exist (`GET/POST /outputs/:id/provenance`, `GET /outputs/:id/lineage`,
        `/ancestors`, `/descendants`, `POST/DELETE /outputs/lineage…`).

        THE INSPECTOR EXISTS, IN THE OTHER PRODUCT. `apps/oshun/web` has an
        `IsisProvenanceInspector` fed by a real chain verification: `verifyProvenanceLedger`
        reports `ok` and the index the chain broke at, and the Oshun server loader is its one
        product consumer. The Isis operator console has NO inspector, NO verification status, NO
        blast-radius preview and NO recall case — its entire provenance surface is the
        register/update forms and a single "Refresh Lineage" button on the outputs page. 0 of
        .c's five statuses (valid/invalid/missing/stripped/unknown) are shown against a
        signature, hash or integrity check; 0 of .e's seven case steps are named; 0 recall or
        quarantine routes exist anywhere in the Isis services; 0 of 13 operator Playwright specs
        mention provenance, lineage or recall.

        CHECKED AND FOUND CORRECT — recorded so a later pass does not re-flag them. (1) The
        lineage walk keeps a `visited` set and a depth cap, so a cyclic graph terminates.
        (2) The lineage route resolves an owner and a tenant scope before reading. (3) The
        fabricated "broken ledger" verdict in the Oshun loader is reachable only through
        `FixtureIsisProvenanceLedgerStore`, which is returned only when
        `OSHUN_ENABLE_TEST_HARNESSES=true` AND `OSHUN_ISIS_PROVENANCE_LEDGER_FIXTURE=clean` — a
        gated test double at a dependency boundary, which the quality bar explicitly permits.

        TRAP. A STATUS WORD IS NOT A VERIFICATION STATUS: matching the bare literals credited
        `'unknown'` (an id fallback at OutputsPage:850) and `'missing'` (a bundle-presence table
        cell) as verification statuses; a status counts only where it sits beside a signature,
        hash, integrity or verification word.

        3 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: nine of .a's ten
        lineage families cannot be nodes, .c has no verification surface in this console, .d has
        no blast radius at all, .e has no recall route, and .f has no case history to be
        immutable. -->
- [ ] I7.9 Integrate AAA brief, graph, model, training, voice, music, 3D, and
      post-process surfaces into Yemaya Studio navigation. Evidence: no
      duplicated Isis operator controls.
  - [ ] I7.9.a Map each AAA surface to a Yemaya project/asset/shot/sequence
        context, authorized roles, Isis route/capability, and return location.
        Evidence: navigation integration matrix.
  - [ ] I7.9.b Add restrained project-level workbench navigation and command
        palette entries for brief, graph, model, training, voice, music, 3D, and
        post-process without a second global nav. Evidence: navigation tests.
  - [ ] I7.9.c Reuse Isis APIs/contracts and audience-safe projections while
        keeping provider fleet, global registry lifecycle, incidents, billing,
        and security administration in operator hosts. Evidence: boundary tests.
  - [ ] I7.9.d Preserve selected project/asset/shot/sequence/revision and
        unsaved state warnings across tool transitions and deep links. Evidence:
        context journey.
  - [ ] I7.9.e Render feature unavailable/degraded/not-configured/unauthorized
        states from capabilities with safe remediation and no hidden-feature
        hints. Evidence: capability state tests.
  - [ ] I7.9.f Verify direct URL/role/property/function authorization, keyboard/
        screen-reader/responsive navigation, and that no operator control/API is
        copied into Yemaya. Evidence: AAA boundary Playwright suite.
        <!-- MEASURED 2026-08-19 (I7.9.a-f). The parent's evidence is "no duplicated Isis operator
        controls", which is two questions wearing one coat: does Yemaya Studio HOST the AAA
        surfaces, and does it AVOID the operator controls. The second answer is largely good. The
        first is the finding. Instrument: `generate-yemaya-aaa-navigation.mts` (+11 tests), wired
        into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/yemaya-aaa-navigation.json`.

        THE BOUNDARY EVICTED THIRTY-ONE CAPABILITIES TO A HOST THAT MOUNTS NONE OF THEM.
        `AAA_ONLY_STUDIO_ROUTES` names 31 route segments the contemplative product must never serve
        because they are AAA controls, and BASE.5.e ratified that list against
        `docs/oshun/studio-vs-yemaya-studio.md`. Yemaya Studio is where they were sent: 0 of the 31
        have a route in `apps/yemaya/studio-{web,desktop}` and 2 appear in their sources at all
        (`music-generation` and `text-to-audio`, generation-type strings inside a store). Of the
        eight surface families .b names - brief, graph, model, training, voice, music, 3D,
        post-process - the navigation vocabulary names 2: music through "Score Editor", and
        post-process through "Object Removal"/"HDR Trim"/"Relight Studio". Brief, graph, model,
        training and voice are named nowhere in the navigation of either host.

        .b ASKS FOR NO SECOND GLOBAL NAV AND THERE ARE ALREADY TWO: a `<nav>` in the header (6
        entries) and a `<nav>` in the sidebar (19 entries), neither scoped to a project, plus 0
        command palettes - the only mention of one in the estate is an `@example` in a doc comment
        on `useKeyboardNavigation`, a hook with no caller. Three of those controls land on the 404:
        the sidebar's "Assets" and the header's persistent "Templates" and the welcome page's "From
        Template", all pointing at routes that have never existed. So do 3 of the 7 in-app PWA
        manifest entry points (`/ai`, `/editor/script`, `/protocol`) - and `/editor/script` is a
        registered OS file handler, so opening a script file with the app lands on Not Found.

        NO AAA WORKBENCH KNOWS WHICH SHOT IT IS EDITING. 7 routes mount a page that imports an
        `@isis/*` library; 0 of them read a route param or the project store, so .a's mapping to a
        project/asset/shot/sequence context and .d's preservation of it across a transition have
        nothing to preserve. 1 of .d's 5 context dimensions reaches an Isis job at all - a
        `metadata.projectId` the store attaches when a project happens to be open. The
        unsaved-changes modal is translated into 3 locales, 5 strings each, and has 0 consumers.

        AN AUTHORIZATION FUNCTION WITH NO CALLER IS NOT AN AUTHORIZATION. `UserEntitlementTier =
        'contemplative' | 'aaa-creator' | 'operator'` exists in exactly one place, the score
        editor's `core.ts`, and `canAccessSceneScoreEditor`/`assertSceneScoreEditorAccess` have 0
        product callers and 2 test callers. Both hosts mount the scene score editor: the web one
        behind `RoleGuard roles="admin"` (Yemaya's role vocabulary is `admin|user|viewer`, with no
        AAA role in it), the desktop one behind nothing - 6 of 6 desktop routes have no guard, and
        that renderer has no auth store at all. The two `score-editor/core.ts` files are
        byte-identical 490-line copies. On the web, 24 routes admit any authenticated user
        including `viewer`, and before this pass the four guards had 0 tests.

        A DECLARED SURFACE IS NOT A RENDERED ONE. A walk of relative imports from `main.tsx` reaches
        85 modules; 26 UI modules are outside it, including all five of `src/layouts/` - `AppLayout`,
        `AuthLayout`, `DashboardLayout`, `SettingsLayout` and `ProjectLayout`, the last being exactly
        the project-context shell .d would have needed. That is also why the operator answer is
        unambiguous rather than arguable: `SettingsLayout` declares Billing and API Keys sections,
        but nothing renders it, and the settings page that IS rendered has four sections (General,
        Appearance, Account, About). Of .c's five operator concerns, 3 have no control anywhere and
        2 have exactly one surface between them - the desktop settings page's "AI Provider" heading
        over an "Enter your API key" field, in the host with no authentication.

        A CONTROL THAT DISCARDS ITS INPUT STILL LOOKS LIKE A SETTING: across the two settings pages
        the entry reaches, 2 of 15 form controls are bound to a handler. The other 13 - six
        `defaultChecked` checkboxes claiming features are on, two `defaultValue` selects, and the
        API-key password field - read nothing and write nothing.

        FIXED IN THIS CELL. All four domain client stores (Isis, Sophia, Hathor, Bellona) named
        `clientConfig` in `partialize`, and every one of those configs declares an `apiKey` plus a
        bearer or access token, so `persist` would write a service credential into localStorage in
        plaintext where it outlives the session. `IsisClient.getConfig()` already refuses to hand
        its own credentials back out, reporting `hasApiKey`/`hasBearerToken`; the stores that supply
        them now follow the same rule through `withoutClientCredentials`, which keeps the endpoints
        and drops `apiKey`/`bearerToken`/`accessToken`. Driven both ways: with the redaction
        removed, the new spec finds the key in `localStorage.getItem('yemaya-isis-store')`. Nothing
        on a product path sets a credential today, so this is what the persisted shape would have
        done to the first caller of `updateConfig` - which is when it would have been too late.

        Also added, as rules rather than sentences: `RouteGuard.spec.tsx` (5 assertions) for .f's
        direct-URL and role authorization, including the characterisation that `AuthGuard` admits
        `viewer`, and `client-credential-persistence.spec.ts` (7 assertions).

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The Isis
        store's failure path records the real submission error on the job and fabricates no success.
        (2) `metadata.projectId` is spread into the POST body by `submitJob` and carried by the
        generation API's job route, so the one context dimension that is sent does persist. (3)
        `RouteGuard` evaluates its role check exactly once per mount: `RoleGuard` builds a fresh
        `requireRoles` array every render and `RouteGuard` lists it in an effect dependency array,
        which looks like a re-check loop and is not one, because the array is built by the parent
        and the parent does not re-render when the guard's own state settles (measured: 1 render, 1
        role check, over 100ms). (4) The Isis client store touches 1 of the client's 4 resources
        (`generation`) and 5 methods, and no operator resource. (5) The e2e suite's axe fixture runs
        a WCAG 2.1 AA analysis after every spec.

        TRAPS. A REACT CONTEXT PROVIDER IS NOT A PROVIDER FLEET: matching "provider" credited 24
        files, 13 of them `<X.Provider>` wrappers. A WORD IN AN UNRELATED ENUM IS NOT A CAPABILITY
        STATE: `'unavailable'` is a touch-support enum member in the desktop's multi-monitor code
        and a rollback-dialog title in its updater, and `'Unauthorized'` is an HTTP status label -
        adding "the file also says capability" still credited the first, because a DISPLAY
        capability is not a feature capability. A macOS ENTITLEMENT IS NOT A PRODUCT ENTITLEMENT: 8
        files carry the word, 4 code-signing and 4 score-editor, and 0 consult a product
        entitlement. A SUBSTRING IS NOT A WORD: `rig` matches `right` in 10 pages. And
        `<RouteSuspense` starts with `<Route`, so splitting the router on the bare string reported a
        null element for all 33 web routes.

        2 of 17 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: .a has no context
        binding and no AAA role, .b has two global navs and no palette, .c reaches Isis by importing
        its libraries rather than calling its API, .d has nothing to preserve, .e has no capability
        source to render a state from, and .f has no boundary suite - 0 of the 24 e2e tests asserts
        that an operator control is absent. -->
- [ ] I7.10 Implement handoff from Yemaya project context to an Isis run and
      back. Evidence: project, shot/asset, actor, and lineage persist.
  - [ ] I7.10.a Define handoff intent/envelope for tenant/project, asset/shot/
        sequence/revisions, brief/spec, requested operation, actor/audience,
        return route, expiry, nonce, and signature. Evidence: handoff schema.
  - [ ] I7.10.b Validate Yemaya context access and current revisions before
        issuing the minimal signed Isis link/request. Evidence: issuance tests.
  - [ ] I7.10.c Reauthorize Isis actor/object/function, validate link freshness/
        replay, resolve canonical inputs, and preview substitutions/missing
        dependencies before plan creation. Evidence: acceptance tests.
  - [ ] I7.10.d Create Isis request/plan/run with immutable Yemaya source links
        and carry only authorized project/shot/asset metadata. Evidence: run
        lineage tests.
  - [ ] I7.10.e Return via a signed intent to the exact permitted Yemaya context
        and highlight resulting proposal/output/revision without assuming it was
        accepted. Evidence: return journey.
  - [ ] I7.10.f Handle logged-out, expired, changed/deleted/quarantined context,
        cancellation, failure, multiple outputs, and cross-tenant attempts.
        Evidence: handoff Playwright matrix.
        <!-- MEASURED 2026-08-19 (I7.10.a-f). The parent's evidence is "project, shot/asset, actor,
        and lineage persist", so the measurement follows one generation the whole way round: what
        leaves Yemaya, what Isis is asked to check before it acts, what the run records about where
        it came from, and what comes back. Instrument:
        `generate-yemaya-isis-handoff.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/yemaya-isis-handoff.json`.

        THERE IS NO ENVELOPE; THERE IS A JOB SUBMISSION. The only request Yemaya sends Isis is
        `JobSubmitRequest` (10 fields), and 1 of .a's 9 envelope elements is expressible in it -
        the requested operation, as `type`. No tenant, no shot/sequence/revision, no brief, no
        actor, no return route, no expiry, no nonce and no signature. Two callers issue it: the
        studio-web Isis store, 4 `submitJob` calls attaching `{projectId}` and only when a project
        happens to be open, and `yemaya asset generate`, 1 call attaching nothing at all. Neither
        reads a revision before issuing, which is .b's whole subject. On the receiving side 0 of
        .c's 5 acceptance checks appear anywhere on the job route or job service: no
        reauthorization of the actor against the object, no link freshness, no nonce or
        consumed-token set, no canonical input resolution and no substitution preview.

        A COLUMN NAMED FOR A LINK IS NOT A LINK. `Asset.isisGenerationId` and
        `Project.isisWorkflowIds` are the only persisted Yemaya-to-Isis references in the estate.
        Both are declared in the Prisma schema; outside that schema and its generated client,
        0 files in `apps/yemaya` or `libs/yemaya` mention either one. Nothing writes them, so .d's
        "immutable Yemaya source links" are two empty columns - and
        `scripts/verify-phase-9-completion.mjs` passes "9.19 schema capability references" by
        regex-matching those column names in the schema file, which is a check that cannot tell a
        filled column from an empty one.

        FIXED: THE RETURN LEG DEAD-LETTERED EVERY EVENT AND ATE EVERY FAILURE. `apps/yemaya/api`
        subscribes `isis.asset.generated` and `isis.job.failed` on the bus, and those two handlers
        are the entire path by which a finished run reaches the project that asked for it. Both
        were written against a payload Isis has never published, and the unit test fed each handler
        its own consumer-side interface, so both stayed green.

        - `isis.asset.generated`: the handler read `metadata.format` and `metadata.fileSize`. The
          producer (`apps/isis/output-registry`) sends `filename`, `mimeType`, `sizeBytes`,
          `provenance` and `thumbnails`, and marks `metadata` OPTIONAL. Driven with the producer's
          own literal: `Cannot read properties of undefined (reading 'format')`, dead-lettered,
          0 asset references recorded, 0 metrics, 0 notifications, 0 workflow actions.
        - `isis.job.failed`: the handler read `errorCode`/`errorMessage`/`retryCount`/`retryable`
          flat; the producer nests them under `error` and calls the count `attemptCount`. Every one
          read `undefined`, `categorizeError` threw on `undefined.toLowerCase()`, and THAT handler's
          catch acknowledges unconditionally - "informational, don't create a failure loop". Driven:
          `acked=1, published=[]`. Every Isis failure was consumed and discarded in silence.

        Both consumer payloads now mirror `@oshun/contracts`, both handlers read the fields the
        producer sends, and `isis-return-leg.spec.ts` (+9) drives them with the producer's own
        literals - including the optional-`metadata` case, the `model_3d` type the old union could
        not express, and `workflow: 'default'`, which Isis sends when a job ran under no workflow
        and which is not a workflow to ask for an action. The instrument compares the producer's
        field set with each consumer's on every verify run.

        STILL OPEN, AND THE SAME DEFECT: `libs/bellona/event-handlers` and `libs/hathor/event-handlers`
        subscribe `isis.asset.generated` too (through `apps/bellona/build-api` and
        `apps/hathor/world-api`), and both still declare `generationParams` and `thumbnailUrl`,
        which Isis sends under `provenance.parameters` and `thumbnails` respectively. Neither
        crashes - both feed the missing field to an extractor that returns null - so Hathor never
        links a generated asset to a world element and Bellona never routes one to an engine. That
        is Bellona and Hathor scope, not this cell's, and it is left for their owners.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) Both
        producers' payload literals match their canonical `@oshun/contracts` schemas field for
        field. (2) `metadata.projectId` travels Yemaya store -> job submission -> output row ->
        event payload, so the one context dimension that is sent does survive the round trip.
        (3) `IsisEventPublisher` swallows publish failures deliberately and logs them, rather than
        breaking the generation it is reporting on.

        TRAPS. A JOB REPLAY IS NOT A LINK REPLAY: matching `replay` on the job route credited 56
        hits, every one the re-run verb, so .c's replay check reads 0 only once a nonce or
        consumed-token set is what is asked for. A SHORTHAND PROPERTY HAS NO COLON: both producers
        write `projectId,` and `userId,` in shorthand, and a `key:` pattern reported the two
        payloads as diverging from the contract they satisfy. A COMMENT STRIPPER THAT DOES NOT KNOW
        ABOUT STRINGS EATS CODE: the producer literal contains `` `s3://${bucket}/${key}` ``, and a
        naive `//` rule deletes the rest of that line. AN EMPTY SET AGREES WITH EVERYTHING: Bellona
        and Hathor declare no job-failed payload at all, and "no field the producer never sends"
        reported them as agreeing with a contract they never read. A WORD THE DOMAIN ALREADY OWNS
        IS NOT A REFERENCE TO YOUR FEATURE: "handoff" is ordinary film vocabulary - a second-unit
        camera handoff, a shift handoff, an AI-to-human session handoff - and matching it credited
        2 specs. A PRODUCT NAME INSIDE A STRING IS NOT A VISIT TO THAT PRODUCT: the Yemaya smoke
        spec renders `isis-relight-compositor-export` and types `isis://references/...` into a
        field without leaving its own origin. 0 of the 14 Yemaya and Isis e2e specs navigates to
        the other product.

        1 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: .a has no envelope,
        .b validates nothing before issuing, .c performs none of the five acceptance checks, .d's
        two link columns have no writer, .e's return is an unsigned bus event with no return route
        (the leg now works, but a working leg is not a signed intent), and .f has no cross-host
        journey at all. -->
- [ ] I7.11 Complete curated Oshun Studio generation cards for illustration,
      narration, ambient audio, explainer, caption/dub, accessibility, music,
      and approved 3D briefings. Evidence: tier allowlist tests.
  - [ ] I7.11.a Define the curated generation-card contract for approved
        operation, audience/host, typed high-level inputs, references, cost/time
        estimate, confirmation, progress, output, and safe errors. Evidence:
        card schema tests.
  - [ ] I7.11.b Implement illustration briefing for subject/purpose/style/
        composition/aspect/palette/required/excluded elements and authorized
        references. Evidence: illustration journey.
  - [ ] I7.11.c Implement narration briefing for approved voice/language/tone/
        pace/pronunciation/script/timing/format with consent-safe choices.
        Evidence: narration journey.
  - [ ] I7.11.d Implement ambient-audio briefing for environment/events/mood/
        perspective/duration/loop/loudness/channel/format. Evidence: ambient
        audio journey.
  - [ ] I7.11.e Implement explainer briefing for audience/objective/script/
        scenes/style/duration/narration/captions/aspect/target. Evidence:
        explainer journey.
  - [ ] I7.11.f Implement caption/dub briefing for source/language/transcript/
        translation/speaker mapping/timing/style/format/review. Evidence:
        caption/dub journey.
  - [ ] I7.11.g Implement accessibility briefing for alt text, audio
        description, captions/transcript, reading level, language, timing, and
        target. Evidence: accessibility asset journey.
  - [ ] I7.11.h Implement music briefing for purpose/mood/tempo/form/duration/
        instrumentation/hit points/loop/stems/format with rights-safe
        references. Evidence: music journey.
  - [ ] I7.11.i Implement approved 3D briefing for asset/use/subject/style/
        dimensions/units/views/materials/poly/texture/rig/format/target without
        graph/model controls. Evidence: 3D journey.
  - [ ] I7.11.j Derive availability/defaults/validation/estimate from canonical
        capabilities and submit through the same governed plan/dispatch APIs.
        Evidence: tier allowlist/contract tests.
        <!-- MEASURED 2026-08-19 (I7.11.a-j). The parent's evidence is "tier allowlist tests", so
        the measurement asks, for each of the eight briefings .b-.i name, whether a card of its own
        exists in the ratified catalog and what its typed inputs can express. Instrument:
        `generate-oshun-curated-cards.mts` (+8 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-curated-cards.json`.

        FIXED: A BORROWED ENTITLEMENT IS NOT THIS CARD'S ENTITLEMENT. `CARD_KINDS` had eight
        members and two of them were not among the eight briefings this cell names: `video` and
        `video-from-image` are cards, and MUSIC and the 3D BRIEFING were not. Both nevertheless
        ship - `/studio/generation/music` and `/studio/generation/nyx-3d`, each with its own BFF
        route - and both resolved a DIFFERENT card's spec to do their entitlement check:
        `music-route.ts` called `getCuratedCardByKind('ambient-audio')` and `nyx-3d-route.ts`
        called `getCuratedCardByKind('illustration')`. The same borrowed spec is what the route
        hands back as the provenance preview, and `CardShell` prints it: a music generation
        displayed `Provenance: wfc-curated-ambient-audio v1.0.0`, and a sky briefing displayed
        `wfc-curated-illustration`. Kill-switching the 3D briefing was impossible without killing
        illustration; a tenant allowlisted for ambient audio was allowlisted for music. Both now
        have their own `CardKind`, their own catalog entry (`curated-music`,
        `curated-sky-3d-briefing`), their own kill switch, tenant allowlist, minimum tier and
        provenance. Entitlement-neutral by construction: the new entries carry the same
        `curated-creator` / `all` / `killSwitchActive: false` defaults the borrowed ones did.
        Driven both ways - with the routes pointed back at the borrowed kinds, the two new
        assertions go red. The two kinds carry no `CuratedCardInputs` variant on purpose: each
        validates its own request shape at its own route, and `POST /v1/isis/curated-cards/generate`
        already rejects any submission whose `inputs.kind` does not match the card's kind.

        A SUBMIT SPINNER IS NOT GENERATION PROGRESS, AND AN EMPTY LITERAL IS NOT OUTPUT. 7 of .a's
        9 contract elements are real - approved operation, audience/host, typed inputs, references,
        cost/time estimate, confirmation and safe errors all exist and are server-authoritative.
        The two that are not: `CardShell` flips its button to "Generating…" while `busy` is true,
        and `busy` is true only between the POST and its 202, after which the label reverts and the
        status line reads "Accepted — queued for generation"; nothing follows the enqueued job.
        And `CardShell` renders a variants grid that its only caller feeds
        `variants: [] as const`, always. So a creator submits a card and never sees the thing it
        made.

        WHAT THE EIGHT BRIEFINGS CAN EXPRESS: 24 of the 75 fields .b-.i name. illustration 3/9
        (no purpose, composition, palette, required or excluded elements, or authorized
        references), narration 4/8 (no pace, pronunciation, timing or format), ambient audio 3/9
        (no environment, events, perspective, loudness, channel or format), explainer 2/10 (no
        audience, objective, script, scenes, duration, narration, captions or target), caption/dub
        2/9 (no transcript, translation, speaker mapping, timing, style, format or review),
        accessibility 2/7 (no audio description, reading level, language, timing or target), music
        6/10 (no form, instrumentation, hit points or format), 3D 2/13 - the sky-event briefing is
        a lunar-phase visualisation with an event id and a viewer angle, not the general approved
        3D briefing .i describes.

        .j IS THREE ENDPOINTS, NOT ONE. `/v1/isis/curated-cards/generate`, `/v1/isis/music/generate`
        and `/v1/isis/nyx-3d/generate`. Availability, defaults and validation ARE derived
        server-side from the catalog, the entitlement gate and the per-card validators; the
        estimate is the one that is not, and honestly so - see below. The curated-cards studio
        renders a form for 6 of the 10 approved kinds; it now skips the four it cannot render
        instead of emitting an empty wrapper for each.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The catalog
        is server-authoritative and frozen, so a client can never assert its own kill switch, tier
        or tenant allowlist. (2) `PENDING_COST_COEFFICIENTS` and `areCostCoefficientsPending` are
        an honest seam, not a stub: until a deployment publishes per-workflow-class pricing the
        pre-flight estimate reads "pending" rather than a fabricated number, and the file says so.
        (3) The generate route rejects a submission whose `inputs.kind` does not match the catalog
        card's kind. (4) Every curated submission passes `auditRawGraphLeak` - model hashes, LoRA
        ids, scheduler names, ComfyUI node keys - before field validation.

        2 of 13 properties present. ALL TEN CELLS AND THE PARENT LEFT `[ ]`: .a is missing progress
        and output, .b-.i can express 24 of 75 asked fields between them, and .j dispatches through
        three endpoints with no published estimate. -->
- [ ] I7.12 Ensure curated cards expose only approved high-level controls.
      Evidence: raw-graph/model/provider leak scan passes.
  - [ ] I7.12.a Ratify a per-card allowlist of field
        names/types/values/defaults/ validation/errors and permitted
        provenance/capability summaries. Evidence: curated control matrix.
  - [ ] I7.12.b Generate curated projections from typed server contracts rather
        than hiding AAA/operator controls with client CSS or feature flags.
        Evidence: projection tests.
  - [ ] I7.12.c Reject unknown/nested/alias/raw JSON fields,
        workflow/model/node/ sampler/provider IDs, advanced parameter names, and
        mass assignment at the curated API. Evidence: adversarial request tests.
  - [ ] I7.12.d Remove hidden details from HTML/JS bundles, responses, errors,
        URLs, state stores, telemetry, exports, deep links, and capability
        hints. Evidence: static/runtime property leak scan.
  - [ ] I7.12.e Keep approved high-level values mapped deterministically to
        governed specs/presets and show audience-safe effective summaries.
        Evidence: mapping golden tests.
  - [ ] I7.12.f Test every card as curated/contemplative/AAA/operator actors,
        direct routes, crafted payloads, stale links, and changed tier.
        Evidence: tier authorization Playwright suite.
        <!-- MEASURED 2026-08-19 (I7.12.a-f). The parent's evidence is a "raw-graph/model/provider
        leak scan", so the measurement asks two things: what a crafted request can push IN, and what
        the curated responses carry OUT. Instrument:
        `generate-oshun-curated-control-surface.mts` (+7 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-curated-control-surface.json`.

        FIXED: THE VALIDATORS CHECKED THE VALUES AND NOTHING CHECKED THE SET. Each per-card
        validator ended `return issues.length === 0 ? ok(kind, i) : fail(issues)` where `i` is the
        caller's whole object, and `curated-generation-executor` forwards "the admitted bare inputs
        unchanged". Driven: an illustration submission carrying `workflowClassId:
        'wfc-aaa-comfy-graph'`, `providerKey: 'replicate'`, `steps: 150`, `cfgScale: 22` and
        `sampler: 'heun'` returned `verdict=pass` with all five forwarded into the job. The
        raw-graph leak audit does not see any of them: it looks for 64-hex model ids, `lora-…`
        patterns, exactly three scheduler names (`ddim`, `dpmpp_2m`, `euler_a` - `heun` is not one)
        and ComfyUI numeric node keys. A crafted value SHAPE was worse than accepted: `subject:
        { $ne: null }` made `i.subject.trim()` throw `i.subject.trim is not a function`, which the
        route turns into a 500 rather than a 422.

        `CARD_INPUT_FIELD_ALLOWLIST` now ratifies 39 field names across the 8 form-driven kinds
        (illustration 5, narration 4, ambient-audio 4, explainer 5, caption-dub 2,
        accessibility-pass 2, video 8, video-from-image 9), and `checkCardFieldAllowlist` runs
        before the per-card switch: an unapproved field name is a 422 issue naming the field, and a
        value that is not a string, number, boolean or array of strings/numbers is a 422 rather
        than a throw. Keyed on `CuratedCardInputs['kind']` rather than `CardKind`, because `music`
        and `sky-3d-briefing` have no form on this API. Driven both ways - with the gate disabled
        the two new route assertions go red, and both confirm the executor was never reached. 7 of
        7 of .c's refusal classes are now refused.

        STILL OPEN, AND A RATIFICATION RATHER THAN A BUG. .a asks for an allowlist of "permitted
        provenance/capability summaries" and there is none, so ALL SEVEN identifier fields in the
        card spec travel out to a curated audience: `GET /v1/isis/curated-cards/catalog` returns
        each entry's whole `CardSpec` (`binding.workflowClassId`, `binding.personaId`,
        `binding.voiceProfileAllowlist`) and whole `ProvenancePreview`, and `CardShell` PRINTS five
        of them - `Provenance: {workflowClassId} v{workflowClassVersion}`, `Provider:
        {providerKey} / {providerRegion}`, `Cost class: {costClass}`. Nothing real leaks today
        because the catalog ships `providerKey: 'pending'`, but the surface is built to display the
        tenant's resolved provider the moment a deployment fills it in. Which of the seven a
        curated audience may see is exactly the decision .a asks a human to make; .a's "defaults"
        clause has no declaration either.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) 0 of the 11
        generation components hides a control with `display: none`, `visibility: hidden` or a
        `hidden={…}` prop: the six forms are generated from the typed catalog, which is what .b
        asks for. (2) The card-to-workflow-class mapping is a pure function of the card kind
        (`wfc-curated-${kind}`), so .e's determinism holds. (3) `auditRawGraphLeak` runs on every
        curated submission before field validation.

        .f HAS NO SUITE AT ALL: 0 of the 561 Oshun web e2e specs visits
        `/studio/generation/{curated-cards,music,nyx-3d}`, so no card is exercised as a
        contemplative, curated, AAA or operator actor, by direct route, or after a tier change.

        3 of 6 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: .a is missing defaults
        and the provenance-summary allowlist, .d ships seven identifiers to the client and prints
        five, and .f has no suite. .b, .c and .e hold. -->
- [ ] I7.13 Complete the Oshun output gallery with lineage, status, quality,
      rights, replay eligibility, and editorial promotion. Evidence: live-data
      journey.
  - [ ] I7.13.a Define gallery list/detail projection for exact asset/output
        revision, preview, source, status, target, generated/manual identity,
        audience, and current availability. Evidence: gallery contracts.
  - [ ] I7.13.b Implement deterministic search/filter/sort/pagination and
        collection/group views with restrained media-led hierarchy and complete
        loading/empty/error/stale/offline states. Evidence: gallery list tests.
  - [ ] I7.13.c Present authorized brief/run/output/ingredient lineage, decision
        history, transformations, provenance credential, and unavailable/
        redacted links. Evidence: lineage detail tests.
  - [ ] I7.13.d Present quality raw measures/verdicts/freshness, rights/consent/
        attribution/expiry, safety state, target compatibility, and recall
        status without false green aggregation. Evidence: status tests.
  - [ ] I7.13.e Calculate replay eligibility from current inputs/dependencies/
        policy/rights/capacity with mode, drift, estimate, and exact reason.
        Evidence: replay projection tests.
  - [ ] I7.13.f Implement editorial promotion request/preview/submit/status with
        destination scope, derivative lineage, role separation, and no direct
        publication. Evidence: promotion journey.
  - [ ] I7.13.g Verify live generation-to-gallery-to-promotion, cross-tenant/
        property authorization, keyboard/screen-reader/zoom/responsive behavior,
        and large-gallery performance. Evidence: gallery Playwright E2E.
        <!-- MEASURED 2026-08-19 (I7.13.a-g). The parent's evidence is a live-data journey over
        lineage, status, quality, rights, replay eligibility and editorial promotion, so the
        measurement asks what the gallery's own record projection can SAY about an output, and what
        the surfaces on top of it can therefore show. Instrument:
        `generate-oshun-output-gallery.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-output-gallery.json`. No fix in this cell: what exists is
        correct, and what is missing is a projection nobody has designed.

        THE RECORD IS A PRODUCTION FACT, NOT A JUDGEMENT ABOUT ONE. `OutputRecord`'s 18 fields say
        who made an output, with what, when, under which licence tag and at what cost. 4 of .a's 8
        projection properties are carried - preview (`thumbnailUrl`), source
        (`generatorKey`/`workflowClassId`), audience (`entitlementTier`) and current availability
        (`takenDownAtUnixSeconds`). There is no revision, no status, no target and no
        generated/manual identity, so the gallery cannot name the exact thing it is showing you or
        say what state it is in. And of .d's ten status families exactly ONE is on the record:
        rights, as a `licenseTag` string. No quality measure, no verdict, no quality freshness, no
        consent state, no attribution, no expiry, no safety state, no target compatibility and no
        recall status. .d's "without false green aggregation" is therefore satisfied the only way
        it can be - nothing is aggregated because nothing is measured.

        THE LINEAGE KNOWS TWO KINDS OF THING. `LineageNodeKind` is `output | job`, so .c's brief
        and ingredient lineage have no node to be, and there is no decision history. The six edge
        kinds (`derived-from`, `cited-by-claim`, `used-in-lesson`, `embedded-in-passage`,
        `used-in-scene`, `send-to-editorial`) are the transformations it can express. A withheld
        link is COMPUTED AND NEVER SHOWN: `buildRenderableLineage` returns `droppedForeignOutput`
        and `droppedInvalid` beside the admitted edges, and the client renders neither - so a
        redacted link is silently absent rather than shown as redacted, which is the one thing .c
        asks for by name.

        REPLAY PLANS A BRANCH; IT DOES NOT DECIDE ELIGIBILITY. 3 of .e's 9 parts: current inputs
        (`OriginalInputs`), an estimate (`estimatedCostCents`) and an exact reason
        (`BranchValidationError` with `out-of-range`/`unknown-parameter` codes). Dependencies,
        policy, rights, capacity, mode and drift appear nowhere in `branch-replay.ts`, so nothing
        asks whether the run may be repeated - only whether the tweaked parameters are in range.

        PROMOTION IS AN OPERATOR BULK ACTION, NOT A WORKFLOW. 5 of .f's 8 parts hold, and the three
        that hold hardest are the ones that matter: role separation (`AuthorizationCheck` gates the
        action by operator tier), no direct publication (the verb emits a `send-to-editorial` edge
        to a `queueId`, never a publish), and derivative lineage (that edge is a lineage kind).
        What is absent is the workflow around them - no promotion request a creator can make, no
        preview of what promotion would do, and no status to follow afterwards; an undo window is
        not a status.

        .b IS HALF BUILT AND THE HALF THAT EXISTS IS THE HARD HALF. `applyGalleryFilter` sorts by
        `producedAtUnixSeconds` descending with a stable `outputId` tiebreak and pages by cursor -
        deterministic, which is what .b asks first. The surface renders 2 of the 5 states it names:
        loading and error. There is no empty state at all (the grid never checks for an empty
        result), no stale state, no offline state, and no collection or group view.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1)
        `buildRenderableLineage` now admits an edge that is ANCHORED (an endpoint is visible) and
        does not REACH OUT (every output endpoint is visible), with job nodes exempt - the fix for
        the earlier defect in which the only edge the estate emits, `output → job:<jobId>`, was the
        only edge both readers dropped. (2) The deterministic sort and cursor pagination above.
        (3) The live provenance inspector fetches the SELECTED output's real bundle and renders
        honest loading / not-recorded / not-authorized / unreachable states rather than the canned
        fixture bundle it used to mount.

        TRAP. A MENTION IS NOT A VISIT: 4 e2e specs name a gallery route and 1 navigates to one -
        and that one is the ADMIN gallery (`/isis/output-gallery`), not the member
        `/studio/generation-gallery`. The other three use the route as an entitlement-bounce
        target, a boundary assertion and a provenance smoke string. So .g's
        generation-to-gallery-to-promotion journey has never been walked.

        1 of 9 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`. -->
- [ ] I7.14 Implement honest fixture, provider-gated, degraded, and offline
      states in every Isis-related host. Evidence: state matrix.
  - [ ] I7.14.a Define canonical data-origin and service-state discriminators
        for live, fixture/demo, provider-gated, not-configured, degraded,
        partial, stale, offline-cached, offline-unavailable, and unauthorized.
        Evidence: state schema.
  - [ ] I7.14.b Label fixture/demo data persistently in UI/API/export and
        prevent its mutation/promotion/release from masquerading as production.
        Evidence: fixture boundary tests.
  - [ ] I7.14.c Present provider-gated state with audience-safe dependency/
        configuration reason, available alternatives, and no simulated success.
        Evidence: provider state tests.
  - [ ] I7.14.d Present degraded/partial/stale state per capability/data region
        with timestamp, missing functions, safe actions, and no global healthy
        implication. Evidence: degraded state tests.
  - [ ] I7.14.e Define offline read cache scope/freshness/classification and
        disable network-required/unsafe mutations with queued-action policy made
        explicit. Evidence: offline behavior tests.
  - [ ] I7.14.f Exercise every state across Isis operator, Yemaya AAA, Oshun
        Studio, and gallery with reconnect/recovery and accessible
        announcements. Evidence: cross-host state matrix.
        <!-- MEASURED 2026-08-19 (I7.14.a-f). The parent's evidence is a state matrix across four
        hosts, so the measurement builds one: for each of .a's ten discriminators and each host,
        whether the state is DECLARED (a union member or a discriminant assignment) and whether it
        is RENDERED. Instrument: `generate-isis-host-state-matrix.mts` (+6 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-host-state-matrix.json`.

        THE STATES EXIST HUNDREDS OF TIMES OVER AND CANONICALLY ZERO TIMES. .a asks for CANONICAL
        discriminators. No exported union in any of the four hosts names five or more of the ten;
        what the estate has instead is one local discriminated union per component. `'unauthorized'`
        alone is declared independently in 359 Oshun Studio files - `{ status: 'unauthorized';
        reason: string }`, written out again in every workspace, each with its own neighbours and
        its own renderer. Nothing can be checked against anything, which is why .f's cross-host
        matrix has nothing to be a matrix OF: 0 state-matrix artifacts exist in `evidence/` or
        `docs/`.

        THE MATRIX, HOST BY HOST. 14 of the 40 (host × state) cells declare their state.
        Oshun Studio 6/10 (live 9, fixture/demo 1, not-configured 3, degraded 13, partial 9,
        stale 7, unauthorized 359), the Oshun BFF 5/10, the ISIS OPERATOR CONSOLE 1/10 (degraded,
        in 2 of its 142 files), and the YEMAYA AAA HOST 0/10 - across 123 files it cannot express
        a single one of the ten states, which is the same answer §I7.9 got from the other
        direction. The gallery, measured as its own directory, is also 0/10. And THREE STATES ARE
        ABSENT FROM ALL FOUR HOSTS: `provider-gated`, `offline-cached` and `offline-unavailable`.
        .c's provider-gated state has no declaration anywhere, so a surface cannot render the
        dependency reason .c asks for; what the estate does instead is fail closed - 264 sources
        answer `not_configured` or a 503 - which refuses honestly and tells the surface nothing it
        can show. That is the right refusal and the wrong vocabulary.

        A LABEL TRAVELS WITH THE DATA. .b asks for persistent fixture/demo labelling in UI, API and
        export. 4 sources carry a data-origin discriminant (`source: 'fixture' | 'bff'` in the
        Lilith data layer is the real one); 264 gate a fixture store behind an env flag, which
        HIDES fixture data rather than labelling it. A record that arrives from a gated fixture
        store is indistinguishable from a live one once it is in hand, so nothing downstream can
        prevent its promotion or release.

        .e's OFFLINE STORY IS A SERVICE WORKER AND NOTHING IT PROMISES. Yemaya AAA and Oshun Studio
        each ship one service worker; between them 10 sources mention a queued action or background
        sync and 2 declare a cache freshness. The Isis operator console has no service worker at
        all. Neither `offline-cached` nor `offline-unavailable` is a state any host can be in, so
        the read-cache scope, freshness and classification .e asks for have nowhere to be recorded.

        TRAPS. A WORD IN A MESSAGE IS NOT A STATE: the census counts a union member or a
        discriminant assignment, never the word inside a sentence the UI prints. A `data-` HOOK
        NAMED FIXTURE IS A TEST HOOK, NOT A DATA-ORIGIN LABEL: allowing the spelling anywhere
        inside an attribute name credited `data-fixture-mount`, `data-texture-fixtures` and
        `data-yemaya-remote-actor-home-capture-white-balance-fixture` — 319 "renders" of a state
        nothing declares. A FILENAME IS NOT A LABEL either: matching `'fixture'` anywhere credited
        every `__fixtures__` helper.

        1 of 7 properties present - .b's label check passes only because four sources carry a
        data-origin discriminant at all, and none of them is on an Isis path. ALL SIX CELLS AND THE
        PARENT LEFT `[ ]`. -->
- [ ] I7.15 Add keyboard and screen-reader parity to media, graph, and 3D
      surfaces. Evidence: semantic alternative journeys.
  - [ ] I7.15.a Define logical focus order, landmarks, headings, labels,
        instructions, status announcements, shortcuts, and escape behavior for
        each complex surface. Evidence: accessibility interaction spec.
  - [ ] I7.15.b Provide image/video/audio controls, timelines, regions,
        annotations, tracks, captions/transcripts, and measurements through
        native/ARIA semantics and keyboard operation. Evidence: media tests.
  - [ ] I7.15.c Provide graph node/port/edge/subgraph/errors/selection/reorder/
        connect/delete operations through synchronized tree/list/forms rather
        than pointer-only canvas. Evidence: graph alternative journey.
  - [ ] I7.15.d Provide 3D scene hierarchy/objects/components/transforms/
        materials/animation/camera/measurements/annotations through synchronized
        tree/table/forms. Evidence: 3D alternative journey.
  - [ ] I7.15.e Synchronize visual and semantic selection/focus/state without
        duplicate announcements, keyboard traps, lost focus, or hidden updates.
        Evidence: focus/state tests.
  - [ ] I7.15.f Verify screen reader/browser combinations, keyboard-only, 200–
        400% zoom/reflow, contrast, reduced motion, high contrast, and long-
        content virtualization. Evidence: semantic E2E suite.
        <!-- MEASURED 2026-08-19 (I7.15.a-f). The parent's evidence is "semantic alternative
        journeys", so the measurement enumerates the complex surfaces the three hosts actually ship
        and asks each the same question: is there a non-pointer way to do what the canvas does?
        Instrument: `generate-isis-semantic-parity.mts` (+5 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-semantic-parity.json`.

        A CANVAS WITH NO KEYBOARD HANDLER HAS NO ALTERNATIVE TO SYNCHRONIZE WITH. 16 complex
        surfaces: 9 media, 6 canvas, 3 three-dimensional (two files are both). Of the 7 canvas and
        3D surfaces, 1 declares a key handler or a `tabIndex` (`InteractiveSkyMap`), and 0 expose a
        tree, grid or table alternative — so .c's "synchronized tree/list/forms rather than
        pointer-only canvas" and .d's 3D hierarchy/transform/material tables have nothing to be
        synchronized WITH, and .e's selection synchronisation has no second selection to
        synchronize. Not one of the three 3D surfaces (Yemaya's `MotionCameraControlPage`, Oshun's
        two Hathor virtual-blocking panels) is keyboard operable at all;
        `StudioIsisGaussianSplattingWorkspace` is 3,034 lines with no `onKeyDown`, no `tabIndex`
        and no `aria-label`.

        MEDIA IS THE CLASS WHERE THE ANSWER IS BETTER. 8 of the 9 media surfaces use native
        `controls`, 4 ship a caption track and 2 a transcript — `SessionPlayer` and
        `StudyWorkspaceApp` are the two that answer .b's captions/transcript clause properly. The
        Isis operator console's three media surfaces sit inside real tables, which is why they
        score a semantic alternative where the canvases do not.

        .f IS ONE DIMENSION OUT OF FIVE IN TWO OF THREE HOSTS. Every spec in the Isis operator
        suite (13) and the Yemaya suite (1) ends in a WCAG axe analysis, and 554 of Oshun Studio's
        561 do. Beyond axe: keyboard-only 0/0/43, zoom-reflow 0/0/263, contrast 0/0/1, reduced
        motion 0/0/540 across isis-operator / yemaya / oshun-studio. So the two hosts that own the
        3D surfaces verify nothing about keyboard, zoom, contrast or motion.

        TRAPS. AN `@example` IN A DOC COMMENT IS NOT A SURFACE: two Yemaya modules — a responsive
        layout helper and a fullscreen hook — carry `<video src="video.mp4" />` inside a `@example`
        block, and matching the raw source counted both as media surfaces. A CHECK IN THE FIXTURE
        COVERS EVERY SPEC THAT IMPORTS IT: Yemaya's whole suite ends in an axe analysis because
        `e2e/fixtures.ts` wraps the base `test`, and reading only the spec files reported 0 axe
        coverage for a suite that is 100% covered.

        1 of 6 properties present (.a — 13 docs define a focus order or an accessibility
        interaction spec, though none is the per-surface spec .a asks for by name). ALL SIX CELLS
        AND THE PARENT LEFT `[ ]`. -->
- [ ] I7.16 Add responsive review surfaces without forcing full graph/3D editing
      onto mobile. Evidence: mobile can inspect, comment, approve, and act on
      incidents.
  - [ ] I7.16.a Define wide, compact, and single-column review information/
        action hierarchy with mobile scope limited to safe inspection and review
        operations. Evidence: responsive behavior spec.
  - [ ] I7.16.b Build mobile asset/media preview and metadata/gate/provenance/
        rights inspector with progressive sections and persistent subject
        identity. Evidence: mobile inspection tests.
  - [ ] I7.16.c Provide graph and 3D semantic tree/list/detail/diff snapshots on
        mobile without loading or exposing the full authoring editor. Evidence:
        mobile graph/3D tests.
  - [ ] I7.16.d Implement anchored comment create/edit/resolve and attachment/
        mention behavior with accessible target context. Evidence: mobile
        comment journey.
  - [ ] I7.16.e Implement approve/reject/request-changes/exception/incident
        actions only where role/state/evidence permit, with impact, reason,
        confirmation, and stale-state conflict. Evidence: mobile action tests.
  - [ ] I7.16.f Verify touch targets, orientation, safe areas, keyboard, screen
        reader, zoom, reduced motion, slow/offline/reconnect, and desktop/mobile
        decision parity. Evidence: responsive Playwright matrix.
        <!-- MEASURED 2026-08-19 (I7.16.a-f). The parent's evidence is "mobile can inspect,
        comment, approve, and act on incidents", and .a asks for mobile SCOPE to be limited to safe
        inspection and review — so the measurement asks the scope question first: does anything
        narrow what a surface will DO when the viewport is narrow? Instrument:
        `generate-isis-responsive-review.mts` (+5 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-responsive-review.json`.

        A BREAKPOINT THAT ONLY MOVES THINGS IS NOT A SCOPE. The three hosts lay out responsively —
        101 sources carry a Tailwind breakpoint, a media query or a viewport hook — and 0 of them
        use that knowledge to withhold an action. A phone gets the same verb set as a workstation,
        including the destructive ones, so .a's "mobile scope limited to safe inspection and review
        operations" is not a restriction the code can make. 2 docs describe a responsive review
        behaviour, neither of them the per-surface spec .a asks for.

        THE OPERATOR CONSOLE HAS NO REVIEW VERB AT ALL. Of .e's five verbs as CONTROLS — a label
        someone can press, not a word in a file — Isis operator has 0 (approve, reject, request
        changes, exception and incident are none of them a control in its 142 sources), Yemaya AAA
        has 3 (approve 7 files, reject 2, request changes 1) and Oshun Studio has all 5. The
        guarantees .e attaches to those verbs do exist somewhere in the estate — confirmation 147
        sources, reason capture 699, impact preview 68, stale-state conflict 117 — but not as a
        contract any one verb is held to.

        .c IS ANSWERED BY .I7.15. That cell measured 0 of the 7 canvas and 3D surfaces exposing a
        tree, grid or table alternative at ANY viewport, so there is no semantic view to serve on a
        narrow one; a mobile graph/3D tree cannot be built out of nothing. .d fares better: 27
        sources carry an anchored-comment identity.

        .f IS ONE HOST OF THREE. Oshun Studio's 561 specs set a viewport in 269, exercise
        offline/reconnect in 106, reduced motion in 52 and orientation in 13. The Isis operator
        suite (13 specs) verifies NONE of the five dimensions, and the Yemaya suite (1 spec)
        verifies one (orientation). And TOUCH IS THE DIMENSION NO HOST VERIFIES: 0 specs anywhere
        use `hasTouch`, `tap()` or a touchscreen context, so .f's touch-target clause has never
        been exercised on any surface.

        TRAPS. A CATCH BLOCK IS NOT A REVIEW VERB: matching the word `exception` across the three
        hosts hit 384 files — `catch (exception)`, `…Exception` classes, exception-handling prose —
        so the verbs are counted as control labels instead. A BREAKPOINT PREVIEW PICKER IS NOT A
        VIEWPORT SCOPE: allowing the bare word `breakpoint` as the guard credited three Hathor
        authoring workspaces whose match was `data-breakpoint-preview-select … disabled`, a
        `<select>` for previewing breakpoints in a design tool.

        3 of 6 properties present (.d's anchored comments, .e's verbs existing somewhere, and .e's
        guarantees existing somewhere). ALL SIX CELLS AND THE PARENT LEFT `[ ]`. -->
- [ ] I7.17 Complete parity/coverage monitors for generation types, workflows,
      models, outputs, provider APIs, SDKs, and UI controls. Evidence: every
      divergence links to the exact missing contract/route/control and cannot be
      dismissed without a disposition.
  - [ ] I7.17.a Define canonical coverage dimensions/identities/versions for
        asset kind, operation, workflow, model, provider capability, API/SDK,
        host/audience, control, output/target, and required evidence. Evidence:
        coverage schema.
  - [ ] I7.17.b Ingest source-of-truth registries, admitted workflow/model/
        provider runtime capability, API schemas, SDK surfaces, UI manifests,
        and automated-test evidence with freshness. Evidence: adapters tests.
  - [ ] I7.17.c Compute expected-versus-observed rows and classify missing,
        incompatible, stale, unauthorized exposure, undocumented, untested, or
        intentionally unavailable. Evidence: parity fixtures.
  - [ ] I7.17.d Link each divergence to exact registry entity, contract field,
        route/function, SDK method, UI control/host, test, owner, and source
        revision. Evidence: traceability tests.
  - [ ] I7.17.e Require disposition with reason/owner/expiry/evidence as fix,
        planned, accepted gap, not applicable, or policy-forbidden; prohibit
        dismissal/deletion. Evidence: disposition tests.
  - [ ] I7.17.f Alert on new/regressed/expired/stale divergence and gate
        releases according to severity without conflating hidden features with
        missing ones. Evidence: monitor/release tests.
  - [ ] I7.17.g Present filters/trends/diffs/ownership and export the same typed
        evidence accessibly at production scale. Evidence: monitor UI tests.
        <!-- MEASURED 2026-08-19 (I7.17.a-g). The parent's evidence is that "every divergence
        links to the exact missing contract/route/control and cannot be dismissed without a
        disposition", so the measurement asks what the estate's parity monitors actually COMPARE,
        and what happens to a divergence once one is found. Instrument:
        `generate-isis-parity-monitors.mts` (+7 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-parity-monitors.json`.

        A PAGE NAMED PARITY IS NOT A PARITY COMPARISON. The operator console ships six pages whose
        names end in "Parity" — Generation, Workflow, Outputs, Models, RunPod and ApiParityMonitor.
        FIVE OF THE SIX COMPUTE NO DIVERGENCE AT ALL: they are API exercisers, forms that call a
        method and show you the response (GenerationParityPage makes 9 live client calls and
        computes nothing; ModelsParityPage, RunPodParityPage and WorkflowParityPage make none).

        AND THE ONE THAT COMPARES ASKS ONE SIDE ABOUT ITSELF.
        `buildApiParityDomainSnapshot` decides a method is "available" with
        `typeof resourceRecord?.[method] === 'function'` — a property of the SDK object the page
        already imports — and compares it against `GENERATION_METHODS`, `WORKFLOW_METHODS`,
        `OUTPUT_METHODS`: hand-written string arrays in the page component. Both sides of the
        comparison ship in the same bundle. Remove a route from the generation service and neither
        side changes; coverage still reads 100%. Nothing reads an OpenAPI document, a registry or a
        runtime capability, which is .b's entire subject.

        AN EXPECTATION OF NOTHING IS NOT FULL COVERAGE: `formatCoverage` returns 100 when
        `totalCount <= 0`, so a dimension nobody has described reads as fully covered.

        WHAT THE MONITOR CAN SAY. 5 of .a's 10 coverage dimensions are named anywhere in it
        (workflow, model, API/SDK, control, output/target — and that is a generous floor, counting
        a dimension the monitor merely NAMES). 1 of .c's 7 divergence classes is emitted
        (`missing`); incompatible, stale, unauthorized exposure, undocumented, untested and
        intentionally unavailable have no representation. 2 of .d's 8 trace targets are linked
        (route and SDK method); a divergence names no registry entity, contract field, UI control,
        covering test, owner or source revision. Of .e's five dispositions, `accepted gap` and
        `policy-forbidden` exist as values NOWHERE in the estate, so a divergence cannot be
        dispositioned in the two ways that would otherwise let it survive honestly — and there is
        no prohibition on dismissal because there is nothing to dismiss it INTO. No release gate
        reads a divergence, and the page exports nothing.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The live
        probe beside the comparison is real: it calls `generation.listJobs` and `workflows.list`
        for true and reports healthy / degraded / unavailable with the actual error, never a
        fabricated verdict. (2) `buildApiParityDriftEvents` compares a previous snapshot with a
        current one and names the methods that were lost or regained rather than only a count, at
        three severities.

        TRAP. A WORD INSIDE A METHOD NAME IS NOT A DIMENSION: without word boundaries, `operation`
        matched inside the SDK method name `getProviderOperations`.

        0 of 9 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`. -->
- [ ] I7.18 Complete RunPod fleet and economics operations for endpoint classes,
      model/node images, warm/cold policy, GPU topology, cache, canary,
      drain/rollback, SLO, and cost attribution. Evidence: live topology and
      controlled failover journey.
  - [ ] I7.18.a Define endpoint class/pool/worker/GPU/image/model/node/cache/
        lifecycle/capacity/cost/SLO contracts and ingest authenticated live
        topology. Evidence: fleet schema/integration tests.
  - [ ] I7.18.b Manage signed/scanned/pinned model-node images and exact
        endpoint compatibility, rollout state, dependencies, and supply-chain
        evidence. Evidence: image admission journey.
  - [ ] I7.18.c Configure warm/min/max/idle/cold-start/autoscale/concurrency/
        region/GPU topology policy with capacity/cost/SLO preview and bounds.
        Evidence: scaling policy tests.
  - [ ] I7.18.d Inspect cache layers/keys/contents/size/hit/miss/eviction/
        integrity/warmup by authorized scope without exposing tenant data.
        Evidence: cache operations tests.
  - [ ] I7.18.e Roll out canaries with bounded traffic/capacity, comparison
        metrics, abort thresholds, observation window, and automatic/manual
        promotion rules. Evidence: canary journey.
  - [ ] I7.18.f Execute drain/failover/rollback with in-flight policy,
        reservation migration, health/capability validation, immutable
        decisions, and post-action verification. Evidence: controlled failover
        journey.
  - [ ] I7.18.g Present SLOs, queue/runtime/reliability/cold-start/resource
        efficiency, complete fixed/variable/egress/cache costs, allocation, and
        variance by class/pool/workflow/model/tenant. Evidence: economics tests.
  - [ ] I7.18.h Enforce operator roles, two-person high-impact controls,
        confirmation, rate limits, concurrency, emergency stop, audit, and
        accessible large-fleet UI. Evidence: abuse/operations suite.
        <!-- MEASURED 2026-08-19 (I7.18.a-h). The parent's evidence is a "live topology and
        controlled failover journey", so the measurement asks the binding question first: is there
        an authority to read a topology FROM? Instrument: `generate-isis-runpod-fleet.mts`
        (+6 tests), wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/isis-runpod-fleet.json`.

        THE ANSWER IS AN HONEST NO, WRITTEN DOWN — and this cell records it as CORRECT rather than
        as a gap in the code. `apps/oshun/bff/src/isis/runpod-endpoints-store.ts` is an explicitly
        unbound projection: every field `null` or `[]`, `readonly never[]` row types so a caller
        cannot push a fabricated endpoint past the compiler, and a frozen
        `RUNPOD_ENDPOINTS_DISPOSITION` declaring `mode: 'unbound_empty_projection'`,
        `authorityBinding: 'not_configured'`, `authoritativeState: false` and all 8 faculties —
        endpoint inventory, runtime health, tenant cost, budget envelopes, queue telemetry,
        failover history, secret-rotation authority and mutations — `unavailable`. That is the
        honest seam the quality bar asks for. .a's live topology is therefore not ingested, and
        the estate says so instead of inventing a fleet.

        FIXED: A DELIBERATE EMPTY PROJECTION READ AS A MALFORMED RESPONSE. The admin loader's
        `isContextShape` required `typeof record.operatorId === 'string'` with a NON-EMPTY value
        and `typeof record.nowUnixSeconds === 'number'`, and the unbound projection sends `null`
        for both. Driven with the exact body the route serves: `{ok:false,
        reason:'malformed-response'}` — on every request. The page then rendered a red alert
        telling the operator the BFF had misbehaved, when the BFF had answered exactly as
        designed. The loader also DROPPED the disposition it was sent, so nothing downstream could
        explain the empty tables. The guard now accepts the nulls the producer sends, the
        disposition is carried through, and the page renders an honest "no RunPod operations
        authority is bound to this deployment" notice naming the unavailable faculties — instead
        of a failure alert or a silent empty grid. `isis-runpod-endpoints-loader.spec.ts` (+6)
        drives the route's own body and keeps the genuinely-malformed and forbidden cases refusing.

        WHAT THE FLEET SURFACES CAN NAME: 7 of the 21 terms .a-.h operate on appear anywhere
        across the five surfaces (store, route, loader, page, panel) — GPU, SLO, cold start, cache,
        failover, rollback and cost. Nothing names an endpoint class, a pool, capacity, a
        model-node image, a signature/scan/pin, a warm or idle policy, autoscale bounds,
        concurrency, cache eviction, a canary, a drain, egress cost, or a two-person control. So
        .b's image admission, .e's canary rollout and .h's two-person high-impact control have no
        vocabulary at all, let alone a journey.

        THE FAILOVER JOURNEY IS REAL, AND THERE IS NO FLEET BEHIND IT. One admin e2e navigates to
        `/isis/runpod-endpoints`, opens the controls tab, asserts `record-failover` is DISABLED,
        fills a rationale, asserts it becomes enabled, clicks it and asserts the entry appears in
        the failover list. That is a properly gated, audited control — acting on an endpoint
        inventory the disposition declares unavailable.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The unbound
        projection above. (2) The admin route requires an authenticated caller with an Isis
        workspace scope (`admin:*`, `admin:studio` or `admin:workspace:isis`) before serving even
        the empty projection. (3) The panel treats a missing average runtime as ABSENT and falls
        back to the job's own estimate rather than reporting zero.

        TRAP. A MENTION IS NOT A JOURNEY, and a visit is not one either: the census requires a spec
        to navigate to a RunPod route AND exercise a drain, failover or rollback before it counts.

        3 of 11 properties present. ALL EIGHT CELLS AND THE PARENT LEFT `[ ]`: the fleet has no
        authority bound, so .a-.g have nothing to operate on, and .h's two-person control does not
        exist. -->
- [ ] I7.19 Complete model discovery and supply workflows for registry search,
      Civitai intake, collections, license/model cards, malware scanning,
      comparison, quality evaluation, model merging, fine-tuning/LoRA,
      promotion, quarantine, and revoke. Evidence: no external model or
      derivative reaches production without a complete evidence chain.
  - [ ] I7.19.a Build governed internal/external model discovery with
        allowlisted sources, authenticated metadata, filters, stable
        source/version IDs, snapshots, and no automatic admission. Evidence:
        discovery tests.
  - [ ] I7.19.b Intake Civitai/other approved artifacts by immutable download
        hash, source snapshot, uploader, files/dependencies, license/model card,
        training provenance, tags/triggers, and quarantine state. Evidence:
        external intake journey.
  - [ ] I7.19.c Build versioned curated collections with membership rationale,
        audience/use/target, compatibility, lifecycle, owner, and no lifecycle
        authority over member models. Evidence: collection tests.
  - [ ] I7.19.d Run malware/archive/format/serialization/signature/dependency/
        vulnerability scans in isolated bounded processing and retain results.
        Evidence: hostile artifact suite.
  - [ ] I7.19.e Compare models/versions on rights, provenance, compatibility,
        quality/safety calibration, latency/cost/resources, outputs, and
        uncertainty using pinned fixtures. Evidence: comparison journey.
  - [ ] I7.19.f Define model merge
        inputs/weights/method/config/tool/environment/ license compatibility,
        generate a new artifact identity, and rerun full admission evidence.
        Evidence: merge lineage tests.
  - [ ] I7.19.g Define fine-tune/LoRA dataset/splits/consent/rights/filtering,
        base model, method/config/seed/compute, checkpoints/evaluations, and new
        artifact lineage. Evidence: training journey.
  - [ ] I7.19.h Promote exact admitted versions only after complete license/
        provenance/security/quality/safety/compatibility approvals; quarantine/
        revoke with blast radius and fail-closed routing. Evidence: lifecycle
        journey.
        <!-- MEASURED 2026-08-20 (I7.19.a-h). The parent's evidence is that "no external model or
        derivative reaches production without a complete evidence chain", so the measurement asks
        the binding question first: is there a path by which an external model reaches production
        at all, and does anything on that path require the chain? Instrument:
        `generate-isis-model-supply.mts` (+13 tests), wired into
        `verify:inventory:v1-workbenches`, writing `evidence/v1-workbenches/isis-model-supply.json`.

        THE SUPPLY BOUNDARIES ARE HONESTLY UNBOUND AND SAY SO — recorded as CORRECT, not as a gap.
        All three (`civitai-intake-store.ts`, `model-merging-store.ts`, `lora-training-store.ts`)
        are explicit empty projections: `readonly never[]` rows so a caller cannot push a fabricated
        candidate past the compiler, and a frozen disposition per store declaring
        `mode: 'unbound_empty_projection'`, `authorityBinding: 'not_configured'`,
        `authoritativeState: false` and every faculty `unavailable` — the LoRA store names 8 of them
        (run inventory, dataset rights, execution, checkpoint telemetry, evaluation authority,
        promotion authority, lifecycle history, mutations). So .b's external intake, .f's merge
        execution and .g's training runs are not performed, and the estate says so instead of
        inventing an intake queue.

        FIXED: TWO READERS REJECTED THE PROJECTION THEIR OWN PRODUCER SENDS — the §I7.18 defect
        again, in two more routes. `isis-civitai-intake-loader.ts` and `isis-model-merging-loader.ts`
        both required `typeof record.operatorId === 'string'` with a NON-EMPTY value; both stores
        send `operatorId: null` because no reviewer directory resolves one. Driven with the exact
        bodies the two routes serve: `{ok:false, reason:'malformed-response'}` from both, on every
        request. Each page then rendered a red alert telling the operator the BFF had misbehaved,
        when the BFF had answered exactly as designed, and both loaders DROPPED the disposition, so
        nothing downstream could explain the empty queue or the empty component catalog. Both guards
        now accept the null the producer sends, both carry the disposition through, and both pages
        render an honest "no external model intake authority / no model-merging authority is bound
        to this deployment" notice naming the unavailable faculties. `isis-civitai-intake-loader.spec.ts`
        (+7) and `isis-model-merging-loader.spec.ts` (+7) drive the routes' own bodies and keep the
        genuinely-malformed, unauthenticated, forbidden and http cases refusing. The LoRA loader was
        already correct — it models the projection exactly via `isExactDisposition` — and is the
        pattern the other two now follow.

        A CATALOG IS NOT A GATE. `getModelService()` is called from exactly two non-test files in
        the generation API — `routes/models.ts` and the service itself — and ZERO of them are on the
        generation path. Nothing between a submitted job and a provider call asks the model registry
        whether an artifact was admitted, so .h's "fail-closed routing" has no site to fail closed
        at. The registry is a catalog the operator browses, not a gate generation passes through.

        A DEPRECATION IS NOT A REVOCATION. .h asks for quarantine and revoke; the canonical admin
        model-governance contract (`libs/contracts/src/common/admin-model-governance.ts`) can express
        NEITHER. Version statuses are production/staging/preview/deprecated/archived; promotion
        events are review_opened/review_approved/review_rejected/promoted/rolled_back/
        emergency_override; review states are not_required/pending/approved/rejected; rollout
        statuses are not_started/in_progress/completed/rolled_back. 0 of 2 withdrawal verbs are
        expressible in any of the four unions. `deprecated`/`archived` are voluntary retirement and
        `rolled_back` is a rollout state; an operator who discovers a poisoned artifact has no state
        to put it in.

        A REVIEW DECISION THAT NEVER LEAVES THE BROWSER IS NOT A REVIEW. `IntakeQueuePanel.tsx`
        wires 8 click handlers — approve, reject, conditional, takedown, denylist and three
        selection controls — and makes 0 network calls. Every decision calls `applyReviewDecision`
        and advances React state; `POST /v1/admin/isis/civitai-intake/decisions` exists, is
        authenticated and scope-checked, and answers 503 `intake_authority_not_configured` — and the
        panel never calls it. The panel then prints "OK for runtime" and "· staged" off that local
        state. `ModelMergingPanel.tsx` is the other shape: its one mutation control, "Save as
        managed model", has 0 click handlers at all, and `summarisePreview` — the A/B-against-a-
        fixture-set the panel's own doc comment claims — has 0 product callers, so the fixture-set
        selector selects nothing.

        A CONSUMER THAT NOTHING REACHES IS NOT REACH. .c's collections, .e's comparison and quality,
        .f's merging and .g's LoRA training are implemented — 13 modules, 23,393 lines, under
        `libs/isis/ai-providers/src/providers/civitai/`, every one of them published on the
        `@isis/ai-providers` barrel. Product code imports 29 runtime bindings from that package
        across 40 files, and 0 of the 29 come from any of the 13. Counted from import bindings whose
        SPECIFIER is this package, never from bare identifier matches: `interpolate`, `formatBytes`
        and `calculateOverallScore` are declared independently all over this monorepo, and a name
        census reports them as reach.

        A COMPILED SHADOW BESIDE ITS SOURCE WINS THE RESOLVER. All 13 of those modules have a
        tracked `.js` and `.d.ts` committed next to the `.ts`. Driven through Vite's own resolver
        against that directory: `./lora-training-provider` and four siblings resolve to the `.js`,
        because Vite's default extension order puts `.js` before `.ts`. Vitest uses that resolver,
        so the five provider tests in the directory grade the compiled snapshot rather than the
        source. 10 of the 13 `.js` were last regenerated BEFORE their `.ts` was last committed, and
        at least one diverges behaviourally: `model-quality-provider` emits trend entries as
        `{date,count}` in the `.js` and `{date,value}` in the `.ts`. Recorded, not removed:
        deleting 26 tracked build outputs out of a published package is a decision about that
        package's build contract, not a repair this cell is entitled to make.

        FLAGGED, NOT FIXED: .e's quality instrument manufactures its measurements.
        `ModelQualityProvider.ensureDownloadMetrics`/`ensureGenerationMetrics` build a 31-day series
        from `createHistory(modelId * 17 + 300, 31, 12)` and `createHistory(modelId * 31 + 1200, 31,
        28)` — a sine wave over the subject's own identifier — and the object around it carries 15
        literal rates and shares (`successRate: 97/95/93/91`, `percentage: 55/45/40/60/45/30/15/10`,
        `totalModelsInCategory: 100`) presented as telemetry, with no `not_configured` seam. It has
        0 product consumers, so nothing displays these today; it is recorded here rather than
        rewritten because .e has no bound quality authority to rewrite it against.

        WHAT AN INTAKE RECORD CAN CARRY: 3 of .b's 9 subjects. `CivitaiIntakeCandidate` and
        `ManagedModelRecord` carry the immutable download hash, the uploader (as
        `creatorAttribution`) and the license tag. They carry no source snapshot, no files or
        dependencies, no model card, no training provenance, no tags or trigger words, and no
        quarantine state.

        WHAT THE ADMISSION PATH SCANS: 2 of .d's 7 kinds. `model.service.ts` runs a malware scan and
        a format check (filename-matches-format plus a content-type allowlist); there is no archive,
        serialization, signature, dependency or vulnerability scan. Bounded yes — `assertAllowedUploadSize`
        — and isolated only in the sense that the one malware-capable engine is ClamAV, a separate
        daemon.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The three
        unbound projections above. (2) The model upload-complete path downloads the object,
        re-computes the digest over the bytes and refuses on a mismatch, rather than recording the
        hash the caller asserted; it also refuses on a size mismatch and on a disallowed content
        type. (3) Malware scanning is fail-closed on an engine that cannot see malware (the §I7.3
        fix): `MALWARE_CAPABLE_SCAN_ENGINES = {clamav}`, so an incapable engine refuses the upload
        instead of recording a clean verdict. (4) Both admin supply routes require an authenticated
        caller with an Isis workspace scope before serving even the empty projection. (5)
        `buildProvenanceBundle` in `@isis/lora-training-surface` computes a real SHA-256 over the
        canonical component set — .f's new artifact identity is honest arithmetic, it simply has
        nowhere to be written.

        TRAP. A WORD IN A COMMENT IS NOT A SCAN: `pickle` appears on the admission path exactly once,
        inside a doc comment explaining what the builtin engine MISSES, and an unstripped census
        reported a serialization scan that does not run. The stripper tracks string and template
        literals, because the naive version eats code. TRAP. A REFUSAL JOURNEY IS NOT AN ADMISSION
        JOURNEY: `isis-lora-training-boundary.spec.ts` navigates `/isis/lora-training`, exercises
        `promote`, and asserts 503 with 0 controls rendered — a properly built proof that the
        boundary refuses, and the opposite of the evidence the parent asks for. 6 e2e specs name a
        supply surface, 2 navigate to one, 1 exercises a decision, and 0 carry an artifact through a
        decision that succeeds.

        3 of 16 properties present. ALL EIGHT CELLS AND THE PARENT LEFT `[ ]`: no external supply
        authority is bound, nothing on the generation path checks admission, and the governance
        vocabulary cannot express the withdrawal .h requires. -->
- [ ] I7.20 Complete workflow and asset marketplaces only for authorized
      audiences, including submission, review, compatibility, versioning,
      pricing/entitlement, creator attribution, abuse, takedown, and payout
      reconciliation. Evidence: marketplace and creator-monetization journeys
      cannot bypass generation or release gates.
  - [ ] I7.20.a Define audience/host/region/tenant marketplace enablement and
        separate workflow-template, model/modifier, preset, source asset, and
        generated asset item contracts. Evidence: authorization matrix.
  - [ ] I7.20.b Implement creator enrollment/identity/tax/payout/rights
        authority, item submission, immutable files/hashes/dependencies,
        metadata, license, pricing, and attribution. Evidence: creator
        submission journey.
  - [ ] I7.20.c Run type-specific security/rights/consent/provenance/quality/
        safety/compatibility/generation/release gates and independent human
        review before listing. Evidence: review gate tests.
  - [ ] I7.20.d Version items immutably with compatibility matrices, dependency
        locks, migration/deprecation/revoke behavior, consumer notices, and
        historical entitlement. Evidence: version lifecycle tests.
  - [ ] I7.20.e Implement
        offers/prices/currencies/taxes/fees/refunds/promotions,
        entitlements/seats/terms/regions, purchase, receipt, and server-side use
        authorization. Evidence: commerce tests.
  - [ ] I7.20.f Preserve creator/source/version attribution through
        download/use/ derivative/generation/release and calculate royalty/payout
        ledger from verified events. Evidence: attribution journey.
  - [ ] I7.20.g Detect/report abuse, malware, fraud, stolen content,
        impersonation, review manipulation, license breach, and policy
        violations with case evidence and anti-retaliation controls. Evidence:
        abuse tests.
  - [ ] I7.20.h Execute takedown/quarantine/recall/appeal/restore with blast
        radius, consumer state, downloads/caches, payouts/holds, notifications,
        and immutable decisions. Evidence: takedown journey.
  - [ ] I7.20.i Reconcile order/refund/fee/tax/royalty/hold/payout/chargeback
        per item/version/creator/currency and expose discrepancies without
        double payment. Evidence: payout reconciliation tests.
  - [ ] I7.20.j Verify direct API/deep-link/import entitlement bypass, cross-
        tenant access, gate bypass, creator self-approval, accessibility, and
        marketplace scale. Evidence: monetization E2E suite.
        <!-- MEASURED 2026-08-20 (I7.20.a-j). The parent's evidence is that "marketplace and
        creator-monetization journeys cannot bypass generation or release gates", so the
        measurement asks the binding question first: is there a marketplace journey at all — can an
        item be listed, bought, or turned into an entitlement? Instrument:
        `generate-oshun-marketplace-surface.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-marketplace-surface.json`.

        THE FIVE MARKETPLACE ROUTES ARE CALCULATORS, NOT A MARKETPLACE. Three validate a listing
        draft the caller submits (asset, ai-model, plugin), one filters a `listings` ARRAY the
        caller submits in the request body, and one computes a collaborator revenue split for
        collaborators the caller submits. All five are request-time pure functions; 0 of 5 persist
        anything, there is no catalog, no order, no receipt and no payout ledger anywhere in the
        estate. So the parent's evidence HOLDS VACUOUSLY: no purchase can bypass the generation
        gate because no purchase exists. Recording the difference matters — a later reader must not
        take a green parent for a marketplace that was built and then locked down.

        FIXED: TWO LISTS FOR ONE JUDGEMENT, AND THE ONE THAT RUNS WAS MISSING A PERMISSION THE
        OTHER CALLS DANGEROUS. `@yemaya/marketplace`'s own submission checker named
        `{system:process, filesystem:write, network:websocket}` dangerous;
        `apps/oshun/bff/src/yemaya/plugin-manifest-store.ts` — the validator an operator actually
        reaches at `POST /v1/admin/yemaya/plugin-marketplace/validate` — hand-copied a DIFFERENT
        set, `{filesystem:write, system:process, system:env, crypto:sign}`. The two disagreed on 3
        of 5 members: a manifest requesting `network:websocket` cleared the validator that runs
        with zero warnings, and one requesting `system:env` or `crypto:sign` cleared the library's
        checker. Neither side imported the other; the store also hand-copied the 18-member
        permission union and the 7-member type union out of a contract it only cited in a comment.
        `HIGH_RISK_PLUGIN_PERMISSIONS` and `PLUGIN_PERMISSIONS` are now published from
        `@yemaya/marketplace` and read by BOTH — the high-risk set is the union of the two prior
        lists, because each side had independently judged its members to warrant review and
        dropping either side's would be a downgrade. `plugin-permission-vocabulary.spec.ts` (+6)
        drives the library's checker over every member and holds the union closed;
        the route test (+1) drives `network:websocket` through the BFF and asserts the warning.

        FIXED: A FIELD-SHAPE VERDICT IS NOT PUBLISH APPROVAL. The three validators answered
        `{valid: true, errorCount: 0, warningCount: 0, issues: []}` from routes documented as
        "publish-readiness", with nothing at all saying which gates were still owed — no security,
        rights, consent, provenance, quality, safety, compatibility, generation or release gate had
        run, no human had reviewed it, and nothing had been listed, priced, sold or entitled. Each
        store now publishes a frozen `*_VALIDATION_DISPOSITION` naming all ten of .c's reviews and
        gates `unavailable` alongside `listingPublication`, `pricingAuthority`, `entitlementGrant`
        and `mutations`, and each route serves it BOTH on the catalog and beside every verdict, so
        `valid: true` never travels alone. Three route tests (+1 each) assert the pairing.

        A CONSUMER THAT NOTHING REACHES IS NOT REACH. `libs/isis/3d-marketplace-ops/src` holds 25
        modules that answer .c-.i almost name for name — `moderation-surface-policy`,
        `policy-enforcement`, `publisher-verification`, `rights-declaration`, `listing-lifecycle`,
        `listing-versioning`, `listing-commercial-terms`, `chain-of-custody`,
        `listing-abuse-reporting`, `duplicate-detection`, `leak-tracing`, `trust-scoring`,
        `takedown-intake`, `listing-hold-policy`, `payout-ledger`, `refund-dispute-workflow`,
        `seller-payout-readiness`, `sales-analytics`. Product code imports exactly ONE runtime
        binding out of all 25: `createCollaboratorRevenueSplitPlan`. Counted from import bindings
        whose SPECIFIER is the package — `getRecipe`, `checkCompatibility` and `interpolate` are
        declared independently across this monorepo and an identifier census reports them as reach.

        WHAT THE REACHABLE SURFACES CAN NAME: 2 of .a's 5 item contracts (model/modifier and source
        asset; no workflow template, no preset, no generated asset — and `plugin`, which .a does
        not name, is the third that exists). 0 of 4 enablement dimensions — audience, host, region
        and tenant appear nowhere, so .a's authorization matrix has no axes. 1 of 9 commerce nouns
        (`entitlement`; no offer, tax, fee, refund, promotion, seat, purchase or receipt). 1 of 10
        lifecycle verbs (`payout`, and only as a disposition faculty declaring itself unavailable;
        no deprecate, revoke, abuse report, takedown, quarantine, recall, appeal, restore or
        chargeback).

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        generation creator tier is resolved from the AUTHENTICATED token's scopes; the
        `x-oshun-creator-tier` header elevates only outside production, and 0 marketplace nouns are
        read in `generation-admission.ts`. (2) `parseEntitlementsFromRequest` likewise honours the
        `x-oshun-tier` header only outside production, and per-domain overrides — which can
        ELEVATE — are dev-only while suspensions, which only restrict, are honoured everywhere.
        (3) Every marketplace route requires an authenticated caller with a workspace admin scope
        before answering. (4) The creator-monetization planner declares itself an ephemeral
        request-time pure function with plan persistence, rights authority, listing publication,
        payout activation and settlement all unavailable, and collaborator identity `unverified`
        and terms acceptance `self_asserted_unverified`. (5) The unified search says in its own
        summary that it searches the listings the caller supplies — it does not present itself as a
        catalog.

        TRAP. A WORD IN A COMMENT IS NOT A CAPABILITY: every one of these surfaces describes in
        prose what a marketplace would do, so an unstripped census reports takedowns and payouts no
        line of code performs. TRAP. A ROUTE HELD IN A CONST IS STILL A ROUTE: the monetization
        suite interpolates `const ROUTE = '/studio/isis/creator-monetization'` into `page.goto`,
        and a literal-only match reported 0 visits to a page it walks in depth. 3 e2e specs name a
        marketplace surface, 1 navigates to one, and 0 exercise a purchase or entitlement grant.

        2 of 14 properties present. ALL TEN CELLS AND THE PARENT LEFT `[ ]`: nothing is listed, so
        nothing can be reviewed, versioned, priced, attributed, taken down or reconciled. -->
- [ ] I7.21 Complete operator security and financial controls for entitlements,
      quotas, budgets, cost optimization, chargeback fraud, suspicious activity,
      account protection, anomaly triage, feature flags, and emergency kills.
      Evidence: every recommendation is explainable and every mutation is
      authorized, audited, reversible where possible, and tested against abuse.
  - [ ] I7.21.a Build effective-entitlement inspector/change workflow with
        source, scope, conditions, expiry, conflicts, preview, reason, approval,
        propagation, and revocation. Evidence: entitlement journey.
  - [ ] I7.21.b Build quota/budget inspector and bounded change workflow for
        hierarchy, window, usage/reservations, forecasts, thresholds, alerts,
        exceptions, and effective date. Evidence: quota/budget journey.
  - [ ] I7.21.c Generate cost-optimization recommendations from attributed
        versioned measurements with assumptions, confidence, projected savings/
        quality/latency/capacity impact, alternatives, and no automatic apply.
        Evidence: recommendation explainability tests.
  - [ ] I7.21.d Detect chargeback/payout/refund/purchase/usage fraud signals,
        preserve event/feature/rule/model versions, avoid protected-attribute
        inference, and route to human cases. Evidence: fraud triage tests.
  - [ ] I7.21.e Build suspicious-activity/anomaly triage for auth, data access,
        model/workflow intake, dispatch, spend, provider, marketplace, and
        release with baseline/threshold/uncertainty. Evidence: anomaly journey.
  - [ ] I7.21.f Implement account/session/API-key/device protection, step-up
        authentication, lock/recovery, session revoke, and high-risk action
        confirmation without exposing secrets. Evidence: account abuse tests.
  - [ ] I7.21.g Govern feature flags by typed scope/audience/environment/
        prerequisite/owner/expiry, staged rollout, evaluation, rollback, and
        audit; prohibit flags from bypassing code floors. Evidence: flag tests.
  - [ ] I7.21.h Implement scoped emergency kills for route/workflow/model/
        provider/tool/marketplace/release with two-person authority where
        required, immediate propagation, in-flight policy, and restore gate.
        Evidence: kill-switch game day.
  - [ ] I7.21.i Make every mutation previewable, permission/state checked,
        reasoned, confirmed, optimistic-concurrency safe, immutable in audit,
        and reversibly compensated where possible. Evidence: control invariant
        suite.
  - [ ] I7.21.j Test insider, confused-deputy, mass-assignment, replay, race,
        enumeration, cross-tenant, cost-evasion, fraud, and emergency-abuse
        paths plus accessible operator recovery. Evidence: adversarial E2E
        suite.
        <!-- MEASURED 2026-08-20 (I7.21.a-j). The parent's evidence is that "every mutation is
        authorized, audited, reversible where possible, and tested against abuse", so the
        measurement enumerates the mutations first and asks the invariants of each. Instrument:
        `generate-oshun-mutation-invariants.mts` (+8 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-mutation-invariants.json`.

        A CENSUS IS ONLY AS GOOD AS ITS EXTRACTOR, AND THIS ONE WAS WRONG THREE WAYS BEFORE IT WAS
        RIGHT. The first pass reported 28 mutations, 4 of them audited, and no emergency kill
        switch anywhere in the estate. All three numbers were artefacts of the reader.
        (1) A ROUTE BUILT FROM A TEMPLATE LITERAL IS STILL A ROUTE — `admin-agentic-operations.ts`
        registers its kill switch as `${prefix}/agentic-operations/kill-switches` through a helper
        called with '/admin' and '/v1/admin', and a literal-only extractor cannot see it.
        (2) A HANDLER PASSED BY NAME IS STILL THE HANDLER — those routes pass `killSwitchHandler`
        by identifier, so every invariant measured on the registration call reads absent.
        (3) A GUARD HELD IN A HELPER IS STILL A GUARD, TRANSITIVELY — `requireScenePublish`
        delegates to `hasScenePublishScope`, and a one-level scan calls its handlers unscoped.
        Resolving all three moved the population to 81 and the audited count to 52. Two more traps
        were needed to keep it from over-counting: a `.push()` ON A LOCAL ARRAY IS NOT A MUTATION
        (371 of the 407 admin POST handlers are calculators), and a `reason` FIELD IN AN ERROR
        REPLY IS NOT AN OPERATOR'S RATIONALE (`reason: 'missing_auth_context'` matched every
        handler in the estate).

        THE ANSWER, ON 81 DURABLE ADMIN MUTATIONS: 81 authorized, 52 audited, 12 reasoned, 8
        optimistic-concurrency safe, 0 previewable. The authorization half of the parent's claim
        holds outright — every one runs behind an authentication preHandler and checks an admin
        scope in the handler. The rest does not.

        THE DECISIONS THAT MOST NEED A RECORD ARE THE ONES WITHOUT ONE. The 29 unaudited mutations
        are not a random tail. They are every consent act (`aja/consent/grant`,
        `aja/content-security/grants` and its revoke, `rail/ori/consents/:userId/:scope/revoke`),
        every moderation act (`moderation/queue`, `.../:itemId/decide`, `appeals/:appealId/resolve`,
        `bans/:banId/revoke`), the appeal decision in `themis`, all six Isis ledger writes and both
        reference-bank writes, the model-governance upload, the output-gallery bulk action and the
        crisis-suppression toggle. Meanwhile the communications, integrations and
        notification-template CRUD is audited end to end. An operator can grant a consent, decide a
        moderation case and revoke a ban, and the estate keeps no record that they did.

        FIXED: AN INVITE ID IS NOT A TENANT SCOPE. `POST /v1/admin/tenant-console/members/invite/
        :inviteId/revoke` looked its subject up by id in the store's global invite map and revoked
        whatever it found, with no check that the invitation belonged to the caller's tenant and no
        audit event — while the sibling create in the same file resolves the acting tenant and
        writes `tenant.member_invite`. So a tenant-console admin acting in one tenant could
        withdraw another tenant's pending invitation and leave no trace of having done so. The
        store's `revokeInvite`/`revokeInviteDurably` now require the tenant and answer 404 for an
        invitation belonging to a different one — indistinguishable from an id that does not exist,
        so the boundary does not leak which ids are real. The route resolves the tenant like its
        sibling and records `tenant.member_invite_revoked` with the actor. Driven end to end:
        the route spec (+1) creates invitations in two tenants, gets 404 for the cross-tenant
        revoke, confirms the other tenant's invitation is still pending, revokes its own, and reads
        exactly one revocation event back out of the audit store; the store spec (+1) holds the
        same boundary at the unit level.

        A PROMPT COMPRESSOR IS NOT A COST RECOMMENDATION. .c asks for recommendations carrying
        assumptions, confidence, projected savings, latency and capacity impact, and alternatives.
        The one cost surface an operator can reach, `cost-optimization-store.ts`, is a stateless
        token-budget optimiser: it runs prompt text through a real compression technique and
        returns a compression ratio and a quality score. 0 of .c's 6 explainability subjects appear
        on it. It is honest about what it is — `mode: 'stateless_request_engine'`,
        `authoritativeState: false` — it simply is not the thing .c describes.

        WHICH CONTROL WORKFLOWS EXIST: 1 of 5. There is a real emergency kill switch (.h), audited
        and flushed before the reply. There is NO mutating route for an entitlement change (.a), a
        quota or budget change (.b), or a feature flag change (.g) — the three routes whose paths
        carry those words (`studio/experimentation-flags/evaluate`,
        `studio/performance-budgets/evaluate`, `yemaya/budget-management/optimize`) are evaluators
        that write nothing.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) All 81
        durable admin mutations are authenticated and admin-scoped; there is no unauthenticated
        write in the admin surface. (2) The agentic-operations kill switch, gold-set promotion and
        champion-challenger rollout each record an audit event and await a persistence flush before
        replying. (3) The tenant-console invite create resolves the acting tenant and records a
        tenant-scoped audit event, which is why its revoke's silence was visible at all.

        2 of 10 properties present. ALL TEN CELLS AND THE PARENT LEFT `[ ]`: authorization holds,
        auditing covers 52 of 81, nothing is previewable, and four of the five control workflows
        the cells describe have no route to mutate through. -->

### I8 — Review, release, gallery, and lifecycle

- [ ] I8.1 Define review templates by asset kind, risk, audience, and delivery
      target. Evidence: required roles and quorum matrix.
  - [ ] I8.1.a Define immutable review-template identity/version/lifecycle,
        applicability selectors, required review dimensions, roles, quorum,
        sequence/parallelism, deadlines, escalation, and code floors. Evidence:
        review template schema.
  - [ ] I8.1.b Map every publishable asset kind/profile to the applicable
        visual/audio/3D/editorial/technical review dimensions. Evidence: asset
        coverage matrix.
  - [ ] I8.1.c Add risk-class requirements for child/identity/voice/biometric/
        sensitive content, protected sources, high-cost, high-reach, and novel
        workflows. Evidence: risk matrix.
  - [ ] I8.1.d Add audience/host/use/territory/channel and delivery-target-
        specific reviewer roles, evidence, and acceptance criteria. Evidence:
        audience/target matrix.
  - [ ] I8.1.e Validate separation of requester/creator/reviewer/approver and
        quorum independence, conflicts, substitutions, and abstention. Evidence:
        role/quorum policy tests.
  - [ ] I8.1.f Resolve the exact template/version deterministically at candidate
        creation and invalidate eligibility when applicability changes.
        Evidence: resolution/freshness tests.
        <!-- MEASURED 2026-08-20 (I8.1.a-f). The parent's evidence is a "required roles and quorum
        matrix", so the measurement asks the binding question first: can a review template in this
        estate name a required role, or a number of them? Instrument:
        `generate-oshun-review-templates.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-review-templates.json`.

        TWO REVIEW-TEMPLATE CONTRACTS, AND NEITHER CAN. `admin-review-stage.ts`'s
        `AdminReviewTemplate` carries six fields — templateId, artifactClass, label, description,
        requiredSections, defaultChecklist. `review-package.ts`'s `ReviewTemplateSchema` carries
        five — templateId, label, description, artifactTypes, requiredStageTypes. 3 of .a's 11
        subjects are expressible across BOTH of them: an immutable identity, an applicability
        selector and a set of required dimensions. There is no version, no lifecycle, no role, no
        quorum, no sequence or parallelism, no deadline, no escalation and no code floor on either.
        A CHECKLIST IS NOT A QUORUM: the template says what to look at, never who must look or how
        many of them.

        THE VOCABULARY EXISTS AND IT ATTACHES SOMEWHERE ELSE. `ReviewRoleSchema` enumerates eleven
        review roles — collaboration_lead, program_operator, domain_integrator, security_reviewer,
        compliance_auditor, moderator, editor, support_operator, privacy_officer, release_manager,
        reviewer — and the estate validates that a delegation or escalation actually CHANGES role.
        All three consumers are `libs/shared/review-persistence` modules: the roles attach to stage
        assignments and to recorded decisions, never to a template's requirement. So an operator
        can record which role decided, and no template can say which role had to.

        FIXED: A QUORUM IS A COUNT OF PARTIES, NOT OF SIGNATURES.
        `apps/oshun/bff/src/studio/approval-workflow-store.ts` computed
        `approvals.filter(approve).length` and compared it to `requiredApprovals`. `approver` is
        captured on every entry and was never compared — no dedup, no distinctness check. Driven on
        the evaluator itself: a stage with `requiredApprovals: 3` and three entries all from
        `ops-1` returned `{status: 'approved', approveCount: 3, approvalsNeeded: 0}` — byte-identical
        to the verdict for three independent reviewers. Approving entries are now collapsed by
        approver before the count, so one reviewer contributes one vote however many entries they
        submit; a genuine two-hat reviewer still counts once and keeps BOTH roles available to
        satisfy `requiredRole`, and the discarded repeats are reported as `duplicateApprovals` so
        the operator can see them rather than having them vanish. The route spec (+1) drives all
        three cases through the real HTTP surface; every pre-existing expectation is unchanged
        because every existing fixture already used distinct approvers.

        AND THE EVALUATOR STILL CANNOT SEPARATE DUTIES. .e asks for separation of requester,
        creator, reviewer and approver. `ApprovalInput` carries `approver`, `role` and `decision`
        — 1 of the 4 duties. There is no requester and no creator anywhere on the input, so the
        evaluator has nothing to compare an approver AGAINST. Distinctness within the quorum is now
        enforced; independence from the work under review is not expressible.

        A GOVERNANCE CLASS IS NOT AN ASSET KIND. .b asks that every publishable asset kind be
        mapped to its visual/audio/3D/editorial/technical review dimensions. The applicability axis
        both contracts actually carry is a six-member governance class — release_gate,
        model_promotion, runtime_risk, policy_change, content_review, default — with exactly one
        template each. 1 of .b's 5 review dimensions (editorial) appears anywhere in either
        contract.

        1 of .c's 9 risk classes and 1 of .d's 6 audience axes appear in either contract. TRAP. A
        WORD IN A CONTRACT IS NOT A REQUIREMENT: the one risk hit is `voice_profile`, an artifact
        TYPE, not a rule that voice work needs a particular reviewer; the one audience hit is
        `ReviewReleaseChannelSchema`. And bare `target` matched `targetId` and `targets` until the
        spelling was tightened, which would have reported a delivery-target axis that does not
        exist.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1)
        `getAdminReviewTemplate` resolves exactly one template per artifact class with an explicit
        `default` fallback that throws if the default is missing, and `ReviewTemplatePanel` resolves
        it from the review record's OWN class rather than from a caller-supplied one — .f's
        deterministic resolution holds. (2) A reject on any approval rejects the stage outright,
        whatever the approval count. (3) The delegation/escalation validator refuses a transition
        that leaves the role unchanged.

        TRAP. A CONSTANT WITH NO IMPORTERS CAN STILL BE READ THROUGH ITS ACCESSOR: grepping for
        `ADMIN_REVIEW_TEMPLATES` finds only the contract and its own spec, which reads as six
        templates nothing consumes. The panel calls `getAdminReviewTemplate`, so the templates are
        rendered — ask of the accessor, not only of the constant.

        WHAT .f CANNOT DO: neither contract carries a version or a lifecycle, so a resolved template
        cannot be pinned to the candidate and a later change in applicability has nothing to
        invalidate. Resolution is deterministic and re-runs from scratch every render.

        2 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: the quorum now counts
        parties and resolution is deterministic, but no template can require a role or a quorum,
        which is the matrix the parent asks for. -->
- [ ] I8.2 Add anchored review comments for regions, frames, timecodes, graph
      nodes, mesh parts, and audio ranges. Evidence: anchor survival tests.
  - [ ] I8.2.a Define immutable comment/thread/reply identity, subject revision,
        typed anchor, author, visibility, status, mentions, attachments,
        resolution, and supersession. Evidence: comment schema tests.
  - [ ] I8.2.b Implement image/video region anchors in canonical pixel/
        normalized/frame coordinate space with crop/orientation/version context.
        Evidence: region anchor tests.
  - [ ] I8.2.c Implement frame/timecode/shot/range anchors with rate/time-base/
        sequence revision and retime/edit mapping. Evidence: temporal anchor
        tests.
  - [ ] I8.2.d Implement graph node/port/edge/parameter/subgraph anchors using
        stable graph entity/version identities rather than screen coordinates.
        Evidence: graph anchor tests.
  - [ ] I8.2.e Implement mesh/scene object/component/face/vertex/UV/material/
        animation anchors with stable semantic IDs and transform/LOD context.
        Evidence: 3D anchor tests.
  - [ ] I8.2.f Implement audio sample/timecode/clip/track/channel/speaker/
        transcript-token ranges with sample/time-base context. Evidence: audio
        anchor tests.
  - [ ] I8.2.g Map anchors across compatible revisions with exact/shifted/
        ambiguous/orphaned status and preserve the original anchor. Evidence:
        revision survival tests.
  - [ ] I8.2.h Provide synchronized accessible anchor lists/navigation,
        authorization, notifications, resolve/reopen history, and no comment
        authority over approval. Evidence: review UI/API tests.
        <!-- MEASURED 2026-08-20 (I8.2.a-h). The parent's evidence is "anchor survival tests", so
        the measurement asks the binding question first: is there a typed anchor at all, and does
        anything map one across a revision? Instrument: `generate-oshun-review-anchors.mts`
        (+8 tests), wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-review-anchors.json`.

        AN UNTYPED KIND-AND-VALUE PAIR IS NOT A TYPED ANCHOR. The estate has exactly one
        review-comment anchor: `AdminEntityCommentAnchorSchema = { kind: string(1..80), value:
        string(1..400) }`, and its own doc comment says the values are "intentionally free-form so
        each workspace can carry its own target shape (line, field, region) without a rigid
        contract". It can HOLD a serialisation of a region, a timecode, a graph node, a mesh face
        or an audio range, and it can VALIDATE none of them — `kind: 'anything-at-all'` is a
        well-formed anchor. So .b's coordinate space, .c's time base, .d's stable graph identity,
        .e's semantic mesh ID and .f's sample rate are not merely absent from the contract; there
        is nowhere in it for them to go. 0 of the 18 context terms those five families need appear
        in either source that references the anchor.

        AND THE COMMENT CANNOT SAY WHICH REVISION IT WAS ANCHORED TO. `AdminEntityCommentSchema`
        carries 6 of .a's 11 subjects — comment identity, thread/reply identity, author, status,
        mentions and resolution. It carries no subject revision, no visibility, no attachments, no
        supersession, and no typed anchor. `revision` on the record is the COMMENT's own edit
        counter, not the revision of the thing it points at. Without a subject revision, .g's
        survival mapping is not unimplemented — it is unaskable, because nothing can tell whether
        the anchored thing moved. 0 sources carry any re-anchoring or anchor-status logic, and 0 of
        .g's four statuses (exact/shifted/ambiguous/orphaned) appear beside an anchor.

        FIXED: A RESOLVE IS A REVIEW ACT. `POST .../entities/:entityId/comments` recorded
        `admin.entity_comment.created` (or `.entity_annotation.created`) and flushed it; `PATCH`
        and `DELETE` on the same comment recorded NOTHING. So the record held the moment a finding
        was raised and not the moment it was closed, reopened, reworded, moved to a different
        anchor or deleted — and the PATCH handler is the one that can change the anchor. The
        update now records `admin.entity_comment.resolved`, `.reopened` or `.updated` by what the
        request actually did, with `bodyChanged`/`anchorChanged`/`mentionsChanged` in the payload,
        and the delete records `admin.entity_comment.deleted`; both flush before replying, like the
        create. The route spec (+1) drives create → move the anchor → resolve → reopen → delete and
        reads all five events back in order out of the audit store.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        approval evaluator never mentions a comment: a stage is decided by approvals alone, so
        .h's "no comment authority over approval" holds. (2) A comment update preserves the
        existing anchor when the request omits it and clears it only on an explicit null. (3) A
        body edit by a non-author is refused with 403.

        TRAP. A SUBSTRING IS NOT A SUBJECT. Scanning `libs/` and `apps/oshun/` for the word
        "anchor" finds 97 files and 43 more carrying re-anchoring logic — every one of them an AR,
        XR, cinema or brand-strategy anchor with nothing to do with a review comment, and counting
        them would have credited .b-.g with 12 of 18 context terms and 43 survival sources. The
        honest population is the sources that reference THIS contract by name: two of them. A
        GENERATED CLIENT CONTAINS THE WHOLE API — `libs/openapi/src/generated/oshun-bff.ts`
        mentions the anchor and also every unrelated `trackId`, `channel` and `sampleRate` in the
        estate; it is excluded for the same reason. And bare `normalized` matched a local
        mention-identity variable, crediting a coordinate space that does not exist.

        2 of 11 properties present. ALL EIGHT CELLS AND THE PARENT LEFT `[ ]`: the comment
        lifecycle is now fully recorded and comments hold no approval authority, but the anchor is
        untyped, the subject revision is absent, and nothing maps an anchor across a revision. -->
- [ ] I8.3 Add safety, rights, identity, quality, brand/editorial, and technical
      signoff gates. Evidence: separation-of-duties policy tests.
  - [ ] I8.3.a Define distinct signoff contracts for safety, rights/consent,
        identity, quality, brand/editorial, and technical/target dimensions with
        required evidence and decision codes. Evidence: signoff schemas.
  - [ ] I8.3.b Resolve eligible reviewers/approvers, role separation, conflict-
        of-interest, quorum, delegated/substitute rules, and expiry per
        dimension. Evidence: duty matrix.
  - [ ] I8.3.c Bind each signoff to exact candidate/bundle revision and hashes,
        target, measurements/verdicts, policy/template versions, comments, and
        evidence snapshot. Evidence: binding tests.
  - [ ] I8.3.d Implement approve/reject/request-changes/abstain with immutable
        decision, rationale, conditions, and no cross-dimension authority.
        Evidence: decision tests.
  - [ ] I8.3.e Calculate aggregate readiness only when every required dimension
        has fresh valid quorum and no blocking conflict/condition. Evidence:
        aggregation tests.
  - [ ] I8.3.f Invalidate affected signoffs on subject/evidence/policy/role/
        rights change without deleting prior decisions. Evidence: freshness
        tests.
        <!-- MEASURED 2026-08-20 (I8.3.a-f). The parent's evidence is "separation-of-duties policy
        tests", so the measurement asks the binding question first: does anything on the review
        signoff path refuse an approval from the person who requested or created the work?
        Instrument: `generate-oshun-signoff-gates.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing `evidence/v1-workbenches/oshun-signoff-gates.json`.

        THE ESTATE KNOWS HOW TO REFUSE A SELF-APPROVAL — IN NINETEEN PLACES, NONE OF THEM THIS ONE.
        The exemplar is Oshun's own: `libs/oshun/workbench-kit/src/gate-waivers.ts` flags
        `approver-is-the-requester`, `approver-is-the-accountable-owner` AND
        `approver-role-is-not-the-one-the-gate-names` — .b's duty matrix, implemented, in this
        estate. Neith's privileged-access elevation throws 'requester cannot approve own
        elevation'; the vault escrow, PAM approval, SCIM lifecycle, IDP recovery, Metis peer
        review, Mnemosyne assessment, Psyche's tool-call approvals and the Yemaya study-workspace
        release path all compare a decider against a requester, author or owner. 0 of the 19 are on
        the review signoff path — and the reason is structural: `ApprovalInput` is
        `{approver, role, decision}`, so the evaluator has no second identity to compare against.

        RECORDED AS A DECISION, NOT FIXED. Adding an optional `requestedBy` to the workflow input
        and refusing that party's approval would be five lines, but the whole input is
        caller-supplied: a caller who omits the field gets no protection while the response looks
        identical to one that was checked. A half-guarantee that reads like a full one is worse
        than a stated gap, and choosing where the requester identity comes from is a contract
        decision this cell is not entitled to make alone.

        THREE OF THE SIX DIMENSIONS HAVE NO STAGE TYPE. `ReviewStageTypeSchema` enumerates twelve —
        review, approval, security, compliance, release_gate, rights, privacy, safety, editorial,
        accessibility, localization, appeal. .a's safety maps to `safety`, rights/consent to
        `rights` and `privacy`, brand/editorial to `editorial`. Identity, quality and
        technical/target map to nothing, so a signoff in those three dimensions cannot be raised
        under a type that names it, let alone carry its own required evidence.

        THE DUTY MATRIX AND THE BINDING DO NOT REACH THE EVALUATOR THAT RUNS. 3 of .b's 6 duty
        subjects appear in the review contracts (quorum, delegation, expiry) and 1 reaches the
        evaluator an operator can call — quorum, as `requiredApprovals`. Eligible reviewers, role
        separation and conflict of interest appear in neither. 1 of .c's 7 binding subjects appears
        in the contracts and 0 on the evaluator: it receives no candidate revision, no hash, no
        target, no measurement, no policy or template version and no evidence snapshot. .f's
        invalidation is therefore not unimplemented but unaskable — the evaluator cannot tell that
        any of those changed because it never saw them.

        THERE IS NO WAY TO ABSTAIN. .d names approve/reject/request-changes/abstain. Both contracts
        stop at three: `ReviewDecisionOutcomeSchema` is approved|rejected|changes_requested and
        `ADMIN_REVIEW_DECISION_OUTCOMES` is none|approved|rejected|changes_requested. A reviewer
        with a conflict has no code to record that they stood down, so the only ways out of a
        quorum are to approve, to reject, or to leave the stage pending forever.

        A QUORUM WITH NO CLOCK CANNOT BE STALE. The aggregate is real — the workflow reports
        `approved` only when every stage is approved — but `ApprovalInput` carries no decision
        time, so .e's "fresh valid quorum" has nothing to measure freshness against and an approval
        counts forever.

        THE MODEL THAT COULD HAVE DONE ALL OF THIS HAS NO CONSUMER. `@oshun/review-persistence`
        publishes 18 modules — a decision lifecycle with prior-decision lineage, a delegation
        policy, a release-validity service, a review queue with SLA weighting, a template registry
        and in-memory repositories — and 0 product files import it. Its own contracts carry
        attestations with `expiresAt` and validation that an expired attestation must name when it
        expired. None of that is reachable from any route.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) Each stage
        is evaluated over its own approvals array, so one dimension's quorum cannot be filled from
        another's — .d's "no cross-dimension authority" holds. (2) A reject on any approval rejects
        that stage outright, and any rejected stage rejects the workflow. (3) The aggregate
        requires every stage approved before reporting the workflow approved.

        TRAP. A COMPARISON IS NOT ALWAYS A REFUSAL: the 19 include eligibility checks
        (`accountable-signoff.ts` compares the signer against the owner and their delegates) as
        well as outright self-approval refusals. Both are duty checks, which is why the census
        counts "compares a deciding identity against another party's" rather than claiming
        nineteen self-approval guards.

        1 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: dimensions do not
        separate duties, signoffs bind to nothing, and the only thing the reachable evaluator
        enforces is that one dimension cannot borrow another's approvals. -->
- [ ] I8.4 Lock approved release candidates to exact revisions and hashes.
      Evidence: post-approval mutation invalidates approval.
  - [ ] I8.4.a Define release-candidate manifest listing exact
        asset/output/edit/
        metadata/rights/provenance/gate/target-profile/dependency revisions and
        cryptographic hashes. Evidence: candidate schema.
  - [ ] I8.4.b Canonicalize and hash the complete candidate manifest and
        required files before review begins. Evidence: hash golden tests.
  - [ ] I8.4.c Bind every review/signoff/exception/confirmation to the manifest
        digest and immutable candidate identity. Evidence: approval binding
        tests.
  - [ ] I8.4.d Make approved candidate records immutable and route any content/
        metadata/dependency change through a new revision/candidate. Evidence:
        mutation refusal tests.
  - [ ] I8.4.e Rehash/revalidate manifest and stored bytes at release planning,
        immediately before execute, and after target assembly. Evidence: TOCTOU
        tests.
  - [ ] I8.4.f Mark prior approvals stale when any bound version/hash/policy/
        evidence changes while retaining historical approval. Evidence:
        invalidation tests.
        <!-- MEASURED 2026-08-20 (I8.4.a-f). The parent's evidence is that a "post-approval
        mutation invalidates approval", so the measurement asks the binding question first: is an
        approval bound to anything a mutation could change? Instrument:
        `generate-oshun-release-locking.mts` (+7 tests), wired into
        `verify:inventory:v1-workbenches`, writing `evidence/v1-workbenches/oshun-release-locking.json`.

        THE ESTATE CAN LOCK A PUBLISH AND DOES NOT LOCK A RELEASE.
        `apps/oshun/bff/src/studio/studio-scene-publish-store.ts` is the exemplar and it is exactly
        what .b, .d and .e describe: it hashes the exact submitted manifest into `manifestSha256`,
        derives the publish identity AND the release-edition stream FROM that digest, and on every
        rehydration recomputes `sha256(manifestJson)`, refuses a record whose digest does not match
        its bytes, then rebuilds the identity from the canonical fields and refuses a record whose
        `publishId` or `releaseStream` is not the one those fields imply. That is a locked
        candidate. Recorded as CORRECT so a later pass does not re-flag it.

        THE RELEASE-READINESS DECISION, TWO FILES AWAY, BINDS TO A MUTABLE POINTER.
        `AdminReleaseReadinessEntry` declares releaseId, releaseName, targetDate, tier, score,
        summary, gates, goNoGoAt, approverUserId, approverName — no digest, no revision, no
        manifest. `AdminRecordGoNoGoDecisionInputSchema` is {reportId, releaseId, decision,
        approverUserId, approverName, rationale}. So .c has no digest to bind a signoff to, and .f
        cannot mark an approval stale because the approval was never bound to anything that could
        change. 0 of .a's 9 manifest subjects — asset, output, edit, metadata, rights, provenance,
        gate, target profile, dependency — are pinned to a revision or a hash in either release
        contract.

        FIXED: AN APPROVER NAMED IN THE BODY IS NOT THE APPROVER WHO CALLED.
        `recordAdminGoNoGoDecision(userId, input)` receives the authenticated principal as `userId`
        and then wrote `release.approverUserId = input.approverUserId` — the value straight off the
        request — using `userId` only to choose which per-operator record to mutate. The two were
        never compared. So an authenticated operator could record a GO decision attributed to any
        principal they cared to name, and the release would read as decided by someone who never
        called.

        The named approver is KEPT as given, because transcribing a decision taken in a meeting is
        legitimate — `approverName` is a human name, not a principal. What was missing was the
        disclosure. The record now carries `recordedByUserId` (the authenticated caller, never
        caller-supplied) and `approverIsRecorder`, and the appended summary line reads "GO decision
        by <name>, recorded by <caller>" whenever they differ. This is a seam, not a guarantee: it
        makes an existing ambiguity visible rather than pretending to close it, and the field
        documentation says plainly that nothing downstream should read `approverUserId` as an
        identity the platform verified. The route spec (+1) names a principal the caller is not and
        asserts all three.

        NOTHING REHASHES ON THE RELEASE PATH. .e asks for a rehash at release planning, immediately
        before execute, and after target assembly. 5 sources in the estate recompute a digest and
        refuse a mismatch — Bellona's remote-host snapshot verifier ("refusing to restore a
        tampered snapshot"), the scene-publish store, an Isis 3D pipeline, the workbench kit's
        rebuild-identity and Yemaya's resumable upload — and exactly 1 of the 5 is on the release
        path. 0 release sources carry any approval-staleness marker.

        CHECKED AND FOUND CORRECT. (1) The scene-publish lock above. (2) A release whose `goNoGoAt`
        is already set refuses a second decision with `release_already_decided` — .d's immutability
        for the decision itself holds, even though a content change is not routed through a new
        candidate. (3) A `go` is refused unless every gate is passing or waived, and a `no_go` is
        refused unless at least one gate is not passing; the decision cannot contradict its own
        gates.

        TRAP. AN IDENTIFIER CLASS WITHOUT DIGITS CANNOT MATCH `manifestSha256`. The first version of
        the rehash predicate used `[A-Za-z.]*`, which stops at the `2` of `Sha256`, and it reported
        2 rehashers where there are 5 — missing Bellona's snapshot verifier, the estate's clearest
        example of the thing .e asks for. A census that undercounts the estate's own capability is
        as wrong as one that overcounts it.

        3 of 8 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: one publish path locks
        correctly and the release decision refuses to be retaken, but the decision binds to a
        releaseId and nothing else, so there is no manifest to rehash and no binding to invalidate. -->
- [ ] I8.5 Assemble deterministic output bundles per target. Evidence: golden
      bundle round-trip for every publishable asset kind.
  - [ ] I8.5.a Define target bundle manifest for entry assets, derivatives,
        dependencies, metadata, captions/transcripts, provenance, rights/
        attribution, checksums, target profile, and tool versions. Evidence:
        bundle schema.
  - [ ] I8.5.b Resolve only exact authorized release-candidate files/
        dependencies and fail on missing/mutable/ambiguous/quarantined inputs.
        Evidence: input resolution tests.
  - [ ] I8.5.c Transform/package each publishable asset kind with pinned tools,
        configs, locale/time/random/order controls, and reproducible filenames.
        Evidence: per-kind assembly fixtures.
  - [ ] I8.5.d Generate normalized metadata, accessibility files, rights/
        attribution, C2PA/ledger links, target descriptors, and checksums from
        canonical records. Evidence: metadata mapping tests.
  - [ ] I8.5.e Prevent secrets/private fields/path traversal/symlinks/hidden
        files/unsafe archives and enforce file/count/size/resource budgets.
        Evidence: hostile bundle tests.
  - [ ] I8.5.f Canonically order and serialize manifests/archive entries/
        timestamps/permissions/compression so identical inputs produce identical
        hashes where format permits. Evidence: determinism tests.
  - [ ] I8.5.g Parse and validate every golden bundle through the target
        consumer/importer and compare semantic round-trip. Evidence: per-target
        round-trip suite.
        <!-- MEASURED 2026-08-20 (I8.5.a-g). The parent's evidence is a "golden bundle round-trip
        for every publishable asset kind", so the measurement asks the binding question first: what
        does a bundle manifest in this estate declare, and could two runs over identical inputs
        produce identical bytes? Instrument: `generate-oshun-bundle-assembly.mts` (+8 tests), wired
        into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-bundle-assembly.json`.

        FIXED: A PATH THAT ONLY DESCRIBES AN ARCHIVE IS STILL A PATH SOMEBODY WILL EXTRACT. All
        four bundle file-entry schemas — `EngineBundleFileEntrySchema` and
        `DCCBundleFileEntrySchema` in the output registry, `JobEngineBundleFileEntrySchema` and
        `JobDCCBundleFileEntrySchema` in the generation API — declared `path: z.string().min(1)`.
        Those manifests arrive on `RegisterOutputRequestSchema`, a CALLER-SUPPLIED registration
        request, so `../../etc/passwd`, `/etc/passwd`, `..\..\windows`, `.ssh/config`, `a//b` and
        `trailing/` were all well-formed bundle entries, and nothing downstream that unpacks a
        bundle by these paths could tell them from ordinary ones. The two `superRefine` validators
        that already read `manifest.files[].path` check for a README entry and a mesh role — never
        for a traversal.

        The rule now lives once, in `libs/shared/security/src/bundle-entry-path.ts`, and all four
        schemas read it. A bundle entry path must be a relative POSIX path of ordinary segments: no
        absolute, drive or UNC prefix; no backslash (a `\` is a legal POSIX filename character, so
        an archive writer and reader can disagree about whether `a\b` is one segment or two); no
        `.`, `..`, empty or leading-dot segment; no control characters; no trailing separator; and
        bounded at 1024 characters overall and 255 per segment. Every applicable refusal is
        reported, not just the first, so a caller fixing a manifest sees the whole problem at once.
        ONE RULE IN ONE PLACE WAS THE POINT: putting a copy in each app would have recreated the
        §I7.20 defect — two lists for one judgement — so `@oshun/security` was added to the output
        registry's dependencies rather than duplicating the predicate. `bundle-entry-path.spec.ts`
        (+8) pins every refusal, and a schema-level test in each app (+1 each) drives six hostile
        paths through the real request schema and asserts the ordinary path still parses.

        A GENERATION TIMESTAMP IN THE MANIFEST DEFEATS THE DETERMINISM THE CELL ASKS FOR. All 4
        manifests carry `generatedAt: z.string().datetime()`, so an identical bundle assembled
        twice differs in exactly the field that records when it was assembled. TRAP. A
        CALLER-OVERRIDABLE CLOCK IS NOT A PINNED ONE: both assemblers
        (`engine-export-bundles.ts`, `dcc-export-bundles.ts`) accept `options.generatedAt` — which
        is how their own fixtures stay stable — and both fall back to `new Date().toISOString()`,
        so a production bundle is not reproducible even though a test bundle is. Neither sorts its
        `files`, so entry order is whatever the caller passed; 0 of the 23 export-bundle sources
        carry any canonical ordering or stable serialisation.

        AN ENTRY CAN SAY WHERE A FILE SITS AND NOT WHAT ITS BYTES HASH TO. 0 of the 4 file-entry
        schemas declare a checksum; the entry carries `path`, an optional `sourceUrl`, a `role`, an
        optional `mimeType` and an optional `sizeBytes`. `checksums` is also one of the 6 of .a's 10
        manifest subjects that is absent — with derivatives, dependencies, captions/transcripts,
        rights/attribution and tool versions. The 4 present are entry assets, metadata, provenance
        (as `sourceWorkflowId`) and the target profile (as `engine`/`dcc`).

        3 of .e's 7 hostile-bundle subjects still have no guard anywhere: symlinks, unsafe archives
        (no decompression-ratio or entry-depth bound) and secrets or private fields. Path
        traversal, absolute paths, hidden files and size budgets are guarded by the validator added
        here.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The engine
        bundle schema refuses a manifest whose `engine` disagrees with the bundle's, whose
        `sourceMesh.format` disagrees with the declared format, or whose `entryCount` disagrees with
        `files.length` — real cross-field validation, not shape checking. (2) The engine export
        bundle requires a `README.md` entry and at least one entry with the `mesh` role.

        TRAP. A WORD IN A DIFFERENT BUNDLE IS NOT THIS BUNDLE. Searching `apps/` and `libs/` for
        "bundle" plus a round trip reports 21 suites — investigation bundles, data-export bundles,
        offline bundles — and searching for a stable serialiser finds Isis's PROVENANCE bundle. The
        honest population is the 23 sources that name an engine or DCC export bundle, and 0 of them
        round-trips a golden bundle through a target importer, which is the parent's whole evidence.

        1 of 7 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: entry paths are now
        safe, but a bundle carries no checksums, its manifest embeds the moment it was built, its
        entries are unordered, and nothing parses one back through the consumer it was built for. -->
- [ ] I8.6 Verify outputs after write in output registry, gallery, marketplace,
      or downstream store. Evidence: read-back mismatch fails release.
  - [ ] I8.6.a Define per-destination write receipt and verification contract
        for canonical object/version, bytes/hash/length/content type, manifest,
        metadata, visibility, URL, status, and timestamp. Evidence: receipt
        schemas.
  - [ ] I8.6.b Write idempotently using destination-native conditional/versioned
        operations and retain immutable request/response correlation. Evidence:
        duplicate-write tests.
  - [ ] I8.6.c Read back from the authoritative destination rather than local
        request/cache and verify bytes/hash/length/format/manifest/dependencies.
        Evidence: read-back integrity tests.
  - [ ] I8.6.d Verify destination metadata, permissions/audience, publication/
        listing state, target URL/download behavior, cache/CDN state, and
        provenance credential. Evidence: visibility tests.
  - [ ] I8.6.e Treat timeout/partial/unknown/mismatch as unverified and block
        release success; retry or compensate idempotently. Evidence: failure
        injection tests.
  - [ ] I8.6.f Persist signed/hashed verification evidence and recheck after
        downstream transformations or configured propagation delay. Evidence:
        delayed verification tests.
        <!-- MEASURED 2026-08-20 (I8.6.a-f). The parent's evidence is that a "read-back mismatch
        fails release", so the measurement asks the binding question first: does anything fetch the
        bytes back from the authoritative destination and compare them? Instrument:
        `generate-oshun-write-verification.mts` (+8 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-write-verification.json`.

        IT DOES, AND THAT PART IS RIGHT — recorded as CORRECT rather than as a gap. On registration,
        `output.service.ts` builds the storage object URL, fetches it over HTTP, stream-hashes the
        body with a reader rather than buffering the whole file, and throws `HashMismatchError`
        when the computed digest disagrees with the one the caller declared. A wrong digest has
        always blocked registration, and `verifyIntegrity` is routed as a later recheck that fetches
        the same way. That is .c, implemented properly.

        FIXED: A VERDICT FROM AN INSTRUMENT THAT COULD NOT LOOK IS NOT A VERDICT. The read-back had
        three exits and only one of them was a measurement. A genuine mismatch threw. But an
        algorithm this runtime cannot compute — `blake3` falls back — logged "skipping server-side
        hash verification" and RETURNED; and unreachable storage logged "could not verify upload
        hash from storage; proceeding with client hash" and returned too. Both left an output
        registered with a digest nobody had checked, byte-identical in the database to one whose
        bytes had been fetched and hashed. .e asks for exactly timeout/partial/unknown to be treated
        as unverified and to block release, and nothing can block on a fact nobody wrote down.

        The read-back now returns a typed verdict — `verified`, `unsupported_algorithm` or
        `storage_unreachable`, carrying the algorithm, the expected digest, the computed digest (or
        null) and the reason — and the registration records it on the output under
        `metadata.hashVerification`. A mismatch still throws. Driven end to end in
        `output.service.hash-verification.spec.ts` (+6): matching bytes → `verified` with the
        computed digest; different bytes → `HashMismatchError`; a refused connection → 
        `storage_unreachable` with the reason and a null digest; a 404 → the same with the status in
        the reason; `blake3` → `unsupported_algorithm`; and a property that `verified` is never
        returned without a computed digest.

        RECORDING IS NOT BLOCKING, AND THE CELL ASKS FOR BOTH. Registration still proceeds after an
        unreachable destination — that was a deliberate availability choice by the original author,
        stated in its own comment — so .e is only half closed. What changed is that a release gate
        now has a fact to refuse on instead of a warning in a log.

        ONE READ-BACK FOR FOUR DESTINATIONS. .a asks for a per-destination write receipt across the
        output registry, gallery, marketplace and downstream stores. Exactly 1 source in Isis and
        Oshun fetches from a destination and refuses a digest mismatch, and it is the output
        registry. The other three destinations verify nothing after writing.

        WHAT THE RECEIPT RECORDS: 4 of .a's 10 subjects — the canonical object, the bytes hash, the
        status and the timestamp. It does not record the content length, the content type, the
        manifest, the destination metadata, the visibility or the URL. 0 of .d's 6 destination
        checks exist at all: nothing verifies destination metadata, permissions or audience,
        publication or listing state, target-URL download behaviour, cache or CDN state, or a
        provenance credential. Nothing beyond the bytes is checked.

        A RESULT YOU RETURN IS NOT EVIDENCE YOU KEPT. `verifyIntegrity` is routed and real, and its
        `IntegrityVerificationResult` is returned to the caller and never written down — there is no
        INSERT or UPDATE anywhere in its body. So .f's "persist signed/hashed verification evidence"
        holds only for the registration verdict added here, and there is no scheduled recheck after
        a propagation delay. .b's destination-native conditional write is absent too: nothing on the
        path uses an `If-None-Match`, `If-Match` or versioned-write operation, so a duplicate write
        is not refused by the destination.

        TRAP. A FIELD SOMEWHERE IN A THREE-THOUSAND-LINE SERVICE IS NOT A FIELD ON THE RECEIPT.
        Matching the receipt subjects against the whole of `output.service.ts` reported 9 of 10 —
        `manifest`, `metadata`, `visibility` and `url` all appear in the file, none of them on a
        verification result. Scoped to the verification contract and the two methods that produce
        one, it is 4. TRAP. A REGEX THAT SEARCHES PAST A FUNCTION FINDS THE NEXT FUNCTION'S CODE:
        looking for an INSERT "within 4000 characters of verifyIntegrity" reported the result as
        persisted; brace-matching the method body reports the truth.

        3 of 9 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: the read-back is real
        and a mismatch blocks, an unverifiable write is now recorded as unverified, and everything
        else — per-destination receipts, idempotent conditional writes, destination state, persisted
        evidence and delayed recheck — is absent. -->
- [ ] I8.7 Implement collection and campaign curation through versioned bundles.
      Evidence: no direct catalog mutation.
  - [ ] I8.7.a Define collection/campaign bundle identity/version, purpose,
        audience, owner, schedule, ordered members/exact revisions, metadata,
        targets, constraints, and lifecycle. Evidence: curation schema.
  - [ ] I8.7.b Add/remove/reorder/group/feature members only through a new draft
        bundle revision with reason and optimistic concurrency. Evidence:
        curation edit tests.
  - [ ] I8.7.c Validate each member’s availability, target compatibility,
        rights/consent, gates, provenance, editorial approval, and campaign
        steering constraints. Evidence: membership tests.
  - [ ] I8.7.d Preview deterministic catalog/gallery/channel diffs, assets/
        metadata/order/schedule/dependencies, and removals before approval.
        Evidence: publication preview tests.
  - [ ] I8.7.e Review/approve/release the exact bundle manifest through governed
        release APIs; prohibit direct catalog/gallery mutation. Evidence:
        boundary tests.
  - [ ] I8.7.f Preserve released bundle history and propagate member quarantine/
        recall/expiry through explicit successor/incident state. Evidence:
        lifecycle tests.
        <!-- MEASURED 2026-08-20 (I8.7.a-f). The parent's evidence is "no direct catalog mutation",
        so the measurement asks the binding question first: can the catalog be mutated without
        going through a curation bundle? Instrument: `generate-oshun-curation-bundles.mts`
        (+8 tests), wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-curation-bundles.json`.

        IT CAN, AND THAT IS THE ONLY WAY IT IS MUTATED. `POST
        /v1/admin/isis/output-gallery/bulk-actions` takes a list of output ids and one of four
        action kinds — `re-tag`, `send-to-editorial`, `revoke-consent`, `takedown` — and applies it
        to the gallery. The route names no bundle, no collection and no campaign anywhere; there is
        no manifest to approve, no draft revision, and no optimistic-concurrency check. That route
        IS the catalog mutation .e says to prohibit, and it is the only catalog-editing surface an
        operator has.

        THE CURATION MODEL EXISTS AND NOTHING REACHES IT. `libs/contracts/src/common/collection.ts`
        is 1030 lines: a 24-field collection with sections, items, collaborators, smart
        configuration, and real cross-field validation that checks domain membership, collaborator
        and section coherence, smart-config consistency, and that `itemCount` equals the number of
        items. 0 product files import a `Collection` binding from `@oshun/contracts`, and 0 admin
        routes name a collection. `CollectionManagementProvider` in `@isis/ai-providers` — the other
        1814-line collection implementation, measured under §I7.20 — has 0 importers too. Two
        curation models, neither reachable, while the catalog is edited by a route that knows about
        neither.

        A COLLECTION CANNOT SAY WHICH REVISION IT IS. 6 of .a's 11 bundle subjects are expressible:
        identity, purpose, audience, owner, metadata and lifecycle. There is no version or revision,
        no schedule, no ordered members at exact revisions, no targets and no constraints. .b asks
        that a member edit happen only through a NEW draft bundle revision under optimistic
        concurrency — with no revision field there is nothing to draft and nothing to compare, so
        .b is unaskable rather than unimplemented, and .f's released-bundle history has nothing to
        keep.

        NOTHING VALIDATES A MEMBER WHERE MEMBERS ARE ACTUALLY EDITED. .c asks each member to be
        checked for availability, target compatibility, rights and consent, gates, provenance,
        editorial approval and campaign steering. 1 of those 7 appears on the gallery route, and
        only as the `revoke-consent` ACTION — an operator withdrawing consent, not a check that a
        member holds it. The other six appear nowhere on the editing path.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        collection contract validates its own shape thoroughly, including the `itemCount`-versus-
        `items.length` agreement that a hand-maintained count would otherwise drift on. (2) The
        gallery bulk action requires an authenticated caller with an Isis workspace scope, and its
        `takedown` kind requires a reason.

        TRAP. `etag` IS A SUBSTRING OF `removeTags`. Testing the gallery route for an
        optimistic-concurrency check with `/expectedVersion|ifMatch|etag|revision/i` matched inside
        the identifier `removeTags` and reported a concurrency guard the route does not have; word
        boundaries give the truth. TRAP. A FIELD BAG IS NOT THE WHOLE SCHEMA: `CollectionSchema`
        spreads `CollectionFields` and then adds seven more fields of its own, so reading only the
        bag reported 17 fields and called `owner` absent when it is present — the census reads both.
        TRAP. A NAME IS NOT AN IMPORT: `CollectionSchema` is declared independently in Athena,
        Hestia, Cybele and Veritas, and a bare identifier search reports six consumers this contract
        has not got; counting import bindings whose specifier is `@oshun/contracts` gives zero.

        0 of 6 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: the catalog is edited
        directly by a route with no bundle, no revision and no membership validation, and the two
        curation contracts that could have carried one are unreachable from any surface. -->
- [ ] I8.8 Implement supersession, archive, unpublish, quarantine, and recall.
      Evidence: downstream state and lineage remain coherent.
  - [ ] I8.8.a Define separate lifecycle command/state/reason/authority/
        effective-time/target/notification/verification contracts for supersede,
        archive, unpublish, quarantine, and recall. Evidence: lifecycle schemas.
  - [ ] I8.8.b Supersede by linking an eligible replacement revision/bundle,
        migration/effective behavior, prior availability, and consumer notice
        without rewriting the predecessor. Evidence: supersession tests.
  - [ ] I8.8.c Archive inactive authoring/catalog records from routine discovery
        while retaining authorized history, links, and restore rules. Evidence:
        archive tests.
  - [ ] I8.8.d Unpublish distribution/listing visibility by explicit targets
        while preserving source asset/release evidence and recording residual
        copies. Evidence: unpublish tests.
  - [ ] I8.8.e Quarantine suspected versions from reuse/review/release/delivery
        with incident scope, restricted evidence, and fail-closed dependency
        propagation. Evidence: quarantine tests.
  - [ ] I8.8.f Recall confirmed affected versions through complete blast radius,
        downstream disable/removal, notification, receipt, and verification.
        Evidence: recall tests.
  - [ ] I8.8.g Project coherent current/historical state across lineage, search,
        gallery, marketplace, collections, campaigns, URLs, caches, and exports.
        Evidence: cross-system lifecycle tests.
        <!-- MEASURED 2026-08-20 (I8.8.a-g). The parent's evidence is that "downstream state and
        lineage remain coherent", so the measurement asks the binding question first: which of the
        five withdrawal verbs exists as a state a record can be in, and does any of them reach
        anything downstream? Instrument: `generate-oshun-lifecycle-withdrawal.mts` (+7 tests), wired
        into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-lifecycle-withdrawal.json`.

        AN ARCHIVE THAT ONLY MOVES THE BYTES IS NOT AN ARCHIVE THAT REMOVES THE RECORD FROM
        DISCOVERY. `OutputStatus` is pending | available | archived | expired | deleted, and 1 of
        .a's 5 verbs — archive — has a state at all. That one is annotated "Moved to cold storage",
        which is a storage-tier fact `StorageTier` already carries separately as
        hot | warm | cold | glacier. The three list and search condition sets in the output service
        each exclude exactly one status, `deleted`, so an ARCHIVED output is still returned by
        routine discovery. .c asks archive to take a record out of routine discovery while retaining
        authorized history; the status names a place the bytes went, and nothing anywhere changes
        what a caller browsing the catalog sees.

        SUPERSEDE, UNPUBLISH, QUARANTINE AND RECALL HAVE NO STATE. A record cannot be in any of
        them, so .a's per-verb contracts — command, state, reason, authority, effective time,
        target, notification, verification — have nothing to attach to for four of the five verbs.
        .b's supersession is the sharpest case: nothing on the output service carries a
        `supersededBy`, `successorId` or `replacedBy`, so a replacement cannot be linked and a
        predecessor cannot be left intact-but-superseded, which is exactly what .b asks for.

        THERE IS A REAL TAKEDOWN WORKFLOW, AND IT IS NOT THE CATALOG'S. `/v1/admin/takedowns`,
        `/v1/admin/takedowns/:id/process` and `/v1/admin/takedowns/:id/counter-notice` are a proper
        notice-and-counter-notice surface, and `/v1/living-scenes/admin/artifacts/:id/takedown`
        withdraws a living-scene artifact. None of them sets a state on an output, and the output
        service does not read them. So .f's recall exists as a process for a different subject.

        NOTHING PROPAGATES. No quarantine propagation, no recall cascade, no successor link. 11
        sources across the estate name a blast radius — incident contracts, reliability engineering,
        the Tara workbench — and 0 of them are in the output registry. A CASCADE THAT STOPS AT THE
        RECORD IT WAS CALLED ON IS NOT PROPAGATION, and here there is no cascade to stop.

        4 of .g's 9 projection surfaces are even nameable from the output service: lineage, search,
        URLs and exports. Gallery, marketplace, collections, campaigns and caches appear nowhere in
        it, so a withdrawal could not be projected onto them coherently even if one could be issued.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) A delete is
        a SOFT delete — the row is marked `deleted` rather than removed, and every list, search and
        lookup query excludes it, so history survives a deletion. (2) The provenance backfill scan
        deliberately includes archived and expired outputs, which is right: historical records need
        provenance too, and a sweep is not a discovery query.

        TRAP. A VERB THAT MEANS SOMETHING ELSE IN ANOTHER DOMAIN IS NOT THIS VERB.
        `/v1/admin/hathor/npc-memory-system/recall` is an NPC retrieving a memory, and counting it
        reported a recall command the catalog has not got. TRAP. A SWEEP IS NOT A DISCOVERY QUERY:
        the expiry sweep also excludes `expired`, and aggregating status filters across the whole
        service reported discovery hiding expired records when the list paths do not. Reading the
        list/search condition arrays specifically gives 3 sites excluding 1 status, out of 7 status
        filters in the file.

        1 of 7 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: one verb has a state
        and it means something else, four have no state at all, nothing links a successor, and
        nothing propagates to any downstream surface. -->
- [ ] I8.9 Implement scheduled drops with generation and review capacity
      reservation. Evidence: overbook and missed-dependency tests.
  - [ ] I8.9.a Define scheduled-drop identity/version, timezone/locale, release
        window, targets, campaign bundle, required generation/gates/reviews,
        deadlines, dependencies, fallback, owner, and status. Evidence: drop
        schema.
  - [ ] I8.9.b Build a dependency DAG and backward schedule for generation,
        evaluation, review, correction, packaging, distribution, verification,
        and notification with buffers. Evidence: schedule fixtures.
  - [ ] I8.9.c Forecast and reserve provider/queue/GPU/storage/budget plus named
        reviewer/approver capacity for the required windows. Evidence: capacity
        reservation tests.
  - [ ] I8.9.d Reject overlapping/overbooked reservations and detect holidays/
        working hours/region/target embargo conflicts with exact shortfalls.
        Evidence: overbook tests.
  - [ ] I8.9.e Monitor dependency completion/freshness/slippage/capacity and
        recalculate risk without silently moving the ratified release window.
        Evidence: missed-dependency tests.
  - [ ] I8.9.f Execute hold/delay/reduce-scope/cancel/fallback only through
        authorized impact preview and immutable schedule revision/decision.
        Evidence: contingency tests.
  - [ ] I8.9.g Trigger release only when current candidate/approvals/gates/
        rights/capacity remain valid and verify all targets. Evidence: scheduled
        release journey.
        <!-- MEASURED 2026-08-20 (I8.9.a-g). The parent's evidence clause names two tests —
        "overbook and missed-dependency" — so the measurement asks those two questions first, of the
        surfaces that actually exist. Instrument: `generate-oshun-drop-scheduling.mts` (+10 tests),
        wired into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-drop-scheduling.json`.

        A LONGEST PATH IS COMPUTED FROM DURATIONS, NOT DECLARED. The estate's one reachable
        dependency scheduler is `POST /v1/admin/yemaya/scheduling/schedule` over
        `@yemaya/budget-management`'s TimelineManager, and its `findCriticalPath` returned the tasks
        a caller had flagged `critical` — and, when none were flagged, every task carrying any
        dependency. It read no duration at all: given a(1) -> b(1) -> c(1) beside a lone d(100), it
        answered `["b","c"]`, which omits the head of the chain it names and drops the task 33x
        longer than that chain. Given a 1000-unit `render` and a 1-unit `trivial` flagged critical,
        it answered `["trivial"]`. The studio workspace joins that list with arrows and prints it as
        `critical path: b → c`, so A SET RENDERED WITH ARROWS READ AS A PATH. FIXED: the longest-
        duration chain through the graph, with `findCriticalPathDuration` beside it so the number is
        checkable, and the flag is deliberately not consulted. The route test that pinned the old
        answer — `expect(v.criticalPath).toEqual(['A','B']); // the flagged-critical tasks`, and a
        whole case named "falls back to dependency-carrying tasks when none are flagged critical" —
        WAS GRADING THE ECHO OF ITS OWN INPUT, and is replaced.

        FIXED, SAME FILE, TWO MORE. (1) `generateScheduleFromTasks` marked a task visited only AFTER
        recursing into its dependencies, so `x -> y -> x` recursed until the stack ended: a caller
        supplying two mutually-dependent task ids to an admin route got `RangeError: Maximum call
        stack size exceeded`, driven. Now Kahn ordering that raises `ScheduleDependencyCycleError`
        naming the tasks that could not be ordered; the route answers `400 dependency_cycle`.
        (2) `propagateDelay` recursed once per path, so A DIAMOND REACHED ITS JOIN TWICE: for
        a -> {b,c} -> d a 500ms delay returned `["b","d","c","d"]` and left d moved by 1000, driven
        before and after. Now breadth-first over a shifted set — `["b","c","d"]`, d at 500 — and it
        terminates on a cycle. (3) A dependency naming a task the request did not supply ordered
        nothing and was dropped in silence; it is now reported as `unresolvedDependencies` and named
        by the studio workspace, and a duplicate task id is a 400 rather than a quietly smaller
        schedule. THAT IS THE PARENT'S "missed-dependency" TEST, and it is the 1 of 13 properties
        this cell can record as present.

        A QUOTA IS NOT A RESERVATION AND A PRIVATE CALENDAR CANNOT COLLIDE. The nearest thing to a
        scheduled drop is `AdminEditorialReleaseStream` (embargo | hotfix | emergency_correction;
        pending | blocked | in_progress | released | cancelled), and its store keeps ONE BUCKET PER
        OPERATOR: all 5 read paths are `buckets.get(userId)`, and the only bucket-wide expression is
        `exportState(): EditorialReleaseStreamsPersistedState`, which no query calls. Two operators
        can hold streams over the same subject in the same window and never meet, so .d's
        overlapping-reservation refusal has nothing to refuse with. The one limit enforced is
        `ADMIN_EDITORIAL_RELEASE_STREAM_QUOTA_PER_USER` — a per-user count, not a capacity.

        5 of .a's 14 subjects are expressible over the record's 18 fields: identity, required
        reviews (a nullable `reviewPackageRef`), deadlines, owner, status. Absent: version,
        timezone/locale, release WINDOW (only single instants, `embargoLiftsAt` and
        `targetReleaseAt`), targets, campaign bundle, required generation, required gates,
        dependencies, fallback. 0 of .b's 8 stages — generation, evaluation, review, correction,
        packaging, distribution, verification, notification — is a value the scheduler holds; its
        tasks are opaque caller-supplied ids. No backward pass and no buffer quantity, so .b's
        backward schedule from a ratified window does not exist.

        1 of .c's 6 resources has a reservation the Oshun product can reach: budget, through
        `GenerationBudgetGate`. Provider, queue, storage and named reviewer/approver capacity have
        none. GPU reservations DO exist — `reserveGPU`, `reserveModel` — in two Isis libraries no
        Oshun source imports, so A RESERVATION IN A LIBRARY NOTHING IMPORTS HOLDS NOTHING. And 0 of
        12 `reserve*` sites can hold a FUTURE window: the budget gate's window key is
        `day:YYYY-M-D` derived from its own clock, so a drop cannot be promised capacity it will
        need on Friday. 0 of .d's 5 conflict classes is checked anywhere on the drop path.

        THE SLIPPAGE MACHINERY EXISTS AND THE CONSOLE CALLS NONE OF IT. TimelineManager declares 24
        methods and the Oshun store calls 6 — addTask, generateScheduleFromTasks, findCriticalPath,
        findCriticalPathDuration, findUnresolvedDependencies, getTaskCount. `assessScheduleRisk`,
        `recordDelay`, `propagateDelay`, `generateRecoveryPlan`, `compressSchedule`,
        `levelResources`, `negotiateDeadline`, `optimizePhaseOverlaps` and the whole milestone
        vocabulary are unreached by any route, so .e's monitoring is not absent from the estate — it
        is absent from the product. And where it does run, A SCHEDULE EDITED IN PLACE HAS NO
        BASELINE TO SLIP AGAINST: `recordDelay` writes `endDate: task.endDate + delayMs` onto the
        task and `propagateDelay` shifts every dependent, with no copy of the ratified dates kept
        beside the working ones. .e asks for risk recalculated WITHOUT silently moving the ratified
        window; the only way this library records a slip is by moving it.

        1 of .f's 5 contingency verbs is expressible: cancel. Hold, delay, reduce-scope and fallback
        appear nowhere on the record, `revision` is an in-place counter rather than a history, and
        there is no impact preview. A REMINDER IS NOT A RELEASE: the 4 surfaces carrying a publish
        moment (`content-drop-*`, `admin-reminders-produce`) send notifications and release nothing,
        so .g has no trigger to revalidate against and 0 of its 5 preconditions is asked.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        generation budget gate is a real reservation: `reserveSpendUsd` is atomic, `settle`
        reconciles the estimate against the provider's captured cost, and an unreachable store fails
        closed when configured to — its two refusals are `generation_daily_budget_exceeded` and
        `generation_budget_store_unavailable`. (2) The release-stream store enforces its quota,
        requires `embargoLiftsAt` at creation for embargo streams, refuses a blocked status with no
        blocker, and validates every status transition. (3) No project sharing
        `configs/vitest/node-test-coverage.ts` holds a `.spec.ts` file today, so its `.test.ts`-only
        include — a preserved contract for 35 projects — is skipping nothing; this cell added a
        local vitest config for the one project it needed rather than widening the preset.

        TRAPS, all four found by disagreeing with a first reading. (1) `= z` AND `.object({` SIT ON
        DIFFERENT LINES, so `indexOf('z.object({')` skipped this schema and anchored on the NEXT
        one: the census read the create-request envelope's 12 fields and reported them as the
        record's 18. (2) A WORD INSIDE A SENTENCE IS NOT A THING THE CODE HAS — a substring search
        reported `review` as a named stage (its one occurrence is the template `${n} subsequent
        milestones need review`) and a buffer in the schedule (its one occurrence is inside the
        justification string 'Extended buffer provides safety margin'). Whole string literals for
        values, identifiers-only for quantities. (3) AN `overlap` IN A TEXTURE EXECUTOR IS NOT A
        RESERVATION OVERLAP: estate-wide the pattern matches 57 files and 0 of them are on a
        schedule. (4) A PARAMETER NAMED `at` IS THE MOMENT THE RESERVATION IS MADE, NOT A WINDOW IT
        HOLDS — matching `at:` reported a forward reservation in a workbench cost fixture.

        1 of 13 properties present. ALL SEVEN CELLS AND THE PARENT LEFT `[ ]`: the drop record
        cannot say when its window opens or closes, the scheduler names none of the stages it would
        order, five of six resources cannot be reserved at all and none can be reserved ahead, no
        conflict class is checked, and nothing triggers a release. -->
- [ ] I8.10 Implement rights/consent expiry monitoring for released assets.
      Evidence: warnings and automatic policy actions fire at known times.
  - [ ] I8.10.a Index exact rights/consent grant versions, effective/expiry/
        review dates, territory/use/target scope, revocation, and dependent
        released assets/bundles/targets. Evidence: expiry index tests.
  - [ ] I8.10.b Calculate next action using timezone-safe instants and versioned
        warning/action windows, including missing/indefinite/unknown dates.
        Evidence: fake-clock boundary tests.
  - [ ] I8.10.c Run fenced idempotent monitoring with missed-run catch-up,
        duplicate prevention, clock skew, pagination, and production-scale
        throughput. Evidence: scheduler tests.
  - [ ] I8.10.d Notify configured owners/reviewers/legal/subjects before expiry
        with safe asset/scope/action/deadline details and escalation. Evidence:
        warning tests.
  - [ ] I8.10.e At action time revalidate grant/dependencies and automatically
        hold/unpublish/quarantine/recall only as canonical policy authorizes.
        Evidence: timed action tests.
  - [ ] I8.10.f Cancel/reschedule obsolete timers on renewal/replacement/
        revocation/supersession and retain immutable event/action history.
        Evidence: lifecycle tests.
        <!-- MEASURED 2026-08-20 (I8.10.a-f). The parent's evidence is that "warnings and automatic
        policy actions fire at known times", so the binding question is asked first: is there any
        clock-driven, subject-independent reader of a rights or consent expiry at all? Instrument:
        `generate-oshun-rights-expiry.mts` (+10 tests), wired into
        `verify:inventory:v1-workbenches`, writing `evidence/v1-workbenches/oshun-rights-expiry.json`.

        AN EXPIRY THAT ONLY HAPPENS WHEN THE SUBJECT LOOKS IS NOT A MONITOR. The transition itself is
        real and complete: `applyExpirations` in `apps/oshun/bff/src/consent/state.ts` marks a
        `granted` record `expired` once `expiresAt` has passed, raises `renewalRequired`, stamps
        `renewalRequestedAt`, and appends a typed `policy_expiration` history event with
        `actorType: 'system'`. What it is not is a monitor. All 7 of its call sites are request
        handlers — getSnapshot, getFlowState, grant, withdraw, revoke, renew, getAudit — and every
        one takes a caller-supplied `userId` and reads only `this.users.get(userId)`. A SWEEP THAT
        TAKES A SUBJECT ID IS NOT A SWEEP. A consent that lapsed six months ago is still `granted` in
        the store until that subject next touches a consent endpoint, and nothing anywhere fires at
        the moment `expiresAt` names. 0 timers, 0 cron entries, 0 interval sweeps on this path.

        A FENCED SWEEP EXISTS IN A SERVICE THAT HOLDS NO RELEASED ASSETS. `apps/yemaya/svc-study-
        workspace` has exactly what .c describes — `STUDY_EXPIRY_SWEEP_INTERVAL_MS` bounded to a
        60s floor with 0 disabling the timer, plus `POST /api/study/expiry-sweep` for a manual or
        catch-up run. It sweeps study-report retention, not rights or consent, and it is one of 2
        interval-sweep services in the estate, neither under `apps/oshun`. On the Oshun consent path
        0 of .c's 5 properties — fenced, idempotent, missed-run catch-up, duplicate prevention,
        pagination — is present, because there is no run to make idempotent.

        A GRANT CANNOT NAME WHAT WAS RELEASED UNDER IT. 7 of .a's 10 index subjects are expressible,
        and the record is better than expected: `record.version`, `record.grantedAt`,
        `record.expiresAt`, `record.target`, `record.revokedAt`, and — on the permission grant
        itself — `permission.allowedRegions` for territory and `permission.useScopes` for use. What
        is missing is the half that makes this cell about RELEASED ASSETS: no review date, no
        dependent released assets, no dependent bundles or targets. Nothing links a grant to the
        outputs published under it, so .e's "revalidate grant AND DEPENDENCIES" has nothing to walk
        and .a's dependency index does not exist.

        THE PROJECTOR THE ESTATE DOES HAVE IS CALLED BY NOTHING. `libs/contracts/src/common/
        entitlement-inspection.ts` contains a genuine warning-window calculator — `projectGrantState`
        with a configurable `expiringSoonHours` over `DEFAULT_EXPIRING_SOON_HOURS` — and 0 files
        outside its own module call it. The consent path does not reach it, and there is no warning
        window of any kind on the consent state, so .b's versioned warning/action windows have no
        implementation to version.

        0 of .d's warnings fire. 31 messaging producers live under `apps/oshun/bff/src/reminders` and
        `libs/oshun/messaging-channels`, and 0 of them names a rights, consent, licence or grant
        expiry. No owner, reviewer, legal or subject is addressed, and there is no escalation.

        0 of .e's 4 automatic actions exists to be taken. `OutputStatus` is
        pending | available | archived | expired | deleted, so hold, unpublish, quarantine and recall
        are not states an output can be in — §I8.8's finding, re-measured here so this cell stands on
        its own evidence — and the consent path invokes none of them. .f's obsolete timers cannot be
        cancelled because none was ever scheduled; 3 of its 4 invalidating events ARE recorded on the
        record (`renewalRequired`, `revocations`, `supersededById`; replacement is not spelled).

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The expiry
        transition is honest end to end: status, renewal flag, renewal timestamp and a typed history
        event with actor, prior status and new status. (2) `computeExpiry` returns null when the
        taxonomy sets `defaultRenewalDays` to null, so an indefinite consent gets no invented expiry
        — the taxonomy's cadences are 30, 180, 365, 730 and null. (3) It is epoch-millisecond
        arithmetic on an ISO instant with no local-calendar walk, so the boundary does not shift
        across a DST transition. That is .b's timezone-safe instants and its missing/indefinite
        dates, and it is 1 of the 2 properties this cell can record present. (4) The consent history
        is append-only through a private `appendHistory` over an 11-member typed vocabulary
        including `expired`, `renewal_requested`, `renewed` and `superseded` — .f's immutable event
        history, and the other present property.

        TRAPS, all four found by disagreeing with a first reading. (1) AN EMPTY PARSE AND A MEASURED
        ABSENCE LOOK IDENTICAL: `OutputStatusSchema` and `ConsentHistoryEventTypeSchema` are
        `z.enum([...])`, not `as const` arrays, so reading them the wrong way reported an 11-event
        history as no history and made "0 of 4 policy actions" an unmeasured zero that happened to
        match the truth. Both parses now throw on an empty result. (2) A SCHEMA NAME GUESSED IS A
        POPULATION OF ZERO — `ConsentPermissionSchema` and `ConsentCollectionSchema` do not exist
        (they are `…PermissionGrantSchema` and `…CollectionContextSchema`), so the grant's own
        territory and use scope read as absent from a contract that carries both. (3) WHERE A
        CONSENT WAS COLLECTED IS NOT THE TERRITORY IT COVERS: an identifier sweep over the whole
        contract answered territory scope with `collection.geoCountry`, which records where the
        subject was standing. Fields are now read off named schemas and carry the object they sit
        on. (4) AN EMAIL SUBJECT IS NOT A DATA SUBJECT — a proximity match over the messaging
        surface reported that subjects are warned; the hits are a webPush VAPID `subject` and a
        per-locale reminder `subject` line for something else.

        2 of 10 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: the expiry moment is
        known and nothing is watching it, no warning window is computed on this path, no notification
        names an expiry, no policy action exists to take, and the grant cannot say which released
        asset depends on it. -->
- [ ] I8.11 Implement release notification and incident routing. Evidence:
      protected metadata is redacted.
  - [ ] I8.11.a Define versioned event-to-recipient/channel/template/escalation
        rules for planned/succeeded/partial/failed/verified/recalled releases
        and incidents. Evidence: routing schema.
  - [ ] I8.11.b Resolve recipients by tenant/project/target/role/on-call/
        subscription/purpose and reauthorize protected detail at delivery time.
        Evidence: recipient tests.
  - [ ] I8.11.c Render localized accessible templates from allowlisted audience-
        safe fields with explicit status, asset/release ref, target, time,
        action, owner, and deep link. Evidence: template tests.
  - [ ] I8.11.d Redact biometric/child/source/provider/credential/private
        prompt/ rights evidence and cross-tenant metadata from
        subject/body/attachments/ URLs. Evidence: notification leak suite.
  - [ ] I8.11.e Deliver idempotently over in-app/email/webhook/paging channels
        with retry/backoff/rate limits/dedup/threading/preferences and receipts.
        Evidence: delivery integration tests.
  - [ ] I8.11.f Escalate unacknowledged/high-severity/failed routes by policy
        and preserve delivery/acknowledgement/audit without treating
        notification as remediation. Evidence: escalation tests.
        <!-- MEASURED 2026-08-26 (I8.11.a-f). The parent's evidence is one clause — "protected
        metadata is redacted" — so the binding question is asked of the surface that would have to
        redact: what does the router actually carry? Instrument:
        `generate-oshun-release-notification.mts` (+10 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-release-notification.json`.

        A ROUTER THAT CARRIES NO CONTENT CANNOT LEAK IT, AND CANNOT DELIVER IT EITHER.
        `POST /v1/admin/studio/notification-routing/route` takes exactly three fields per
        notification — `notificationId`, `priority`, `channel` — plus `{ withinQuietHours }` and
        `{ quietHoursMinPriority, digestLowPriority }`, and answers
        `deliver_now | digest | suppressed`. There is no event kind, no recipient, no template, no
        locale and no body anywhere in it. Nothing protected passes through, so the parent's clause
        is UNFALSIFIABLE on the one surface that routes, and 1 of .a's 4 rule targets — channel —
        is bound, and that one is supplied by the caller rather than resolved by a rule.

        0 OF THE 7 EVENTS THE RULES MUST ROUTE IS A VALUE ANYTHING HOLDS. Planned, succeeded,
        partial, failed, verified, recalled and incident appear in no vocabulary on this path. The
        template contract's nearest thing is `lifecycle` — transactional | lifecycle | announcement |
        crisis | billing | support — which classifies a MESSAGE, not a release outcome. So .a's
        event-to-rule table has no events to key on.

        THE ON-CALL CATALOG EXISTS AND THE ROUTER CANNOT REACH IT.
        `apps/oshun/bff/src/channel-ops/oncall-schedule-catalog.ts` has exactly 1 importer,
        `routes/admin-oncall.ts`, and the routing store and route name no on-call anything. 0 of
        .b's 7 recipient dimensions — tenant, project, target, role, on-call, subscription, purpose
        — is present on the notification path, and nothing reauthorizes protected detail at delivery
        time, because nothing resolves a recipient at all.

        THE TEMPLATE SURFACE IS THE STRONG ONE, AND THIS CELL REPAIRED TWO HOLES IN IT.
        `AdminNotificationTemplate` carries per-locale subject and body across 6 channels, a declared
        variable set, A/B variants with traffic share, a four-state review lifecycle, and a
        deterministic lint pass over 7 rules including alt text, unbalanced HTML, reading grade and
        an unsubscribe requirement. Two of those rules did not do what they said.

        FIXED. AN `alt` ANYWHERE IN THE BODY EXCUSED EVERY IMAGE IN IT. `missing_alt_text` tested
        `body contains an <img>` AND `body contains no alt= anywhere`, so a body holding one
        described image and one undescribed one linted clean — driven. Each `<img>` is now asked for
        its own `alt`, and the message counts how many lack one.

        FIXED. AN ALLOWLIST THAT CANNOT SEE A REFERENCE CANNOT REFUSE IT. The variable-reference
        regex was `/\{\{(\w+)\}\}/`, which matches neither `{{rights.evidenceUrl}}` nor
        `{{provider-key}}` nor `{{ name }}`. An undeclared reference containing a dot, a hyphen or a
        space was not collected and therefore never raised `forbidden_variable` — and .d's protected
        classes are spelled exactly that way. Driven: both passed lint clean before, both are errors
        now. The lint runs on the real create and update path in
        `admin-notification-templates-store.ts`, and no committed template uses such a reference, so
        the allowlist tightened without breaking one.

        0 OF .c's 7 REQUIRED FIELDS IS REQUIRED. Status, asset/release ref, target, time, action,
        owner and deep link are not fields the contract obliges a template to render; `variables` is
        free-form per template. So a release notification can be authored that states none of them.

        1 OF .d's 8 PROTECTED CLASSES IS EVEN NAMEABLE on the notification path, and it is
        `provider`, appearing only as `providerMessageId` on the push payload. Biometric, child,
        protected source, credential, private prompt, rights evidence and cross-tenant metadata are
        not words this path knows, and A CLASS THE PATH CANNOT NAME IS ONE IT CANNOT WITHHOLD. There
        is no redactor on any of it, across all 4 carriers .d lists. The estate's designed
        allowlist projector — `projectAllowed` in `libs/oshun/workbench-kit/src/redaction.ts`, built
        for S8.12 with a disclosure ladder and a withhold-by-default register — has 0 product
        callers.

        5 OF .e's 8 DELIVERY PROPERTIES are present across the messaging stack: retry, rate limit,
        threading, preferences and receipts. Idempotency, backoff and dedup are absent, so a
        redelivery after a partial failure is not distinguishable from a second notification.

        ESCALATION IS ON THE INCIDENT LEDGER AND NOT ON THE ROUTER. `/operator/incidents` has all
        five verbs — acknowledge, assign, escalate, snooze, resolve — and the router has none of
        them, so .f's "escalate unacknowledged and failed ROUTES" has no route state to escalate
        from.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The lint
        pass is honest about what it cannot know: `low_contrast_color` detects inline colours and
        asks an operator to verify WCAG AA rather than returning a contrast verdict it did not
        compute, and `hasUnbalancedHtml` documents that it errs toward false positives. (2)
        `buildPushPayload` refuses a silent push carrying user-visible actions rather than dropping
        them quietly. (3) A NOTIFICATION IS NEVER TREATED AS REMEDIATION here — resolving an
        incident requires a resolution class and a rationale from the caller, and nothing closes one
        because a message went out. That is the one property of the twelve that this cell can
        record present on the product's own behaviour.

        TRAPS. (1) A CONCEPT SPELLED INSIDE A COMPOUND IS STILL SPELLED: `\bpreferences\b` never
        matches `NotificationPreferences` and `\bretries\b` never matches `maxRetries`, so a
        word-boundary search reported a messaging stack containing a `preferences.ts` module as
        having no preferences and put .e at 3 of 8 instead of 5. Identifiers are now segmented on
        camelCase. (2) AN HTML `<link>` TAG IS NOT A DEEP LINK — the bare word matched `'link'` in
        the lint pass's self-closing-tag set and reported .c's deep link as a required field. (3) AN
        ABSENT ANTI-PATTERN IS ONLY EVIDENCE IF THE THING IT WOULD CORRUPT EXISTS: "notification is
        not remediation" was true of a file before the incident ledger's five verbs were read, which
        would have been true of a file with no resolution path at all.

        3 of 12 properties present. ALL SIX CELLS AND THE PARENT LEFT `[ ]`: the router carries three
        fields and none of them is an event or a recipient, no protected class is nameable let alone
        withheld, no required field is required, and delivery cannot tell a retry from a repeat. -->
- [ ] I8.12 Implement recovery of failed multi-target release. Evidence:
      compensation and retry preserve idempotency.
  - [ ] I8.12.a Persist per-target/per-step planned/executing/written/verified/
        failed/unknown/compensating/rolled-back state and immutable receipts.
        Evidence: release state tests.
  - [ ] I8.12.b On interruption, reconcile authoritative destination state by
        idempotency key/version/hash before deciding retry or compensation.
        Evidence: crash recovery tests.
  - [ ] I8.12.c Classify targets/steps as safely retryable, compensatable,
        manually recoverable, or irreversible with exact reason. Evidence:
        recovery policy matrix.
  - [ ] I8.12.d Retry only missing/failed idempotent writes against the
        unchanged candidate/plan and never duplicate verified successful
        targets. Evidence: partial retry tests.
  - [ ] I8.12.e Compensate configured successful targets in dependency-safe
        order and verify removal/reversion while recording residual copies.
        Evidence: compensation tests.
  - [ ] I8.12.f Require fresh authorization/evidence/rights/target eligibility
        and impact preview for resume/retry/compensate/manual-complete commands.
        Evidence: stale recovery tests.
  - [ ] I8.12.g Derive final succeeded/partial/failed/rolled-back/manual state
        honestly and retain every attempt/receipt/cost/notification. Evidence:
        recovery history tests.
        <!-- MEASURED 2026-08-26 (I8.12.a-g). The parent's evidence is one clause — "compensation
        and retry preserve idempotency" — and both nouns presuppose a third: a release that reached
        more than one target and stopped part way. So the binding question is asked of the fan-out
        first. Instrument: `generate-oshun-release-recovery.mts` (+19 tests), wired into
        `verify:inventory:v1-workbenches`, writing `evidence/v1-workbenches/oshun-release-recovery.json`.

        A RELEASE WITH ONE DESTINATION HAS NO PARTIAL FAILURE TO RECOVER FROM. Across the six files
        the preceding cells established as the release path — I8.4's publish store and its route,
        the go/no-go contract, the two publishers an approved item reaches, and I8.6's one
        destination that reads its write back — there are 0 declared collections of targets, steps
        or attempts. Not a short list: no list. So there is no per-target state to persist, no
        subset to retry and no successful target to compensate, and the parent's clause is
        UNFALSIFIABLE for the same reason I8.11's was — the thing it constrains does not pass
        through. The detector is not blind: asked the same question, it finds `steps` in
        `workbench-kit/publishers.ts`, `targets` in `withdrawal.ts` and `attempts` in the wearable
        store.

        NO VOCABULARY IN THE ESTATE CAN SAY WHAT .a's RECORD IS SUPPOSED TO HOLD. Every declared
        `as const` array, `z.enum` and string union in the 141 non-spec sources that spell a
        compensation or rolled-back state was scored against .a's eight. The best is 3 of 8 —
        `libs/aje/account-abstraction/src/eip7702/types.ts#MigrationStatus` — and none of the top
        five sits on a publishing surface. The release path itself reaches 1 of 8, and that one hit
        is I8.6's read-back verdict `verified | unsupported_algorithm | storage_unreachable`, which
        says `verified` about bytes rather than about a target.

        THE ESTATE DESIGNED ALL OF I8.12 AND NO PRODUCT CODE CALLS ANY OF IT.
        `libs/oshun/workbench-kit/src/publishers.ts` (S6.9) is .a-.g almost line for line: a plan
        that is a value with a digest, a per-step durable record with idempotency key and
        request/response digests, an execute admission that rechecks the lock, the capability, the
        gates, rights, consent and target health, a read-back over seven dimensions including
        discoverability, a compensation order that runs in reverse and stops at the first
        irreversible step, and a reconciler for an outcome nobody observed. 9 analyzers, 59 named
        fault kinds, 0 callers outside the kit. 6 of 6 receipt fields are in it and 0 are on the
        release path; 3 of .c's 4 classes are in it and 0 are on the release path.

        EVEN THE DESIGN HAS NO STATE FOR A COMPENSATION IN FLIGHT. Its `STEP_STATES` are pending,
        sent, succeeded, failed, unknown, compensated, compensation-failed — 2 of .a's 8. `compensated`
        is the terminal one; .a asks for `compensating`, which is the state a crash DURING the undo
        leaves behind, and it is the one state whose absence makes a crashed compensation
        indistinguishable from one that never started.

        THE IDEMPOTENCY CLAIM IS WIRED, AND NOT TO ANYTHING THAT PUBLISHES. The kit's idempotency
        module is imported by `assistant/workbench-kit-read.ts` and enforced on the five commands
        `workbench-kit-commands.ts` exposes — capture, promote and archive a spark, transition and
        schedule a concept. 0 of the five publishes or releases.

        0 OF .f's FOUR RECOVERY COMMANDS IS A ROUTE ON A RELEASE, over 1597 route literals mounted
        under `/v1/` and the legacy `/admin/` prefix. `resume` exists three times — iris
        continuation tokens, a psyche adapter session, a routines execution — and retry, compensate
        and manual-complete exist zero times. A whole path segment is the rule, so
        `/analytics-reporting/retry-stats` and `/multi-agent-orchestration/retry-delay` are recorded
        as seen and not counted. Of .f's five freshness subjects only rights is nameable on the
        release path, and there is no impact preview because there is no command to preview.

        .g CANNOT BE DERIVED BECAUSE ITS OUTCOMES DO NOT EXIST. No release contract anywhere spells
        `partial`; the best final-state vocabulary in the estate reaches 3 of 5 and belongs to
        `libs/psyche/disaster-recovery`. Of .g's four retained subjects the release path carries
        receipt and cost and not attempt or notification — and I8.11 already established that
        nothing notifies about a release outcome at all.

        THE CONTROL: THE ESTATE CAN BUILD THIS AND HAS, ON A PATH THAT IS NOT A RELEASE.
        `POST /v1/wearable/health-sessions` → `wearable-health-store.deliverSessionDurably` is a
        routed two-target durable delivery. Its pending per-target attempt reaches durable storage
        BEFORE either platform is written; both operation ids derive from the durable session id so
        the key does not move between attempts; a replay returns the recorded platform record for a
        target already written instead of calling the bridge again; the partial outcome names both
        the failures and the delivered; and the erasure path deletes at both platforms by that same
        operation id, covers partial attempts as well as completed sessions, and throws rather than
        removing the local rows when a platform delete fails — it never records an erasure it did
        not perform. That is .d and most of .e, working, in this codebase, for health data. What it
        still cannot do is the same thing the release path cannot: nothing declares whether either
        platform HONOURS the key it is sent, and nothing sweeps — the resume happens only when the
        caller sends the same session again.

        FIXED. A CHECKER MUST NOT FAULT THE DECIDER'S OWN CORRECT ANSWER. `reconcilePublish` returns
        `retry-under-the-same-key` with no probe when the target cannot be asked and the adapter
        declares `returns-the-first-result` — the module's own test calls it "the one case where a
        retry IS the probe" — and feeding that record straight into `publishReconciliationFaults`
        produced `unknown-outcome-retried-before-a-probe`. Driven. The fault now excludes a target
        that promises the first result, because under that declaration repeating the request is how
        you ask; it still fires for `rejects-the-duplicate`, `ignores-the-key` and `unknown`.

        FIXED. AN EFFECT THAT STAYS BEHIND A BLOCKED ONE BECAME NOBODY'S TASK. Decision 4 says every
        effect that stays becomes an instruction naming the step, the reference and what a person
        must do. `compensationOrder` kept that promise for the step that blocked and dropped it for
        everything preserved behind it — so a plan with two irreversible steps preserved both and
        instructed about one, and feeding its own output back to `compensationFaults` raised
        `irreversible-effect-with-no-manual-instruction`. Driven. A COMPUTED PLAN MUST PASS ITS OWN
        CHECKER: it does now, and the checker still reports the plan when nobody was told anything.

        FIXED. A MAP KEYED BY STEP OVER A LOG OF ATTEMPTS HOLDS WHICHEVER ATTEMPT CAME LAST.
        `executionFaults` reads one row per ATTEMPT — `retry-that-changed-the-idempotency-key`
        exists precisely because it does — and built `new Map(executions.map(…))` to answer the
        dependency question, so it read whichever row the caller put last. The same three-row log —
        upload failed, upload succeeded, publish — reported `step-executed-out-of-dependency-order`
        newest-first and nothing oldest-first. Driven both ways. A dependency is now satisfied when
        ANY attempt of it succeeded, and the finding is raised once per pair rather than once per
        attempt.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1)
        `reconcilePublish` never settles an unknown outcome by guessing: with no probe and a target
        that ignores the key it escalates to a person, and `mark-failed-from-the-probe` is in the
        vocabulary ONLY so the checker can report a system that took it. (2) `compensationOrder`
        stops at the first irreversible step counting backwards and preserves everything before it,
        which is the right answer and not the convenient one. (3) The wearable erasure fails loud
        rather than reporting a removal it did not perform.

        TRAPS. (1) A CLASSIFICATION IS A VALUE A RECORD HOLDS, NOT A FIELD NAME: an identifier scan
        blanks literal contents, so `REVERSIBILITY = ['reversible', 'reversible-with-residue',
        'irreversible', 'unknown']` read as absent from the module that declares it, and .c went to
        0 of 4 in the design instead of 3. Classes are now read from declared vocabularies and whole
        literals. (2) `safely retryable` is genuinely not a stored class even so — the design
        DERIVES it from the adapter's declared idempotency at the moment it is needed, which is
        recorded rather than scored as a hole. (3) A WHOLE PATH SEGMENT, NOT A PREFIX: `/retry-stats`
        reports on retries and is not one. (4) AN EMPTY PARSE AND A MEASURED ABSENCE LOOK IDENTICAL —
        the population and both rankings throw when they come back empty, because every file in the
        population spells a state both required lists contain.

        0 of 12 properties present. THE PARENT AND ALL SEVEN CELLS LEFT `[ ]`: the release path has
        no second target, so it has no partial failure; nothing anywhere holds .a's states; the
        module that holds the design has no callers; and no route lets anyone resume, retry,
        compensate or manually complete a release. -->
- [ ] I8.13 Build campaign/program steering overlays that may tighten workflow,
      quality, safety, rights, budget, and audience limits but never loosen
      platform floors. Evidence: property tests and impact preview.
  - [ ] I8.13.a Define versioned overlay identity/scope/priority/lifecycle and
        typed workflow allow/deny, quality threshold, safety/rights requirement,
        budget/quota, audience/target, schedule, and owner fields. Evidence:
        overlay schema.
  - [ ] I8.13.b Resolve applicable platform/tenant/program/campaign/project
        policies in canonical precedence with conflict and cycle detection.
        Evidence: resolution tests.
  - [ ] I8.13.c Merge overlays only by intersection, stricter threshold, lower
        ceiling, additional requirement, or narrower audience/target semantics.
        Evidence: field-level merge property tests.
  - [ ] I8.13.d Reject any direct/indirect/default/omission/inheritance attempt
        to loosen code-owned safety, rights, consent, privacy, security, spend,
        or authority floors. Evidence: monotonicity property tests.
  - [ ] I8.13.e Preview affected workflows/models/controls/gates/budgets/users/
        scheduled work/releases, conflicts, and new blockers before activation.
        Evidence: impact preview tests.
  - [ ] I8.13.f Review/approve/version/schedule/deactivate with role separation,
        exact effective dates, audit, rollback to prior stricter state, and
        dependent revalidation. Evidence: overlay lifecycle tests.
        <!-- MEASURED 2026-08-26 (I8.13.a-f). The parent's evidence is two artifacts — property
        tests and an impact preview — over one claim: an overlay may TIGHTEN a limit and may never
        LOOSEN a platform floor. That claim presupposes three things and this cell asks for each: an
        overlay object, a floor for it to sit above, and a merge whose result is provably no looser
        than either input. Instrument: `generate-oshun-steering-overlays.mts` (+16 tests), wired
        into `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-steering-overlays.json`.

        A CAMPAIGN THAT ASKS PEOPLE TO SIGN A POLICY IS NOT A CAMPAIGN THAT STEERS ONE. The only
        thing in the estate called a campaign is `AdminPolicyAttestationCampaign` — 20 fields that
        assign a policy to an audience, count required attestations and track a compliance target
        percentage. 0 of the 20 is a threshold, a budget, a quota, an allow/deny or a limit. It is a
        campaign in the sense an email campaign is one.

        THE NEAREST REAL STEERING OBJECT IS THE POLICY BUNDLE, and it declares 28 fields covering 7
        of .a's 11 subjects. The four it misses are the four that make an overlay an overlay:
        PRIORITY, BUDGET/QUOTA, SCHEDULE and OWNER. An owner exists on the operator-side editor
        record as `ownerTeam` and not on the contract that travels, so the shipped overlay has
        nobody attached to it.

        NO PRIORITY MEANS NO PRECEDENCE, AND NONE OF .b's FIVE SCOPES IS A SCOPE. The scope
        vocabulary is global | domain | surface | persona | workflow_template | model_version |
        region | experiment and the 13 target kinds add provider, entitlement, feature_flag,
        memory_scope and policy_bundle. Platform, tenant, program, campaign and project are 0 of
        21. There is no priority field, so two applicable bundles have no declared order between
        them, and there are 0 resolvers in the estate — nothing anywhere picks among applicable
        bundles, so the precedence .b asks to be canonical is not computed at all.

        A BUNDLE MAY TARGET A BUNDLE AND NOTHING LOOKS FOR A CYCLE. `policy_bundle` is a target
        kind, so a → b → a is expressible, and the contract has neither cycle nor conflict
        detection.

        0 MERGE SITES. There is no `mergePolicyBundles`, no overlay intersection, nothing that
        combines two applicable policies — so .c's five permitted moves (intersection, stricter
        threshold, lower ceiling, additional requirement, narrower audience) have nothing to
        constrain, and .d's monotonicity property has no function to be a property OF.

        A FLOOR IS A VALUE A CHANGE IS COMPARED AGAINST, NOT A SUBJECT THE SCHEMA HAS A FIELD FOR.
        The overlay's validation is 16 `superRefine` passes raising 82 distinct refusal messages,
        and every one was read: not one refuses a value for being below a minimum. There are 0
        `MIN_`/`FLOOR_` constants in the contract, and no mutation reads the prior policy before
        writing the new one, so nothing can tell a tightening from a loosening. 3 of .d's 7 floors —
        privacy, security and spend — are not even expressible on the overlay; 4 are named and 0 are
        enforced.

        THE ONE SPEND LIMIT ON THE GENERATION PATH IS A SINGLE UNSCOPED NUMBER THAT IS OFF WHEN
        UNSET. `resolveGenerationBudgetConfig` reads `OSHUN_GENERATION_BUDGET_DAILY_USD`; a value
        `<= 0` disables the gate entirely, and there is no tenant, project, program or campaign
        dimension. So "an overlay may never loosen the spend floor" is vacuous twice over: there is
        nowhere to attach a tighter number, and by default there is no floor.

        9 PREVIEW SURFACES ARE MOUNTED AND 0 OF THEM PREVIEWS A POLICY. Over 1597 route literals the
        BFF mounts under `/v1/` and the legacy `/admin/` prefix, the preview surfaces are an
        incident impact view, an Aja avatar preview, an Isis live preview, a OneRoster preview, a
        digest preview and a Nyx ritual preview. None names a policy, an overlay, a bundle, a
        campaign or a program, so .e's ten subjects have no surface to be shown on.

        2 OF .f's FIVE LIFECYCLE VERBS ARE COMMANDS. Review and approve exist as store mutations;
        version, schedule and deactivate do not. The approver is stamped straight from the caller
        and compared to nobody, so there is no role separation on the one approval that exists —
        consistent with §I8.3's finding that the estate refuses a self-approval in 19 places and
        none of them is a review signoff. There are no effective dates because there is no schedule
        field, and no dependent revalidation.

        FIXED. A PRODUCTION PROMOTION WAS GATED ON A STRING BEING NON-EMPTY.
        `promoteAdminPolicyBundle` refused a production promotion unless
        `governance.rollbackBundleId !== null`. The gate exists so nothing reaches production
        without a state to fall back to, and it was satisfied by text: the seeded consumer-default
        bundle sits in production on the strength of `policy-pack-consumer-default-v2`, and the
        workspace holds three bundles, none of which is it. `rollbackAdminPolicyBundle` had the same
        hole — it stored whatever string it was handed and appended a `rolled_back` event naming it.
        Both now resolve the target against the workspace's bundles and refuse with
        `rollback_target_not_found`.

        FIXED. THE SAME DEFECT IN THE MODEL REGISTRY. `promoteAdminModelGovernance` and
        `rollbackAdminModel` accepted any non-empty `rollbackModelId`. Fixed the same way. The
        contrast is the useful part: the model seed DOES retain its prior version
        (`model-oshun-gpt-5-3`, which `model-oshun-gpt-5-4` points at), so its rollback test still
        exercises the success path against a target that exists — the same convention, in the same
        file, that the policy seed does not follow.

        FIXED. THE ESTATE'S OWN TEST CALLED A PHANTOM ID KNOWN-GOOD. `rolls back the
        consumer-default production rollout to a known-good bundle` asserted a 200 for
        `policy-pack-consumer-default-v2`. It now asserts the refusal, and a second case exercises
        the success path against a bundle the workspace holds — with the note that the store has no
        lineage between bundles, so the rule it can enforce is "the target is a bundle this
        workspace holds" and not "the target is a prior version of this one". .f's "rollback to
        prior stricter state" needs both a lineage and a strictness order and the model has neither.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        allowlists surface is a real workflow allow/deny: workflowTemplateIds, modelVersionIds and
        providerIds allow, blockedProviderIds denies, every list is uniqueness-checked, and a
        provider appearing in both lists is refused rather than silently resolved. (2) The
        governance schema will not let promotion metadata exist without an approval, will not let
        `promotedAt` precede `approvedAt`, and requires a changeSummary for an emergency override.
        (3) The generation budget gate reserves before the work and settles after, and its
        reservationId is the job id so a retried admission cannot consume the same estimate twice.

        TRAPS. (1) A SUBSTRING IS NOT THE CONCEPT: `/cycle/` matches `lifecycle`, and a contract
        declaring a lifecycle enum reported cycle detection it does not have. Concepts are matched
        on identifier SEGMENTS. (2) A bare `loosen|monotonic` sweep returned 77 files, almost all of
        them monotonic clocks; the monotonicity question is now asked of the overlay's own refusal
        messages instead. (3) A FIELD BAG IS NOT THE WHOLE SCHEMA — .a's subjects are read off the
        declared `PolicyBundleFields` key set rather than looked for as words in a 1686-line file.
        (4) AN OWNER ON THE EDITOR RECORD IS NOT AN OWNER ON THE OVERLAY.

        0 of 13 properties present. THE PARENT AND ALL SIX CELLS LEFT `[ ]`: no campaign or program
        is a scope, no overlay carries a priority or a budget, nothing merges two policies, no floor
        is a value anything is compared against, no surface previews a policy change, and three of
        the five lifecycle verbs do not exist. -->
- [ ] I8.14 Build UI-governed asset-kind, use-case, campaign, style, delivery,
      and gallery taxonomy registries over code-owned floors. Evidence:
      versioned review, alias, deprecation, localization, and migration journey.
  - [ ] I8.14.a Define separate typed registry entry/version/lifecycle contracts
        for asset kind, use case, campaign, style, delivery target/profile, and
        gallery taxonomy with stable code. Evidence: registry schemas.
  - [ ] I8.14.b Declare code-owned noneditable identities, safety/rights/
        compatibility semantics, required fields, forbidden relationships, and
        fallback behavior as registry floors. Evidence: floor tests.
  - [ ] I8.14.c Build operator list/detail/create-draft/edit/diff/validation UI
        with typed fields, restrained hierarchy, ownership, dependencies, usage,
        and no arbitrary JSON. Evidence: authoring tests.
  - [ ] I8.14.d Implement alias creation/resolution with scope, uniqueness,
        collision/cycle checks, canonical identity retention, and no silent
        semantic merge. Evidence: alias tests.
  - [ ] I8.14.e Implement localization for labels/descriptions/help/search terms
        with locale fallback, completeness, translator context, and no localized
        identity keys. Evidence: localization tests.
  - [ ] I8.14.f Review/approve/publish/deprecate/supersede/archive registry
        versions with role separation, effective dates, audit, and consumer
        compatibility checks. Evidence: lifecycle journey.
  - [ ] I8.14.g Preview and execute resumable migrations of dependent briefs/
        assets/workflows/collections/releases with exact mapping, conflicts,
        unchanged history, and rollback. Evidence: migration journey.
  - [ ] I8.14.h Verify unknown/deprecated/missing entries fail or degrade safely
        in every consumer, with contract/parity/accessibility/performance tests.
        Evidence: cross-host registry suite.
        <!-- MEASURED 2026-08-26 (I8.14.a-h). The parent asks for six REGISTRIES over code-owned
        floors, with a versioned review, an alias, a deprecation, a localization and a migration
        journey as its evidence. A registry is a thing entries are added to and retired from under a
        lifecycle, so the binding question is what shape each of the six vocabularies actually has.
        Instrument: `generate-oshun-taxonomy-registries.mts` (+14 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-taxonomy-registries.json`.

        A CONST ARRAY IS NOT A REGISTRY. 0 of the six is one, and one of them does not exist:
        asset kind is `ASSET_KINDS = ['image', 'video'] as const` in a rendition store; use case is
        a 6-member `THREE_D_QUALITY_USE_CASE_VALUES` array in the 3D quality-gates library;
        campaign is §I8.13's 20-field attestation interface; style is a 6-entry
        `Readonly<Record<string, MediaStyleGuide>>`; delivery target is a 4-member Nyx reminder
        channel array; gallery taxonomy is nothing at all. A new asset kind is a code change and a
        deploy. The shape detector is not blind — it tells a const array, a z.enum, a record map and
        an interface apart, and is asserted doing so.

        A NOTIFICATION CHANNEL IS NOT A DELIVERY TARGET. The only thing in the estate spelled
        `DELIVERY_TARGETS` is `['mobile-push', 'email', 'sms', 'calendar-sync']` on the Nyx
        event-action store — where a reminder goes, not what a release is delivered to.

        0 OF .a's THREE ENTRY PROPERTIES IS ON ANY OF THE SIX. None carries a stable code, a version
        or a lifecycle, and a sweep of every contract file naming one of the six that declares BOTH
        a version and a lifecycle returns 0 outside the study contracts.

        1 OF .b's FIVE FLOORS is declared on the six declarations, and it is "required fields".
        Recorded beside it because it is real and sits outside any declaration: the style map
        resolves an unknown preset through a named, documented `DEFAULT_STYLE_GUIDE_KEY =
        'editorial-illustration'` rather than returning undefined.

        0 ROUTES GOVERN ANY OF THE SIX, over 1597 route literals. Two taxonomy routes exist —
        `/v1/admin/hathor/lore-validation-taxonomy` and its `/validate` — and neither is one of the
        six, so .c's six authoring acts, .f's six lifecycle verbs and .g's migration have 0 surfaces
        between them. 0 aliases and 0 translations on any of the six, so .d and .e have nothing to
        resolve or localize.

        A REFUSAL SOMEWHERE IS NOT A REFUSAL EVERYWHERE. .h asks that an unknown, deprecated or
        missing entry fail or degrade safely IN EVERY CONSUMER; each of the three cases is covered
        by exactly one of the six. Unknown: the asset-rendition route refuses a kind outside
        `ASSET_KINDS` at the edge. Deprecated: only the campaign interface has a status field at
        all — A VOCABULARY WITH NO LIFECYCLE HAS NO DEPRECATED STATE TO DEGRADE FROM. Missing: the
        style map's documented default.

        THE GOVERNED REGISTRY EXISTS AND IS WIRED TO ANOTHER WORKBENCH. Yemaya's study taxonomy is
        every one of .a-.g: `TaxonomyTermFieldsSchema` declares 14 fields including an owner, a
        semver `vocabularyVersion`, `aliases`, BCP-47 `translations`, provenance,
        broader/narrower/related relations, applicability, and a lifecycle discriminated union where
        a deprecation MUST carry a `migrationRule`. `register-taxonomy-term.ts` refuses a relation
        pointing at a term that is not registered and refuses a deprecation whose replacement is
        not, and versions through envelope superseding. 13 files reach it; 0 of them is an Isis or
        Oshun consumer.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        control's registration refuses a second address for one term, refuses an address that
        reassigns ownership, refuses a term specialising itself, and requires a tenant. (2) Its
        self-reference check is DEPTH-1 only — a to b to a is not refused at registration — but the
        search expansion records every reference before queueing it and caps depth, so the walk
        terminates rather than hanging; the bounded consequence is recorded rather than reported as
        a defect. (3) The style map's fallback is named, documented and actually on the resolution
        path. (4) The asset-kind route refuses an unknown kind with an explicit message rather than
        passing it through.

        TRAPS. (1) A DECLARATION'S FILE IS NOT THE DECLARATION: `apps/oshun/bff/src/admin/state.ts`
        is 30,000 lines holding the whole admin workspace, so asking the FILE whether the campaign
        carries translations or a fallback finds both — belonging to something else. .b, .d and .e
        went from 3/5 and two spurious trues to 1/5 and two falses once every question was asked of
        the declaration's own slice. (2) A REGISTRY ROUTE THAT IS ANOTHER DOMAIN'S IS NOT ONE OF THE
        SIX — the two Hathor lore-validation taxonomy routes are recorded as seen and not counted.
        (3) AN EMPTY PARSE AND A MEASURED ABSENCE LOOK IDENTICAL: the control's fields come back
        empty unless the reader knows `z.object({`, and the throw caught it.

        0 of 9 properties present. THE PARENT AND ALL EIGHT CELLS LEFT `[ ]`: none of the six is a
        registry, one of them does not exist, no route governs any of them, and the governed
        registry the estate did build serves a different workbench. -->

### I9 — Isis observability, evaluation, and launch gates

- [ ] I9.1 Instrument plan, dispatch, provider, stage, gate, review, release,
      and recall traces with bounded attributes. Evidence: trace walkthrough.
  - [ ] I9.1.a Define canonical trace/span names, parents/links, status, events,
        sampling, retention, and schema versions for every listed lifecycle.
        Evidence: telemetry semantic convention.
  - [ ] I9.1.b Propagate approved
        trace/correlation/request/job/run/stage/release context across HTTP,
        queue, worker, provider, storage, outbox, and downstream target
        boundaries. Evidence: propagation tests.
  - [ ] I9.1.c Allowlist bounded low-cardinality tenant-safe attributes for
        asset/workflow/model/provider classes, state, failure class, target, and
        policy versions. Evidence: cardinality review.
  - [ ] I9.1.d Exclude prompts/media/source titles/identity/biometrics/secrets/
        URLs/raw IDs and apply centralized scrubbing to events/errors. Evidence:
        trace privacy tests.
  - [ ] I9.1.e Instrument success/failure/cancel/retry/cache/partial/rollback/
        recall transitions and asynchronous span links without false parentage.
        Evidence: lifecycle trace tests.
  - [ ] I9.1.f Walk a complete brief-to-recall journey and reconstruct every
        seam from traces plus canonical records. Evidence: trace walkthrough.
        <!-- MEASURED 2026-08-26 (I9.1.a-f). The parent's evidence is a TRACE WALKTHROUGH: walk one
        journey and reconstruct every seam. A walkthrough presupposes spans and spans presuppose
        something that starts them, so the binding question is asked of the product rather than of
        the manifest — on the eight lifecycles I9.1 names, what starts a span? Instrument:
        `generate-oshun-lifecycle-tracing.mts` (+15 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-lifecycle-tracing.json`.

        NOTHING IN THE BFF STARTS A SPAN, AND NEITHER DOES `apps/isis`. 18 files in the estate call
        `startSpan`/`startActiveSpan`, across 10 domain roots — lilith, v2, cybele, iris, isis
        libraries, oya, psyche, shared, yemaya — and 0 of them is in `apps/oshun` or `apps/isis`.
        Neither app imports `libs/shared/tracing`, which is a complete tracer with decorators, GenAI
        conventions and middleware. The sweep is not blind: it finds the roots that do trace, and it
        is asserted doing so.

        A TRACER THAT DEFAULTS TO RECORDING NOTHING IS NOT INSTRUMENTATION. The one tracing seam on
        the Isis side is `libs/isis/llm-providers/src/client/tracer.ts`, whose default is a
        `NoopTracer` and whose header documents exactly how to plug a real OTel tracer in. 0 product
        call sites do. The seam itself is honest — it records nothing rather than pretending to —
        and that is the whole of the Isis generation path's tracing.

        2 OF THE EIGHT LIFECYCLES HAVE A SPAN FAMILY. `libs/oshun/analytics/src/tracing-manifest.ts`
        declares 22 families across 11 subsystems, each with a SpanKind, a description, required
        propagation channels and typed attributes. Of I9.1's eight — plan, dispatch, provider,
        stage, gate, review, release, recall — only review (`review.queue.item_processed`) and
        release (`bff_release.request.handled`) are named, and neither is an Isis span. Isis has
        `isis.generation.run` and `isis.workflow.run`, which are the run and not the lifecycle.

        A SPAN DEFINITION FIXES 2 OF .a's SEVEN SUBJECTS. Read off `SpanFamilyDefinition`'s own
        declared fields — name, subsystem, kind, description, propagation, attributes — .a's name
        and parents/links are there and status, events, sampling, retention and schema version are
        not fields a family can carry.

        NAMING A CONTEXT FIELD IS NOT PROPAGATING IT. All seven of .b's identifiers — trace,
        correlation, request, job, run, stage, release — appear somewhere across the BFF's files,
        and that says nothing: S10.2's rule is to name the field the context rides in and then show
        that field has both a WRITER and a READER. The HTTP carrier is `traceparent`, and across
        `apps/oshun/bff/src` it has 0 readers and 0 writers. 0 of .b's 7 boundaries — HTTP, queue,
        worker, provider, storage, outbox, downstream target — carries a trace context at all.

        A TRACE ID MINTED PER CALL IS A NEW TRACE EVERY TIME. The BFF's one tracing port is the
        workbench-kit command router's `tracing.begin()`, and it mints a fresh `randomBytes(16)`
        trace id and a fresh `randomUUID()` correlation id on every call, accepting nothing inbound.
        Two commands in one operator session are two unrelated traces, and nothing joins them —
        which is exactly the missing edge S10.2 was written about, on the one path in the estate
        that has a tracing seam at all.

        THE ATTRIBUTE DESIGN IS 3,200 LINES WITH 0 PRODUCT CONSUMERS.
        `libs/oshun/workbench-kit/src/telemetry-attributes.ts` is a cardinality and unit design —
        signal kinds, cost shapes, bounded-domain requirements, unit dimensions and conversions —
        and no product code reaches it, so .c's allowlist is a design nothing draws from. For .d the
        estate holds exactly one scrubber, `apps/v2/telemetry-privacy-compliance`, and A SCRUBBER IN
        ANOTHER APP IS NOT ON THIS PATH: 0 of .d's 8 excluded classes is withheld anywhere Isis
        writes.

        3 OF .e's EIGHT TRANSITIONS ARE A VALUE THE MANIFEST HOLDS — success, failure and partial.
        Cancel, retry, cache, rollback and recall do not appear in the manifest at all, prose
        included, so the transitions a lifecycle trace exists to show are the ones it cannot.

        THERE IS NO WALKTHROUGH, AND THE THREE DESIGNS HAVE 0 CONSUMERS BETWEEN THEM. No evidence
        artifact anywhere is a trace walkthrough; a remediation note under `docs/domains/isis`
        describes end-to-end trace ids across api, queue, provider adapters and handler runtimes,
        and A DOCUMENT IS NOT A WALKTHROUGH. The span-family manifest, S4.13's correlation link
        graph (`atIngress`, `linkFaults`) and S10.2's `trace-continuity.ts` — 2,752 lines whose
        opening statement is A TRACE IS A CLAIM ABOUT CAUSATION AND IT IS MADE OF EDGES, NOT SPANS —
        have 0 product callers between the three of them.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The Isis LLM
        client's tracer port is an honest seam rather than a fabrication: it mirrors the OTel
        interface, documents how to plug a real tracer in, and its default records nothing instead
        of pretending to record. (2) The span manifest is a real machine-readable definition, not a
        list of names. (3) S10.2's module states the vein this cell turns on, and states it before
        the estate had anything to apply it to.

        TRAPS. (1) NAMING IS NOT PROPAGATING — the context-field sweep over the joined BFF sources
        returned 7 of 7 and would have made .b pass; asked of the carrier instead, it is 0 writers
        and 0 readers. (2) A WORD IN PROSE IS NOT A VALUE THE CODE HOLDS: transitions are read from
        the manifest's declared literals, and the five missing ones turn out to be absent from the
        file entirely. (3) A SCRUBBER IN ANOTHER APP IS NOT ON THIS PATH.

        0 of 9 properties present. THE PARENT AND ALL SIX CELLS LEFT `[ ]`: the lifecycle emits no
        spans, six of its eight stages have no span family, the carrier that would join them has
        neither a writer nor a reader, and the one trace id the BFF mints is new on every call. -->
- [ ] I9.2 Track queue delay, runtime, success, failure class, quality,
      moderation, cache, cost, and release conversion by safe dimensions.
      Evidence: metric schema review.
  - [ ] I9.2.a Define each counter/gauge/histogram name, unit, numerator/
        denominator, event source, aggregation window, owner, and schema
        version. Evidence: metric catalog.
  - [ ] I9.2.b Instrument queue wait/age/depth, execution/stage duration,
        throughput, success/partial/failure/cancel/retry, and stable failure
        taxonomy. Evidence: metric tests.
  - [ ] I9.2.c Instrument raw quality/moderation band distributions and
        unavailable/manual-review rates without exposing sensitive categories to
        unauthorized dimensions. Evidence: safety metric review.
  - [ ] I9.2.d Instrument cache lookup/eligible/hit/miss/corrupt/quarantine/
        recompute and estimate/reservation/actual/pending/variance costs.
        Evidence: cache/cost metric tests.
  - [ ] I9.2.e Define release funnel denominators from generated through gated/
        reviewed/bundled/written/verified/retained/recall states without hiding
        failed or excluded items. Evidence: conversion fixtures.
  - [ ] I9.2.f Enforce allowlisted low-cardinality tenant-safe dimensions,
        aggregation/privacy thresholds, and cardinality/volume budgets.
        Evidence: metric schema/privacy review.
        <!-- MEASURED 2026-08-26 (I9.2.a-f). The parent's evidence is a METRIC SCHEMA REVIEW, and a
        schema review presupposes a schema of something that is written. A metric is a counter, a
        gauge or a histogram — a named series with a unit and an aggregation window that a process
        increments — so the binding question is asked of the emitters: on the queue, the run, the
        gate and the release, what increments? Instrument:
        `generate-oshun-lifecycle-metrics.mts` (+14 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-lifecycle-metrics.json`.

        THE BFF PUBLISHES NINE SERIES AND EVERY ONE OF THEM IS ABOUT AN HTTP REQUEST. `GET
        /metrics` is mounted and serves Prometheus text from `BffMetricsCollector`, fed by an
        `onResponse` hook: requests_total, errors_total, error_rate, latency_ms, latency_mean_ms,
        rps, route_error_rate, domain_success_rate and domain_latency_p95_ms. (It published eight
        when this cell first measured; §I9.3's fix split the mean out from the quantile summary.)
        The record they aggregate has six fields — method, route, statusCode, durationMs, timestamp,
        domain — and not one of the nine names a queue, a run, a stage, a gate, a cache, a cost or a
        release, which is the whole of what I9.2 asks for. 0 files under `apps/oshun` or `apps/isis` construct a Counter,
        a Gauge or a Histogram; the same expression finds 14 elsewhere in the estate.

        A DEPENDENCY OF ANOTHER LIBRARY IS NOT A DEPENDENCY OF THIS APP. `prom-client` and
        `@opentelemetry/sdk-metrics` are declared by seven package.json files — shared/metrics,
        shared/tracing, lakshmi/core, cybele/api, yemaya/core, yemaya's study service and nyx's
        pipelines — and by neither app that would emit here.

        A DASHBOARD PANEL IS A QUESTION AND SOMETHING HAS TO ANSWER IT.
        `dashboards-queue-health.ts` names seven canonical queues — review, moderation, support,
        generation, appeal, incident communication, audit export — and nine metric kinds: depth,
        oldest-item age, throughput, p50 and p99 latency, dead-letter volume, retry rate, SLA breach
        rate and consumer lag. 4 of .b's eleven series are a kind on that dashboard and 0 of the
        eleven is emitted.

        THE ALERTS FIRE ON EVENTS AND THE LIFECYCLE EMITS NONE. All 13 alert definitions name
        `sourceEvents` from the canonical taxonomies rather than a series or a query, which is an
        honest design — but of the five event taxonomies, the release one has exactly 1 consumer and
        it is `feature-flag-expiry-alerts.ts` in the workbench kit. The BFF has one server-side
        event emitter, the studio-workspace telemetry route, and it forwards what a client posted.
        0 emitters are on the generation, gate or release path.

        A METRIC DEFINITION FIXES 4 OF .a's SEVEN SUBJECTS. Read off `QueueHealthMetricDefinition`
        and `AlertConditionDefinition` together: name, unit, event source and aggregation window are
        declared; numerator/denominator, owner and schema version are not fields either can carry.

        0 BAND, CACHE, COST AND FUNNEL SERIES. .c's quality and moderation distributions, .d's seven
        cache states and five cost states, and .e's eight funnel denominators are none of them a
        counter. The cost arithmetic .d would count DOES exist — the generation budget gate reserves
        an estimate before the work, settles the actual after, and reports the difference rather
        than pretending the estimate was right — it is simply never counted. And .f's cardinality
        design, `telemetry-attributes.ts`, is 3,200 lines with 0 product consumers, the same finding §I9.1
        recorded.

        FIXED. A METRIC LABEL FED FROM THE RAW URL IS UNBOUNDED, AND IT CARRIES THE QUERY STRING.
        The `onResponse` hook recorded `request.routeOptions?.url ?? request.url`. `routeOptions.url`
        is the matched route PATTERN and is bounded by the route table; the fallback is the raw path
        AND its query string, so every request that matched no route became its own label value in
        `oshun_bff_route_error_rate`. Driven: a label read back as
        `/v1/unmatched/490?token=secret-490`. The label is now the pattern, and one fixed
        `unmatched` value for everything else — asserted against a real Fastify instance: a matched
        request is labelled `/v1/thing/:id`, two different unmatched paths collapse to one value,
        and neither the id nor the query string reaches the label.

        FIXED. A WINDOW THAT PRUNES ONE STRUCTURE AND NOT THE OTHER IS NOT A WINDOW. `errorMessages`
        and `domainErrors` are keyed on that same caller-supplied route string and were never
        pruned, so the rolling window discarded the requests while the maps kept one permanent entry
        per distinct key — memory whose size was decided by whoever was sending requests. Driven:
        2,000 distinct 5xx routes left 2,000 entries behind a window holding 10. Both maps now
        honour the same time window and a hard 500-entry cap, oldest first, and the entries kept are
        the most recent ones.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The alerts
        manifest is honest about what it fires on: every condition names its sourceEvents, its
        threshold, its unit and its evaluation window, so an alert is a rule over the event taxonomy
        rather than over an imagined series. (2) The queue-health dashboard names its seven queues
        and nine metric kinds explicitly rather than leaving the panels to a query somebody writes
        later. (3) The generation budget gate reserves before the work and settles after, and its
        reservationId is the job id so a retried admission cannot consume the same estimate twice.

        TRAPS. (1) The first pass of this cell asserted there was no metrics collector at all; there
        is one, it is mounted, and it publishes eight real series — the finding is not that metrics
        are absent but that every series is about a request. (2) A DEPENDENCY OF ANOTHER LIBRARY IS
        NOT A DEPENDENCY OF THIS APP — asking the repository rather than the two apps made ".a has a
        metric library" pass. (3) THE SERIES COUNT COMES FROM THE EXPOSITION: series are read from
        the `# TYPE` lines a scrape returns, not from the word "counter" appearing in the module.

        0 of 8 properties present. THE PARENT AND ALL SIX CELLS LEFT `[ ]`: the eight series the BFF
        publishes are all about HTTP requests, no app declares a metric library, no queue or cache
        or cost or funnel state is a counter, and the dashboard that asks nine questions about seven
        queues has nothing answering it. -->
- [ ] I9.3 Define SLOs for plan, dispatch acknowledgement, queue age, provider
      success, artifact durability, gate freshness, and release verification.
      Evidence: error-budget policy.
  - [ ] I9.3.a Define user/system journey, population, eligibility, exclusions,
        measurement point, SLI formula, window, target, and owner for each SLO.
        Evidence: SLO specification.
  - [ ] I9.3.b Define plan latency, dispatch acknowledgement, queue age,
        provider success, artifact durability, gate freshness, and release
        verification SLIs from canonical metrics/events. Evidence: SLI query
        tests.
  - [ ] I9.3.c Segment only by actionable safe service tier/region/provider/
        workflow/target classes and prevent small-tenant disclosure. Evidence:
        SLO dimension review.
  - [ ] I9.3.d Define multi-window burn alerts, paging/ticket thresholds,
        missing-data behavior, maintenance/exclusion rules, and ownership.
        Evidence: alert simulation.
  - [ ] I9.3.e Define error-budget consumption, release/rollout/freeze policy,
        exception authority, recovery criteria, and historical amendments.
        Evidence: ratified error-budget policy.
        <!-- MEASURED 2026-08-26 (I9.3.a-e). The parent's evidence is a RATIFIED ERROR-BUDGET
        POLICY, and a budget is spent against an SLI: a formula over a population, measured
        somewhere, in a window, against a target. So the binding question is asked of the seven SLIs
        .b names — plan latency, dispatch acknowledgement, queue age, provider success, artifact
        durability, gate freshness, release verification — from what canonical metric or event would
        each be computed? Instrument: `generate-oshun-slo-error-budget.mts` (+13 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-slo-error-budget.json`.

        AN SLO FOR A SURFACE IS NOT AN SLO FOR A STAGE. `V1/planning/SLO_AND_DR.md` is a real
        ratified document — ten surfaces with an SLI definition, a target and a monthly error
        budget, a tiering principle that says why each tier is what it is, DR objectives per data
        class, a drill program and a named owner for all seven areas. Its rows are the BFF API,
        auth, two Psyche planes, two Living Scenes planes, the Metis gateway, the public viewer, the
        payments plane and the event bus. 2 of .b's seven SLIs have a row and both are incidental —
        `queue` appears in the render plane's BLACK-queue state and `gate` in the cue gate. 0 of the
        seven is computable from the nine series the BFF publishes.

        THE ONE SLI THAT IS COMPUTABLE, recorded so those zeros read as a distance rather than a
        blank: the BFF API row's availability SLI is "non-5xx responses ÷ valid requests", and both
        numbers are published as `oshun_bff_requests_total` and `oshun_bff_errors_total`. Its
        latency SLI is stated at p99, which the exposition did not carry until this cell.

        THE BURN ARITHMETIC IS CORRECT AND ITS INPUT IS A NUMBER SOMEBODY TYPED. Two surfaces
        implement the multiwindow multi-burn-rate model and both read their inputs from a request
        body. `POST /v1/admin/oncall/burn-decision` applies the thresholds — page at 14.4x over 1h
        only when the 5m window agrees, ticket at 6x over 6h only when the 30m agrees — to four burn
        rates in the body. `POST /v1/admin/studio/observability-dashboards/evaluate` goes further
        and DERIVES the rate, `errorRate / (1 - sloTarget)`, requiring two windows to page — and
        reads the error rate and the target out of the body too. Neither reads a published series.
        The estate can evaluate a burn rate; nothing measures one.

        A DOCUMENT THAT MENTIONS A WORD IS NOT A SPECIFICATION THAT FIXES IT. 4 of .a's nine
        subjects are columns the SLO table fixes per SLO — the surface, the SLI formula, the monthly
        window and the target. Population, eligibility, exclusions and the measurement point live
        inside some rows' free-text SLI cell and not others', and ownership is a separate table
        keyed by ROLE, so no SLO row carries an owner.

        3 OF .c's FIVE SEGMENTATION CLASSES appear in the specification and there is no
        small-tenant protection: no k-anonymity, no minimum cohort, no suppression rule. 3 of .d's
        six alert subjects are present — the four windows and both thresholds — with no
        missing-data rule, no maintenance exclusion and no owner on the alert itself.

        A SPEND BUDGET IS NOT AN ERROR BUDGET. The policy states a feature freeze — budget exhausted
        means only reliability and rollback changes merge, and canary promotions are blocked while
        any RED-tier surface is over budget — and 0 sites enforce it. Seven files mention a budget
        being exhausted; the two a looser sweep first returned are a USD retry budget in
        `ai-platform/efficiency.ts` and a verification budget in the kit. 4 of .e's five policy
        subjects are stated; exception authority is not.

        A NAME THAT COLLIDES IS NOT A CONSUMER. `libs/oshun/workbench-kit/src/service-objectives.ts`
        is 4,026 lines that designed I9.3.b's own vocabulary — its `SLI_CLASSES` are availability,
        correctness, latency, deadline, QUEUE-AGE, DURABILITY and FRESHNESS — with observation
        points, blind spots, exclusions with named deciders, ratio faults and outcome grades. It has
        0 importers. A sweep on `blindSpots|ratioFaults|gradeFaults` returns 11 files and every one
        is another domain's identically-named thing, Veritas' Ghana regions with no source coverage
        among them.

        AN SLO FOR ANOTHER JOURNEY IS NOT THIS JOURNEY'S SLO.
        `apps/oshun/bff/src/agentic/human-authority-slo-store.ts` is a genuine 658-line SLO surface
        with six numeric targets — takeover acknowledgement p95 2s, checkpoint resume p95 30s,
        latency success rate, edit incorporation rate, drill success rate, drill max age — and six
        required drills. 0 of its targets is one of .b's seven.

        FIXED. A MEAN PUBLISHED AS A MEDIAN IS A WRONG NUMBER WITH A CORRECT NAME. The Prometheus
        exposition emitted `avgLatencyMs` under `oshun_bff_latency_ms{quantile="0.5"}`. Driven with
        99 requests at 10 ms and one at 10 s: the median is 10 and the published value was 109.9,
        wrong by eleven times, under a label that says exactly which number it is. The quantile now
        carries the median, the mean is published as `oshun_bff_latency_mean_ms` under a name that
        says what it is, and `quantile="0.99"` was added because the ratified SLO states the BFF
        latency SLI at p99 and `percentile()` was already computing it. §I9.2's record was updated
        from eight published series to nine.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The
        multiwindow burn policy is implemented and not merely described: the page condition requires
        both the 1h and the 5m window, the ticket condition both the 6h and the 30m, and the window
        evidence is returned either way so a decision can be read back. (2) The SLO document says
        why each tier is what it is rather than asserting numbers. (3) Crisis-path availability is
        deliberately NOT given a budget — any unavailability is a Sev-1 regardless of duration —
        which is a decision recorded rather than an omission. (4) The human-authority SLO store is a
        genuine SLO surface for the journey it is about.

        TRAPS. (1) A SUBSTRING IS NOT THE CONCEPT, for the second time this phase: `plan` is inside
        "control plane", `queue` inside "BLACK queue state", `gate` inside "Metis gateway" and "cue
        gate", and `target` inside "targets ratchet". Substring matching put .b at 3 of 7 and .a at
        9 of 9. (2) A DOCUMENT THAT MENTIONS A WORD IS NOT A SPECIFICATION THAT FIXES IT — .a is now
        read off the table's columns. (3) A NAME THAT COLLIDES IS NOT A CONSUMER — an import
        specifier, not a symbol name. (4) The first pass of this cell reported that nothing computes
        a burn rate; a 993-line store does, correctly, and the finding is about its input.

        0 of 8 properties present. THE PARENT AND ALL FIVE CELLS LEFT `[ ]`: no stage of the
        lifecycle has an SLO, no SLI it owes is computable from anything published, the burn model
        is fed from a request body, the freeze the policy ratifies is enforced by nothing, and the
        4,000-line module that designed exactly these SLI classes has no importers. -->
- [ ] I9.4 Build provider and worker fault-injection tests. Evidence: throttle,
      timeout, malformed output, worker loss, and split-brain cases.
  - [ ] I9.4.a Build hermetic controllable
        provider/worker/queue/callback/storage fault fixtures with deterministic
        clocks, IDs, and failure points. Evidence: fault harness tests.
  - [ ] I9.4.b Inject rate limit/throttle/capacity/refusal with Retry-After and
        verify taxonomy, backoff, circuit, reservation, and UI state. Evidence:
        throttle tests.
  - [ ] I9.4.c Inject connect/read/overall/deadline timeouts before, during, and
        after provider acceptance/output write. Evidence: timeout tests.
  - [ ] I9.4.d Inject malformed status/schema/content type/oversize/artifact/
        manifest/hash/callback data and verify quarantine/no success. Evidence:
        malformed output tests.
  - [ ] I9.4.e Kill/evict/restart workers at each checkpoint and verify fenced
        lease, explicit resume/new attempt/failure, no duplicate output/cost.
        Evidence: worker-loss tests.
  - [ ] I9.4.f Simulate lease expiry/network partition/two workers/late callback
        and prove stale actors cannot commit terminal state. Evidence: split-
        brain tests.
  - [ ] I9.4.g Verify telemetry, alert, cancellation, cleanup, resource release,
        and immutable history for every injected case. Evidence: fault coverage
        report.
        <!-- MEASURED 2026-08-26 (I9.4.a-g). The parent names five cases as its evidence — throttle,
        timeout, malformed output, worker loss, split-brain — so the binding question is asked case
        by case: is the fault INJECTED into the real path, and what is asserted once it lands?
        Instrument: `generate-oshun-fault-injection.mts` (+13 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-fault-injection.json`.

        THE PROVIDER HALF IS REAL AND THE WORKER HALF HAS NOTHING TO LOSE. Across 60 generation
        sources and 59 tests, 16 modules take an injectable `fetchImpl`, 6 tests put a 429 on the
        wire through it and 37 assert a rejection rather than a success. That is a genuine hermetic
        seam and the first three cases, on the real modules — not a network test wearing a mock.

        `lease` IS A SUBSTRING OF `release`. Matched as a whole word, the generation path has no
        lease, no fence, no heartbeat and no checkpoint; 1 of .e's five subjects is present and it
        is `attempt`. The executors run in process. A worker that cannot be evicted cannot be
        killed at a checkpoint, and two workers that cannot exist cannot both commit, so .e and .f
        have no subject rather than a failing one. The estate DOES have leases — 44 files carry a
        `leaseId` or a `fencingToken` — and none of them is here.

        A CODE ASSEMBLED FROM A STATUS IS NOT A TAXONOMY. Five providers throw
        `` `…_http_${response.status}` ``, so the failure CLASS is whatever the provider returned
        rather than a member of a closed set a caller could switch on, and .b asks the throttle case
        to verify a taxonomy. 0 declared failure vocabularies exist on this path. Of .b's five
        downstream properties only backoff is present: no Retry-After is read, no circuit exists,
        and nothing releases a reservation when a throttle lands.

        1 OF .c's FOUR TIMEOUT KINDS IS DISTINGUISHABLE, and it is the coarsest. Only an overall
        timeout is expressible; connect, read and deadline are three different failures with three
        different remedies and this path cannot tell them apart, let alone place one before, during
        or after provider acceptance.

        5 OF .d's EIGHT MALFORMED KINDS ARE NAMEABLE — schema, content type, artifact, manifest and
        hash — and status, oversize and callback are not. None is quarantined: quarantine is a state
        an output is IN, 241 files in the estate know the word, and the generation path is not one
        of them.

        0 OF .g's SIX COVERAGE SUBJECTS IS ASSERTED in any generation test. Not telemetry — §I9.1
        established there are no spans and §I9.2 no lifecycle series — not an alert, not
        cancellation, not cleanup, not resource release, not an immutable history.

        THE KIT HOLDS 5,031 LINES OF FIXTURES FOR EXACTLY THESE SEVEN CELLS, WITH 0 IMPORTERS.
        `throttle-fixtures` (652), `timeout-fixtures` (735), `malformed-fixtures` (832),
        `lease-fixtures` (545), `partial-fixtures` (615), `retry-exhaustion-fixtures` (605),
        `cancellation-fixtures` (577) and `duplicate-fixtures` (462) — every one substantial rather
        than nominal, and not one imported by product code.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The provider
        fault tests hold the bright line: an injected 429 or an empty response produces a typed
        error naming the provider and the status, and never a fabricated success. (2) Sixteen
        modules taking an injectable fetch is a real hermetic harness, and .a's provider seam is
        genuinely satisfied even though its queue, callback and storage seams are not. (3) The kit's
        fixtures are written for these cells and are real work, not placeholders.

        TRAPS. (1) `lease` IS A SUBSTRING OF `release` — a substring sweep for a lease over a
        release path finds one in every file. The whole of .e and .f turns on the distinction, and
        the control is recorded: `release` appears on the path, `lease` as a word does not. (2) AN
        ERROR CODE IS A LITERAL, NOT AN IDENTIFIER: an identifier scan blanks literal contents, so
        the taxonomy .b asks about was invisible until literals were read from the SOURCES — and
        from the sources only, because a test's `it('cancels the job')` title is prose and counting
        it would let a description stand in for an assertion.

        0 of 9 properties present. THE PARENT AND ALL SEVEN CELLS LEFT `[ ]`: three of the five
        named cases are injected and two have no subject, the refusal a throttle produces is
        assembled rather than classified, only the coarsest timeout is expressible, nothing is
        quarantined, and none of the six things .g asks to be verified is asserted anywhere. -->
- [ ] I9.5 Build known-value tests for every technical quality gate. Evidence:
      gate registry coverage test.
  - [ ] I9.5.a Enumerate every active technical gate and asset-kind/target/
        profile applicability row directly from the gate registry. Evidence:
        generated coverage manifest.
  - [ ] I9.5.b Create licensed versioned positive/negative/boundary/N/A/
        unsupported/corrupt fixtures with expected raw measurements and units.
        Evidence: fixture manifests.
  - [ ] I9.5.c Pin evaluator code/config/dependencies and numeric tolerance/
        determinism rules per fixture. Evidence: reproducibility configuration.
  - [ ] I9.5.d Assert raw measurements, regions/timecodes/object refs,
        uncertainty/applicability before asserting policy verdicts. Evidence:
        known-value suites.
  - [ ] I9.5.e Assert threshold below/equal/above and target-profile changes
        recompute correct verdicts without reprocessing where permitted.
        Evidence: boundary tests.
  - [ ] I9.5.f Fail CI when an active registry row lacks a current fixture or a
        fixture points to retired/unknown gate versions. Evidence: registry
        coverage gate.
        <!-- MEASURED 2026-08-26 (I9.5.a-f). The parent's evidence is a GATE REGISTRY COVERAGE TEST:
        every active row has a current fixture and CI fails when one does not. A known-value test is
        one where a fixture with a KNOWN raw measurement produces an expected verdict, so the
        binding question is asked of the registry's rows — how many are exercised across a
        threshold, and what happens to the ones that are not? Instrument:
        `generate-oshun-gate-known-values.mts` (+13 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-gate-known-values.json`.

        THE REGISTRY IS REAL AND ITS COVERAGE IS NOT ENUMERATED.
        `libs/oshun/generation-control-isis/src/release-gate-model.ts` declares nine canonical gate
        kinds, an applicability list per output kind, a quality floor and a watermark floor per
        output kind and one safety floor — 9 x 11 is 68 applicability rows, and every threshold
        names the measurement it bounds and varies honestly by kind (a 3d-model's watermark floor is
        0 because watermarking does not apply; an image's is 1). 19 of the 68 rows are exercised by
        a test that names both the row's output kind and its gate. Nothing enumerates the other 49,
        there is no generated coverage manifest, and 0 CI steps fail when a row has no fixture.

        FIXED. A NUMBER THAT IS NOT A NUMBER PASSES A RANGE CHECK WRITTEN AS TWO COMPARISONS.
        `NaN < 0` and `NaN > 1` are both false, so `if (x < 0 || x > 1)` admitted it — and every
        threshold below it, `x < floor`, was false as well. Driven: a measurement whose safety,
        watermark and quality scores were all `NaN` validated as `{ ok: true }` and cleared every
        numeric gate, the release mode read `allow`, and the verdicts announced the comparisons they
        had not performed — "safety score NaN ≥ floor 0.9" and "watermark coverage NaN ≥ floor 1".
        Infinity was caught, because `Infinity > 1` is true; only NaN slipped. All three range
        checks and the validator now use one `Number.isFinite` predicate. The same measurement now
        blocks on all three gates with three validation errors. This matters because
        `apps/oshun/bff/src/generation/release-gate.ts` consults that validator to decide whether a
        measurement is canonical at all, so an unmeasured score was releasable.

        FIXED. ASSERTING A CONSTANT IS NOT TESTING THE THRESHOLD. The suite asserted
        `CANONICAL_SAFETY_SCORE_FLOOR === 0.9` and never drove the evaluator across it. Five cases
        added: each of the three floors below / exactly at / above, the non-number case, and a
        control that the infinities and out-of-range values it always refused are still refused. All
        three thresholds now have a failing verdict AND a passing verdict asserted against the
        evaluator, which is .e's first clause.

        .e HAS TWO CLAUSES AND ONLY THE BOUNDARY ONE IS SATISFIED, SO THE CELL STAYS `[ ]`. Its
        second asks that a target-profile change recompute correct verdicts without reprocessing
        where permitted. The registry has no profile — consistent with §I8.4's finding that 0 of 9
        candidate subjects are pinned to a target profile — so there is nothing for a change to
        recompute against. The kit declares four recompute TRIGGERS (automatic, on-next-save,
        manual, scheduled) and no product registry keys off them.

        5 OF .b's SIX FIXTURE CLASSES are in the suite; `unsupported` is not. 1 OF .c's FIVE PINNING
        SUBJECTS is pinned — a numeric tolerance — and the evaluator version, its config, its
        dependencies and a determinism rule are not. 2 OF .d's FIVE RAW SUBJECTS are fields the
        measurement carries: the raw score and the object ref. A region or timecode, an uncertainty
        and an applicability statement are not, so a verdict cannot say WHERE in the asset it was
        measured or how sure it is.

        A GATE THAT REPORTS UNAVAILABLE IS NOT A GATE THAT PASSED. The second registry,
        `libs/oshun/tara-workbench/src/gate-registry.ts`, records its own absences: four gates are
        `available` and one is a `seam` waiting on a bound audio-analysis pipeline, and a seam
        reports unavailable rather than passing. That is the shape .a's enumeration wants and it
        exists on the smaller of the two registries.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The floor
        comparisons are consistent and self-documenting: `< floor` fails and the pass note reads
        `≥ floor`, so the boundary is stated in the verdict a reader sees. (2) Every threshold
        varies by output kind rather than being one number applied to everything. (3) The Tara
        registry's seam is the honest kind — no evaluator at all, recorded as such.

        TRAPS. (1) A `gateCoverage` IDENTIFIER IN ANOTHER DOMAIN IS NOT THIS REGISTRY'S COVERAGE
        GATE: the bare name matches 21 files, Maya's standards modules among them, and .f would have
        passed on them. A site has to name the registry it covers — and then A RULE THAT NAMES WHAT
        IT WATCHES COUNTS ITSELF: this instrument spells `CANONICAL_OUTPUT_GATE_REQUIREMENTS` in
        order to parse it, so the moment it was committed it matched its own tightened sweep and .f
        passed on the existence of the census that reports .f absent. The instrument now excludes
        itself. (2) A ROW IS A PAIR: `image`
        appears in a dozen tests that say nothing about watermark coverage, so a row counts as
        exercised only when the test names both halves. (3) Prettier wraps a long assertion, so a
        verdict literal can sit on its own line and a `\.toBe\('` pattern misses it — the boundary
        detector reported 2 of 3 until it allowed the wrap.

        1 of 8 properties present, and it is the one this cell earned. THE PARENT AND ALL SIX CELLS
        LEFT `[ ]`: 49 of 68 registry rows are unexercised, nothing enumerates them, no CI step
        fails when one has no fixture, the evaluator is not pinned, the measurement cannot say where
        or how surely it measured, and .e's target-profile clause has no profile to change. -->
- [ ] I9.6 Build calibrated evaluation suites for model-based safety,
      similarity, and perceptual gates. Evidence: precision/recall, threshold,
      subgroup, and uncertainty report.
  - [ ] I9.6.a Define licensed/consented representative train-free calibration,
        threshold-selection, and held-out evaluation corpora with provenance,
        labels, subgroup/risk/style slices, and versioning. Evidence: corpus
        manifests.
  - [ ] I9.6.b Define expert annotation guidance, multi-rater process,
        adjudication, inter-rater reliability, ambiguous/N/A labels, and blinded
        sampling. Evidence: labeling report.
  - [ ] I9.6.c Run safety, modality-specific similarity, and perceptual
        evaluators with pinned versions/configs and retain per-item raw outputs.
        Evidence: reproducible evaluation runs.
  - [ ] I9.6.d Report precision/recall/specificity/FPR/FNR/PR or ROC/calibration
        and confidence intervals at candidate thresholds. Evidence: metric
        report.
  - [ ] I9.6.e Report subgroup/content/style/device/quality slices with sample
        counts, uncertainty, disparity, worst cases, and privacy thresholds.
        Evidence: subgroup report.
  - [ ] I9.6.f Select fail/review/pass bands against ratified harm/cost criteria
        using threshold corpus only, then verify held-out performance. Evidence:
        threshold decision record.
  - [ ] I9.6.g Define drift monitoring/recalibration triggers and route
        uncertain/ low-quality/out-of-distribution cases to human review.
        Evidence: uncertainty/drift plan.
        <!-- MEASURED 2026-08-26 (I9.6.a-g). The parent's evidence is a precision/recall, threshold,
        subgroup and uncertainty REPORT, and every one of those is a statement about a labelled
        corpus. So the binding question is asked of the corpus: which labelled set was the safety
        gate's threshold chosen against? Instrument:
        `generate-oshun-gate-calibration.mts` (+13 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-gate-calibration.json`.

        A THRESHOLD IS A NUMBER SOMEBODY PICKED UNTIL A CORPUS CHOOSES IT.
        `CANONICAL_SAFETY_SCORE_FLOOR = 0.9` is declared with a comment saying what the number MEANS
        — the minimum safety score an output must earn — and 0 records anywhere say how 0.9 was
        arrived at. 0 of .a's three corpora exists: no calibration set, no threshold-selection set,
        no held-out set. So .d's precision and recall have no population to be computed over, .e's
        subgroup report has no slices to report, and .f's decision record has no decision to record.

        2 OF THE THREE GATE FAMILIES HAVE AN EVALUATOR. Safety has sixteen modules that touch it and
        similarity two; PERCEPTUAL has none on the generation path at all, so the family the parent
        names third has nothing to calibrate.

        A `-latest` ALIAS IS NOT A PINNED VERSION. The one evaluator whose identity is declared is
        `DEFAULT_MODERATION_MODEL = 'omni-moderation-latest'`, a floating alias — so the model
        answering the gate can change under any calibration that would justify the threshold. Its
        per-item raw outputs ARE retained (`categoryScores` per category), which is the other half
        of .c. Whether to pin a dated snapshot here is a product decision with real consequences —
        a dated snapshot can be retired by the provider — so it is recorded rather than changed.

        3 OF .d's EIGHT METRICS reach the path. Specificity, false-positive rate, false-negative
        rate, a PR or ROC curve and a confidence interval do not, and four of them are computed
        elsewhere in the estate for something else. 2 of .b's six annotation subjects and 2 of .e's
        six subgroup subjects are present; there is no multi-rater process, no adjudication, no
        inter-rater statistic, no blinded sampling, no disparity and no privacy threshold.

        A CALIBRATION FOR ANOTHER JUDGE IS NOT THIS GATE'S CALIBRATION.
        `libs/oshun/agentic-studio/src/feedback/gold-set-calibration.ts` is exactly the shape .6
        asks for: a held-out gold set re-scored by an injected machine judge, Cohen's kappa over a
        threshold sweep, a recommended cutoff that maximises agreement with the human verdict, and a
        fail-loud seam that throws rather than fabricate a calibration when the judge is absent. It
        calibrates the agentic-studio content judge. It names no release gate.

        BOTH STATISTICS ENGINES HAVE 0 IMPORTERS. `trust-safety/evaluation.ts` implements an
        expected-calibration-error over bins, a gold set and live cohorts; `search-discovery`'s
        offline evals implement nDCG, MAP and recall@k. Neither is imported outside its own library.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The image
        safety classifier is honest about what it is: a fail-closed adapter to a real moderation
        service that returns null without a key so the gate BLOCKS, and throws on a moderation
        outage rather than releasing ungoverned — its header says in as many words that a fabricated
        passing score would be the forbidden stub. (2) The safety score is a stated transformation
        of the provider's own per-category numbers — one minus the maximum violation — rather than a
        probability it claims to have calibrated. (3) The trust-safety module computes a real
        expected calibration error rather than reporting a calibration it did not perform.

        TRAPS. (1) A DOCUMENT THAT NAMES THE ARTIFACT AS OUTSTANDING IS NOT THE ARTIFACT: a sweep
        for "threshold decision record" returned `ISIS_GENERATION_WORKBENCHES_DESIGN` — the table
        where I9.6.f is listed as work still to do — and .f passed on it. And then A RULE THAT NAMES
        WHAT IT WATCHES COUNTS ITSELF, for the second time this phase: this cell's own evidence
        artifact quotes the checklist text, so writing it moved the candidate count from 1 to 2 and
        the report stopped being idempotent. The sweep now excludes its own output, and idempotence
        is checked by running the generator twice. Naming the constant is not
        deciding it either: `V1/AUDIT_2026-06-24.md` names all three floors in order to observe that
        the docs never surface them. (2) `corpora` IS A SUBSTRING OF `corporate`, so the marketing
        page read as a corpus; the survivors of a tightened sweep are a docs-retrieval fixture and
        the kit's credential- and stripped-content scanners, and none is a labelled evaluation set.
        (3) A PROPERTY OF A CORPUS IS ASKED OF THE CORPUS — over the joined generation path all five
        of .a's properties appear somewhere and .a read 5 of 5. (4) A NAME THAT COLLIDES IS NOT AN
        IMPORTER: `computeMap` matches ten files across asase, iris, nisaba and uzume.

        0 of 8 properties present. THE PARENT AND ALL SEVEN CELLS LEFT `[ ]`: there is no corpus, so
        there is no precision, no recall, no subgroup slice and no threshold decision; the one
        evaluator whose identity is declared floats; and the calibration machinery the estate did
        build serves a different judge. -->
- [ ] I9.7 Build BFF contract tests for every route, role, seam, and failure.
      Evidence: route inventory coverage is complete.
  - [ ] I9.7.a Generate/reconcile the complete Isis/Yemaya/Oshun/gallery BFF
        route-method-operation inventory with handlers, schemas, owners,
        audiences, consumers, and tests. Evidence: route coverage manifest.
  - [ ] I9.7.b Assert request/response/content/error schemas, discriminators,
        versions, units, property absence/redaction, and unknown-field handling.
        Evidence: schema contract tests.
  - [ ] I9.7.c Run anonymous/curated/AAA/operator/reviewer/admin and cross-
        tenant/project object/property/function authorization matrices.
        Evidence: role contract tests.
  - [ ] I9.7.d Exercise service/provider/storage/queue/search/publisher seams in
        success, empty, partial, stale, not-configured, degraded, timeout,
        conflict, refusal, and malformed modes. Evidence: seam tests.
  - [ ] I9.7.e Assert pagination/idempotency/concurrency/cursors/deep links/
        streaming/reconnect/rate/quota/budget semantics. Evidence: protocol
        contract tests.
  - [ ] I9.7.f Fail CI for unclassified routes, untested role/operation rows, or
        consumer/provider drift. Evidence: inventory coverage gate.
        <!-- MEASURED 2026-08-26 (I9.7.a-f). The parent's evidence is one sentence — ROUTE INVENTORY
        COVERAGE IS COMPLETE — so the binding question is a ratio: of the routes the BFF mounts, how
        many does a test actually call? Instrument:
        `generate-oshun-bff-contract-coverage.mts` (+9 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-bff-contract-coverage.json`.

        A ROUTE THAT NOTHING CALLS IS NOT A COVERED ROUTE. 522 route modules declare 1,591 distinct
        paths under `/v1/` and the legacy `/admin/` prefix. Every declared path is reduced to the
        same shape as every URL any spec passes to `app.inject` — a parameter is `:id` on the mount
        and `${id}` in the injection, and a query belongs to neither — and matched. 602 of the 1,591
        paths are exercised and 989 are not; 203 of the 504 modules that declare a path have at
        least one of theirs called and 301 have none. The sweep is not blind: it found 1,491
        distinct injected URLs across 1,137 spec files.

        A WORD SOMEWHERE IN A THOUSAND SPECS IS NOT AN ASSERTION ABOUT A ROUTE. All 7 of .c's
        audiences, 9 of .d's 10 seam modes and 9 of .e's 10 protocol subjects appear somewhere
        across those spec files, and a first pass graded .c, .d and .e present on exactly that. Each
        of those cells is a statement about the ROUTE SURFACE — run the matrices, exercise the
        seams, assert the semantics — so each is now graded alongside the coverage ratio, and none
        can pass while 989 paths are never called. 4 of .b's seven schema subjects are named at all;
        a request, a response and an error schema are not asserted by name anywhere.

        A DISPOSITION RATCHET IS NOT A COVERAGE GATE. The estate does gate on routes: §I0.18's Isis
        route register, its disposition ratchet and the effective-route matrix are all three wired
        into `verify:inventory:v1-workbenches`, and each refuses a drifted CLASSIFICATION. 0 gate
        refuses an untested route. 5 of .a's six inventory attributes appear across the six route
        registers the estate holds; a handler is not one of them.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The specs
        that exist drive the real app through `app.inject` rather than calling handlers directly, so
        a covered route is covered end to end through its middleware. (2) The three route gates are
        real ratchets over real registers, and the classification work they protect is genuine — it
        is simply a different question from this one.

        TRAPS. (1) A RULE THAT NAMES WHAT IT WATCHES COUNTS ITSELF, for the THIRD time this phase:
        wiring this census into the verify script made .f pass on its own name, because the step
        that runs it contains the words a coverage gate would. Two steps match and both of them are
        this one; the census now excludes itself, and the count including it is published so the
        exclusion is legible. §I9.5's instrument matched its own sweep and §I9.6's evidence artifact
        quoted the checklist text — the standing lesson is to run the generator twice and `--check`
        before committing. (2) The first pass of this cell graded .c, .d and .e on word presence
        over a joined thousand-file corpus, which is the same file-scale error §I9.3 and §I9.6 each
        made once.

        0 of 7 properties present. THE PARENT AND ALL SIX CELLS LEFT `[ ]`: 989 of 1,591 mounted
        paths are never called by a test, 301 route modules have no exercised path at all, three of
        .b's seven schema subjects are not asserted by name, and the three route gates that do run
        ratchet a classification rather than a coverage. -->
- [ ] I9.8 Build real-database and object-store integration tests. Evidence:
      durable run/output/provenance/recall round-trip.
  - [ ] I9.8.a Provision isolated real schema/object store/queue/outbox fixtures
        with migrations, two tenants, encrypted buckets, and deterministic
        cleanup. Evidence: integration harness.
  - [ ] I9.8.b Persist brief-to-plan-to-job/stages/checkpoints/attempts/variant/
        decision with transactional audit/outbox and restart recovery. Evidence:
        durable run test.
  - [ ] I9.8.c Upload/read/hash/cache/quarantine/derive/package output objects
        and verify metadata, permissions, signed URL, corruption, and retention.
        Evidence: object round-trip tests.
  - [ ] I9.8.d Persist
        source/model/workflow/consent/rights/run/output/credential/ release
        lineage and verify forward/reverse/blast-radius queries. Evidence:
        provenance round-trip.
  - [ ] I9.8.e Execute release write/read-back then revoke/quarantine/recall and
        verify database/outbox/search/object/gallery target reconciliation.
        Evidence: recall round-trip.
  - [ ] I9.8.f Inject commit/outbox/publish/object/queue/process failures and
        assert idempotent recovery/no orphaned success/cross-tenant leak.
        Evidence: durability fault tests.
        <!-- MEASURED 2026-08-26 (I9.8.a-f). The parent's evidence is a DURABLE RUN / OUTPUT /
        PROVENANCE / RECALL ROUND-TRIP, so the binding question is asked of the four by name and
        then of the thing that would demonstrate them: does anything run these tests? Instrument:
        `generate-oshun-durable-integration.mts` (+8 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-durable-integration.json`.

        THE SPECS ARE REAL AND NOTHING AUTOMATIC RUNS THEM. There are 144 integration specs across
        Oshun, 123 of them in the BFF, and 85 read a database URL from the environment —
        `OSHUN_V1_DATABASE_URL` in 87 places and `OSHUN_ADMIN_DATABASE_URL` in 80. They open a pool
        against it, migrate, write and read back, and they SKIP rather than pass when there is none,
        which is the honest behaviour for a local unit run.

        A CI STEP THAT PROVISIONS A DATABASE IS NOT A CI STEP THAT RUNS THESE TESTS. Of 106
        workflows, two set a database URL. `oshun-ci.yml` stands up a `postgres:16-alpine` service,
        sets `OSHUN_V1_DATABASE_URL`, applies `prisma migrate deploy` — and then runs the
        persistence EXPLAIN checks. It runs no integration spec. `v1-workbench-kit-verification.yml`
        sets the same variable for a loop of `npx tsx` scans. The only workflow in this repository
        that executes an `*.integration.spec.ts` is `nisaba-ci.yml`, for `libs/nisaba/schemas` — a
        different domain.

        AND THE JOB THAT DOES RUN INTEGRATION TESTS RUNS A TARGET THESE PROJECTS DO NOT DEFINE,
        which makes the absence narrower and sharper than "no integration job exists". `ci.yml`
        stands up its own Postgres, exports `DATABASE_URL` and `DATABASE_URL_DIRECT`, and runs
        `nx affected --target=test:integration`. Exactly 10 projects in the monorepo define that
        target — six Aphrodite apps, `libs/iris/testing`, `libs/maya/engine-core`,
        `libs/uzume/protocol-engines` and `testing/` — and 0 of them is under `apps/oshun`,
        `libs/oshun`, `apps/isis` or `libs/isis`. The BFF's own project declares `test` and
        `test:watch` and no integration target at all. So even the job that provisions a database
        and asks for integration tests asks for them by a name these projects do not answer to, and
        the variables it exports are not the two the 85 specs read. The round-trip the parent's
        evidence names is demonstrated by a person running a command, and by nothing else.

        3 OF THE FOUR ROUND-TRIPS HAVE A SPEC AND PROVENANCE HAS NONE. Four specs are named for a
        run, three for an output, one for a recall, and 0 for provenance or lineage — which is .d's
        whole subject.

        7 OF .a's EIGHT HARNESS SUBJECTS are present; the one missing is TWO TENANTS, so the
        cross-tenant leak .f asks to be refuted has no second tenant to leak to. 9 of .b's ten run
        subjects and 8 of .c's ten object subjects appear.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The specs
        skip rather than pass without a database, which is the difference between an honest local
        guard and a green tick over nothing. (2) Each documents the exact command that runs it,
        migration included, in a header comment, so a person can reproduce a round-trip without
        guessing. (3) The one CI job that provisions a Postgres does apply the migrations first, so
        the EXPLAIN checks it performs run against a real, migrated schema.

        TRAPS. (1) A WORD SOMEWHERE ACROSS 144 SPECS IS NOT AN ASSERTION IN THE ONE THAT ROUND-TRIPS.
        All five of .e's recall targets and all six of .f's faults appear somewhere in the joined
        corpus, and both cells passed on exactly that. Asked of the specs whose own names say they
        ARE a recall or a durability test, it is 1 of 5 and 5 of 6; asked of a lineage spec, .d's
        nine subjects are 0 of 9 because there is no lineage spec. This is the same file-scale error
        §I9.3, §I9.6 and §I9.7 each made once, and it is now the standing shape to check first.
        (2) A spec's own FILENAME is what decides which round-trip it is, because a durability spec
        is named for the thing it round-trips.

        0 of 7 properties present. THE PARENT AND ALL SIX CELLS LEFT `[ ]`: no CI step runs an Oshun
        integration spec, no spec round-trips provenance, the harness has no second tenant, and the
        recall spec that exists reconciles one of the five targets .e names. -->
- [ ] I9.9 Build golden deterministic bundles for each delivery target.
      Evidence: validated build/import/read-back.
  - [ ] I9.9.a Enumerate every supported publishable asset-kind/delivery-target/
        profile/version pair and its bundle consumer/validator. Evidence: target
        coverage manifest.
  - [ ] I9.9.b Create canonical minimal/full/edge fixtures with pinned source
        bytes, dependencies, metadata, accessibility, rights, provenance, and
        expected manifest. Evidence: golden inputs.
  - [ ] I9.9.c Build twice in clean isolated environments with pinned tools/
        locale/time/order/config and compare hashes or declared nondeterministic
        fields. Evidence: determinism report.
  - [ ] I9.9.d Validate schema, files, hashes, formats, technical gates,
        signatures/credentials, target profile, and unsafe-package absence.
        Evidence: validation reports.
  - [ ] I9.9.e Import/consume through the target tool/runtime, read back
        semantic state, and compare expected
        identity/units/material/timing/metadata. Evidence: import/read-back
        tests.
  - [ ] I9.9.f Fail coverage when a supported target pair lacks a current
        golden, pinned validator, declared loss profile, or current proof.
        Evidence: bundle coverage gate.
        <!-- MEASURED 2026-08-26 (I9.9.a-f). The parent's evidence is a VALIDATED BUILD / IMPORT /
        READ-BACK, so the binding question is asked of the pair the cell is indexed by: for each
        supported asset-kind x delivery-target x profile x version, is there a golden that was built
        twice and compared? Instrument: `generate-oshun-golden-bundles.mts` (+11 tests), wired into
        `verify:inventory:v1-workbenches`, writing
        `evidence/v1-workbenches/oshun-golden-bundles.json`.

        THE BUILD IS REAL. `libs/oshun/workbench-kit/tools/golden-round-trip.mjs` is 473 lines that
        resolve a layout from a profile, write artefact bodies, serialise a manifest, archive with
        GNU tar, hash the bytes, and repeat the whole build under shuffled input order, touched
        mtimes, a Turkish locale, a Kiritimati time zone, a 0077 umask and interleaved writes —
        comparing tar HEADERS as well as digests, asserting `localeCompare` disagrees with byte
        order before it starts so the locale axis is known to be live rather than assumed, and
        extracting each archive to rebuild from the extracted bytes byte-identical. 5 of .c's 6
        perturbation axes are driven and the sixth is DISCLOSED: platform cannot be varied on one
        machine, and it is named as never perturbed rather than dropped from the list.

        A BUILD THAT AGREES WITH ITSELF IS NOT A BUILD THAT AGREES WITH A RECORDED ANSWER. Every
        comparison in that runner is `sha256(perturbed)` against `sha256(baseline)`, both computed
        in the same process moments apart by the same code. There are 0 checked-in digests.

        AND THE ONE TOOL THAT PERFORMS THE BUILD IS THE ONE THE CI GLOB DOES NOT MATCH. The kit's
        verification workflow partitions `tools/*-scan.mjs` into a files-only job and a
        needs-a-database job, and refuses an empty partition — a careful design. 197 of the 242
        tools in that directory end in `-scan.mjs`; 45 do not, and `golden-round-trip.mjs` is one of
        them. Its three golden siblings — `build-golden-scan`, `golden-manifest-scan`,
        `golden-runner-scan` and `golden-update-gate-scan` — all end in `-scan.mjs` and all run. An
        nx target `golden-round-trip` is declared in the kit's project.json and 0 workflows name it.
        So the determinism run this cell is about is performed when a person types the command.

        A DECLARED UNION IS NOT THE ONLY SHAPE AN AXIS TAKES. All four of .a's axes are declared,
        and two of them are FIELDS on a profile rather than an enum: 7 golden fixture classes
        (minimal, full, boundary, localized, provenance-rich, legacy-supported, invalid), 2 worked
        profiles, 3 delivery targets carried as `BundleProfile.targets` (wire, archive, lakehouse,
        each with a version range and required slots) and 2 `profileVersion`s. 84 pairs.

        A KIT PROFILE IS NOT A PRODUCT TARGET. 0 modules outside the workbench kit reach
        `BundleProfile` or `WORKED_BUNDLE_PROFILES`, so none of the 84 pairs has the bundle consumer
        or validator .a asks to be enumerated beside it — consistent with §I8.14's finding that the
        only thing in the product spelled `DELIVERY_TARGETS` is a four-member Nyx reminder-channel
        array. 0 importers read a bundle back through a target runtime, so .e's five semantic
        comparisons have nothing to compare.

        4 OF .b's SEVEN GOLDEN INPUTS are pinned — source bytes, rights, provenance and the expected
        manifest — and dependencies, metadata and accessibility are not. 3 OF .d's EIGHT VALIDATION
        SUBJECTS are covered: schema, hashes and the target profile. Files, formats, technical
        gates, signatures and the absence of an unsafe package are not.

        CHECKED AND FOUND CORRECT - recorded so a later pass does not re-flag them. (1) The runner
        is a real build, not a description of one, and its honesty about the platform axis is the
        difference between five axes driven and six claimed. (2) THE GOLDEN MANIFEST IS THE STRONG
        PART: `GoldenEntry` carries a contentHash and a `LicensedSource`, `content-has-drifted` is
        one of eight declared fault kinds, and `golden-manifest-scan.mjs` supplies a real negative
        control — an entry whose `recomputedHash` is deliberately wrong — so the worked manifest's
        agreeing hashes are a positive control rather than a comparison that cannot fail. That
        distinction was checked rather than assumed. (3) The workflow refuses an empty partition,
        which is the failure mode a glob-driven runner is most exposed to.

        TRAPS. (1) A TYPE ANNOTATION CONTAINS BRACKETS: `WORKED_BUNDLE_PROFILES: readonly
        BundleProfile[] = [ … ]` has a `[` before its initialiser, and a reader that scanned to the
        first bracket stopped inside the annotation and reported the declaration absent. (2) A
        DECLARED UNION IS NOT THE ONLY SHAPE AN AXIS TAKES — searching for `DELIVERY_TARGETS` and
        `BUNDLE_VERSIONS` put .a at 1 of 4 when the worked profiles carry both as fields. (3) An
        `importBundle` in another domain is not an import of this bundle: the bare names match an
        offline-bundle service in Lilith mobile and a user-model persistence service in Iris.

        0 of 8 properties present. THE PARENT AND ALL SIX CELLS LEFT `[ ]`: the build is real and
        nothing automatic runs it, it compares against itself rather than a recorded digest, the 84
        pairs it could be indexed by have no product consumer, and no bundle is read back through
        the runtime that would receive it. -->
- [ ] I9.10 Build full-journey Playwright for operator model intake to release.
      Evidence: live backing with fixtures explicitly scoped.
  - [ ] I9.10.a Seed a quarantined model artifact with explicit external source,
        license/model card/provenance, security scan, evaluation fixtures, and
        operator/reviewer roles. Evidence: E2E fixture manifest.
  - [ ] I9.10.b Intake the artifact through the real UI/API/storage path and
        assert quarantine, hash, dependency, scan, and evidence states.
        Evidence: intake journey.
  - [ ] I9.10.c Complete license/provenance review and calibrated evaluations,
        assert blocking failures, correct them through new evidence/version, and
        preserve history. Evidence: review journey.
  - [ ] I9.10.d Promote the exact version to an approved environment with named
        approval, then select it in an admitted workflow and dispatch a run.
        Evidence: promotion/dispatch journey.
  - [ ] I9.10.e Compare output, inspect lineage/gates/cost, approve a locked
        candidate, assemble/write/read-back a target release. Evidence: release
        journey.
  - [ ] I9.10.f Assert direct bypasses, stale evidence, wrong role/tenant,
        secret/property leak, accessibility, and immutable trace/audit across
        the journey. Evidence: full Playwright trace.
      <!-- I9.10 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-intake-release-journey.mts`
      (evidence `evidence/v1-workbenches/oshun-intake-release-journey.json`,
      10 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: A JOURNEY IS A WALK, AND NOTHING HERE WALKS. The six children
      are six STAGES of one walk, not six subjects — .f's "immutable
      trace/audit ACROSS THE JOURNEY" cannot be asserted by a spec that only
      saw one stage, and .c's "correct them through new evidence/version, and
      preserve history" needs the artifact .a seeded to still be the subject
      three stages later. So the measurement is how many of the six stages any
      SINGLE spec reaches. Across all 995 e2e specs in the estate the answer is
      TWO, and only two specs manage it: `operator-editorial-review-approval`
      (review + release) and `tenant-integrations-api-keys-webhooks-bff`
      (release + negatives). Neither has a model artifact as its subject, and
      neither touches intake. Every spec that does touch model intake reaches
      exactly one stage.

      .a — 3 specs name quarantine and NONE is a model intake path: a studio
      quality/reliability sweep, a V2 moderation console, and a Neith CI
      insights dashboard. A SPEC NAMED FOR A SUBJECT IS NOT A SPEC THAT WALKS
      IT.

      .b — the surface the journey would have to walk EXISTS AND IS MOUNTED:
      `apps/isis/web/src/pages/ModelsPage.tsx`, routed at `models` in
      `App.tsx`, naming quarantine, with `model-scan-utils.ts` carrying seven
      scan verdicts (clean, failed, infected, skipped, unknown, unrecognized,
      unsupported). It has 0 e2e specs. The entire Isis web e2e suite is 13
      generation control panels — kitbash packs, turnaround sheets, terrain,
      vehicles. And the one spec anywhere about external model intake,
      `studio-isis-civitai-integration.spec.ts`, exists to assert the surface
      is UNBOUND: `mode: 'unbound_empty_projection'`, `producer: 'none'`,
      `persistence: 'none'`, `authorityBinding: 'not_configured'`,
      `authoritativeState: false`. That is an honest fail-loud seam and the
      spec is a correct lock on it — it is simply not an intake journey,
      because there is no intake to journey through.

      .d — A CHILD THAT NAMES TWO ACTS IS NOT EARNED BY ONE. 5 specs promote to
      an environment; 0 of them dispatch a run. Exactly 1 contains the word
      "dispatch" and it is `dispatchEvent` firing `pointerdown` in a Yemaya
      drawing smoke test. `dispatchEvent` IS NOT DISPATCHING A RUN, and the
      matcher in the instrument excludes it.

      .e — 36 specs read back or assemble something; 0 of them also inspect
      lineage AND cost, which is half of what the child asks before it gets to
      approving a locked candidate.

      Four of the cell's named states are asserted by NOTHING, each recorded
      with a POSITIVE CONTROL on the same corpus so an all-zero column and a
      broken matcher cannot look identical: locked candidate 0 (control
      "candidate" 46), stale evidence 0 (control "evidence" 244), SBOM /
      bill-of-materials 0 (control "dependency" 14), named approval 0 (control
      "approve" 113).

      THE PARENT'S OTHER HALF HOLDS. "Live backing with fixtures explicitly
      scoped" is real where specs exist: of 98 admin e2e specs exactly ONE
      intercepts the network (`redesign-incident-signoff.spec.ts`). The suite
      is live-backed. What is missing is the journey, not the backing.

      TWO TRAPS WORTH KEEPING, both of which produced a wrong number here
      first. `\|` INSIDE AN ERE IS A LITERAL PIPE: a first pass scanned this
      corpus with `grep -E 'read-back\|readBack'` and reported 0 for every
      multi-alternative term when the true count is 36, and `licence` and
      `lineage` were likewise reported absent when they are in 11 and 40 specs.
      Every matcher in the instrument is a real alternation and a test asserts
      none contains `\|`.

      And A URL IS NOT A COMMENT. The first comment stripper treated the `//`
      in `'http://127.0.0.1:3020'` as the start of a line comment and deleted
      the rest of the line, which removed both front-end ports from the one
      spec in the estate that names two of them. The stripper is now
      string-aware and a test locks it. Correcting it moved this cell's stage
      counts (.c 4->6, .e 31->36, .f 22->36) and three controls, and moved
      nothing that the grading rests on: the deepest walk is still two of six
      and no child is earned. -->
- [ ] I9.11 Build full-journey Playwright for Yemaya brief to gallery promotion.
      Evidence: cross-host lineage assertions.
  - [ ] I9.11.a Seed authorized Yemaya project/shot/assets/roles and live or
        explicitly hermetic Isis capabilities with rights/provenance. Evidence:
        fixture manifest.
  - [ ] I9.11.b Author and validate a typed brief in Yemaya, hand off exact
        context to Isis, preview/confirm the plan, and dispatch. Evidence:
        outbound handoff test.
  - [ ] I9.11.c Observe queue/run/stages, return to exact Yemaya context,
        inspect generated proposal/output, and accept a project revision.
        Evidence: return journey.
  - [ ] I9.11.d Promote the exact Yemaya output into an Oshun curated derivative
        and complete editorial review without inheriting AAA authority.
        Evidence: editorial boundary test.
  - [ ] I9.11.e Assert immutable project/shot/brief/plan/run/output/original/
        derivative/decision lineage, rights, gates, and audit across hosts.
        Evidence: cross-host lineage assertions.
  - [ ] I9.11.f Exercise expired/deleted context, generation failure, stale
        output, wrong tenant/role, back navigation, accessibility, and no
        duplicated operator controls. Evidence: negative E2E matrix.
      <!-- I9.11 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-brief-gallery-journey.mts`
      (evidence `evidence/v1-workbenches/oshun-brief-gallery-journey.json`,
      10 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: A CROSS-HOST ASSERTION NEEDS TWO HOSTS, AND NO SPEC IN THE
      ESTATE OPENS A SECOND ONE. The parent's entire evidence clause is
      "cross-host lineage assertions", and .e spells out what must survive the
      crossing — project, shot, brief, plan, run, output, original, derivative,
      decision, "across hosts". That is not a vocabulary question but a
      question about whether one Playwright context ever drives two application
      front ends, because a lineage assertion that never leaves one origin is
      an assertion about one host. Of 995 e2e specs, 346 bind a host by some
      spelling and 0 name a second. Exactly one spec names two front-end ports
      — `phase-5-6-operator-review-queue` pairing Oshun web (3010) with Oshun
      admin (3020) — which is one product's two consoles, not the Yemaya ->
      Isis -> Oshun span this cell describes. `cross-host` itself appears in 0
      specs against a live control of 99 for "host".

      .b — AN EVIDENCE ARTIFACT IS NOT A PRODUCT CONTRACT. The typed generation
      brief is real and carefully specified: 11 facet interfaces
      (BriefIdentity, BriefIntent, BriefAudience, BriefAssetKind, BriefLocale,
      BriefReference, RightsClaim, BriefSafety, QualityTarget, BriefBudget,
      BriefDelivery) with 8 validators, from I1.4. It lives at
      `evidence/v1-workbenches/generation-brief/schema.ts`, and 0 files under
      `apps/` or `libs/` name it. There is no typed brief in Yemaya to author,
      so there is no handoff to preview, confirm or dispatch.

      .a/.c/.d — Yemaya's entire e2e footprint in the estate is TWO specs:
      `apps/yemaya/studio-web/e2e/studio-web.smoke.spec.ts` (1,727 lines, 24
      test titles — welcome page, dark mode, a BIM scene review, a scene-graph
      lighting workspace, colour references, a Disney timing chart, HDR trims,
      a PM dashboard, translation memory; 0 of the 24 names a brief) and
      `apps/oshun/web/e2e/studio-yemaya-budget-godot.spec.ts`. Neither seeds a
      project for a journey, and a return to a Yemaya context needs a departure
      no spec makes.

      .f — A PHRASE DISCUSSED IN A COMMENT IS NOT AN ASSERTION. "no duplicated
      operator controls" matches 3 specs in the raw corpus and 0 once comments
      are stripped: all three are prose. Both numbers are recorded, and the
      grading uses the stripped one.

      A TRAP THIS CELL PAID FOR: A URL IS NOT A COMMENT. The first comment
      stripper treated the `//` in `'http://127.0.0.1:3020'` as a line comment
      and deleted the rest of the line, which removed both front-end ports from
      the one spec in the estate that names two and reported the cross-host
      port measure as 0 instead of 1. The stripper is now string-aware in this
      instrument and in I9.10's, and a test in each locks it. -->
- [ ] I9.12 Build full-journey Playwright for curated card generation and
      editorial promotion. Evidence: tier boundary assertions.
  - [ ] I9.12.a Seed curated/contemplative and editorial actors, approved card
        capabilities/presets, provider-gated/live states, and destination
        collection. Evidence: E2E fixture manifest.
  - [ ] I9.12.b Complete one representative text/image, audio, temporal, and 3D
        curated briefing with only approved high-level controls. Evidence:
        curated card journeys.
  - [ ] I9.12.c Validate estimate/confirmation/dispatch/progress/failure/output
        gallery states and audience-safe errors/provenance. Evidence: generation
        journey.
  - [ ] I9.12.d Select an exact output and request editorial promotion; complete
        separate reviewer decision and verified collection release. Evidence:
        promotion journey.
  - [ ] I9.12.e Assert raw graph/model/provider/sampler/node/LoRA/private prompt
        data is absent from UI, payloads, URLs, bundles, errors, and telemetry.
        Evidence: tier property leak assertions.
  - [ ] I9.12.f Assert crafted hidden fields/direct routes/deep links/tier
        switch/ stale output/cross-tenant attempts fail, with accessible
        responsive journey. Evidence: boundary Playwright matrix.
      <!-- I9.12 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-curated-card-journey.mts`
      (evidence `evidence/v1-workbenches/oshun-curated-card-journey.json`,
      20 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: THE TIER IS AN ATTRIBUTE, NOT A GATE. The parent's whole
      evidence clause is "tier boundary assertions", and .e names what the
      boundary must hold back — raw graph, model, provider, sampler, node, LoRA
      and private prompt data, absent from UI, payloads, URLs, bundles, errors
      and telemetry. Unlike I9.11, the subject here is real and good: ten
      approved cards in a server-authoritative catalog, per-card validators, an
      entitlement gate, a raw-graph leak auditor, a fail-closed BFF route with
      twelve route tests (nine of them refusals), and a real executor wired at
      `server.ts:2929`. What is missing is the walk and the boundary.

      The customer-facing generation gallery reads the caller's tier exactly
      ONCE, and writes it into a `data-gallery-tier` attribute. Zero conditionals
      branch on it. Its filter rail ships `data-filter-model` and a
      `data-branch-param="guidance"` (the CFG scale) to every caller, plus
      generator / workflow-class / tenant / entitlement-tier inputs, and the
      per-output panel prints Workflow class, Model and Generator. The §24.11
      boundary that would refuse a tier is keyed on `/studio/isis/`, and 0 of the
      5 curated generation routes are under it. The card shell itself prints
      `Workflow class: <id>` and `Provider: <key> / <region>` on every card.

      AND NOBODY WALKS IT. Of the five curated generation front ends, TWO are
      named by any Playwright spec: `/studio/generation/living-scene` (one spec)
      and `/studio/generation-gallery` (two). The curated-cards studio hosting
      six of the ten cards is named by ZERO, as are the music and nyx-3d
      studios. 0 of the 10 approved card kinds is briefed by a spec, so 0 of
      .b's four modality classes — text/image, audio, temporal, 3D — is walked.

      .e — the estate's entire stock of tier leak assertions is FOUR expect()
      calls, lines 100-103 of `living-scene-card.spec.ts`, against innerHTML, on
      a card that is not in the catalog: `not.toContain` of lora, scheduler,
      workflowgraph, comfy. One of .e's six surfaces is covered and five are
      empty. The honest denominator: 2,788 absence assertions of every kind live
      in the corpus. AN OPERATOR PANEL HIDDEN FROM AN UNAUTHORIZED VIEWER IS NOT
      A CURATED SURFACE PROVEN CLEAN — 38 more absence assertions naming raw
      machinery run across 13 Isis specs (lora-training, comfyui-nodes,
      model-registry, provider-factory...), and they ask whether the OPERATOR
      route refuses a principal, which is the opposite question about a
      different surface. The populations are counted apart, not merged. A
      further 28 matches are incidental: `\bprompt\b` matches
      `data-pwa-install-prompt`, so A PWA INSTALL PROMPT IS NOT A GENERATION
      PROMPT — reported as one number the non-curated population was 66 and
      looked like coverage.

      .c/.d — AN INSPECTOR IN ITS IDLE STATE IS NOT AN OUTPUT INSPECTED. The two
      gallery specs select 0 outputs and assert 1 of the 6 provenance states the
      panel can reach: `idle` ("Select an output to inspect"). They cannot do
      better: `bindGalleryStore` and `bindGalleryContext` are the gallery's only
      way to hold records and their sole callers are in `gallery.test.tsx` —
      A DESTINATION BOUND ONLY BY ITS OWN TEST, while the loader's docstring says
      production wires it. And A BUTTON WITH NO HANDLER IS NOT AN ACTION: 4 of
      the gallery's 6 action buttons — launch-branch, send-to-editorial,
      send-to-asset-library, takedown — carry no `onClick` at all. The estate's
      one promotion verb, `send-to-editorial`, is an operator-admin bulk action
      in the admin store that 0 e2e specs call.

      .a/.f — all 10 catalog cards are allowlisted to every tenant with the kill
      switch off and a single minimum tier, so there is no provider-gated state
      to seed and no cross-tenant refusal to exercise. 3 of .f's 7 negative
      classes (crafted hidden fields, direct routes, tier switch) are covered by
      the BFF route suite; deep links, stale output, cross-tenant and the
      accessible responsive journey are covered by nothing, and 0 classes by a
      Playwright matrix. A ROUTE TEST IS NOT A PLAYWRIGHT JOURNEY.

      FIXED: A PENDING PRICE PRINTED AS ¢0.00 IS A FABRICATED ESTIMATE. The
      catalog ships `PENDING_COST_COEFFICIENTS` on purpose and exports
      `areCostCoefficientsPending` to recognise them; the predicate had ONE
      caller in the estate and it was its own test. All three studios
      (curated-cards, music, nyx-3d) passed the sentinel to `CardShell`, which
      formatted it as `Est. ¢0.00 · 0.0s mean (0.0s p95)` — a free, instant
      generation stated to two decimal places — while the catalog test carried
      the comment `(UI shows "pending")`. The shell now renders `Est. pending`
      with `data-preflight-estimate="pending"`; two tests lock it, both confirmed
      RED against the old shell, and the control proves a published 0.5c/s x 4s
      still prints ¢2.00.

      TRAPS THIS CELL PAID FOR. A SUBSTRING IS NOT THE IDENTIFIER: matching the
      catalog cardId with `includes` credited the illustration card with a
      briefing, because an admin workflow-template example is called
      `curated-illustration-prompt`. Anchors are now identifier-bounded on both
      sides with hyphen as a word character, so `curated-video` no longer
      swallows `curated-video-from-image`. A WORD IN A SPEC THAT WALKS ANOTHER
      CARD IS NOT A BRIEFING OF THIS ONE: the loose measure credited `video` to
      the Living Scene spec, which says "video" about an audio-description
      track; the loose count is kept as a labelled control. A SUBJECT NAMED ON
      ANOTHER LINE IS STILL THE SUBJECT: classifying `expect(html).not.
      toContain('lora')` line-at-a-time returned `unclassified` and reported all
      six of .e's surfaces as zero, hiding the one thing the estate has — the
      binding two lines up decides it. And EVALUATE THE CATALOG, DO NOT GREP IT:
      counting `tenantAllowlist: 'all'` literals reported ONE entry allowlisted
      to all tenants, because a single factory writes the field once for all ten;
      the instrument now imports the catalog and holds the values. `lora` is a
      substring of `exploration` (24 specs unanchored, 5 with a word boundary),
      so every property token is word-boundary matched. -->
- [ ] I9.13 Run WCAG 2.2 AA automation over critical routes and semantic
      alternatives. Evidence: reports for themes/viewports/locales.
  - [ ] I9.13.a Maintain a critical-route/state/action/role inventory covering
        operator, AAA, curated, gallery, errors, dialogs, live updates, media,
        graph, and 3D alternatives. Evidence: accessibility coverage manifest.
  - [ ] I9.13.b Run automated rule scans after stable load and key interactions
        for every supported light/dark/high-contrast theme. Evidence: theme
        reports.
  - [ ] I9.13.c Run keyboard-only focus/order/visibility/trap/shortcut/drag
        alternative and screen-reader name/role/value/status announcement tests.
        Evidence: interaction reports.
  - [ ] I9.13.d Run narrow/mobile/tablet/desktop and 200%/400% zoom/reflow/
        orientation/touch-target checks without horizontal two-dimensional
        scrolling except essential content. Evidence: viewport reports.
  - [ ] I9.13.e Run default/long-text/RTL/CJK/number/date/unit locale cases plus
        reduced motion and browser text spacing overrides. Evidence: locale
        reports.
  - [ ] I9.13.f Verify noncanvas semantic alternatives provide equivalent
        inspect/comment/compare/decide operations for media/graph/3D. Evidence:
        parity journeys.
  - [ ] I9.13.g Triage every violation with owner/severity/route/state/artifact,
        block applicable release issues, and retain current reproducible
        reports. Evidence: zero-blocker accessibility gate.
      <!-- I9.13 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-wcag22-run-coverage.mts`
      (evidence `evidence/v1-workbenches/oshun-wcag22-run-coverage.json`,
      16 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 7 children earned; every box stays unchecked.

      THE VEIN: A REGISTER OF THE HARNESS IS NOT A REPORT FROM A RUN. Five of
      the seven children begin with "Run" and each names its evidence a REPORT
      — theme, interaction, viewport, locale, parity. The estate has something
      rare here: a careful accessibility REGISTER from §S11.3 — 8 axe harness
      sites with engine bindings, tag selections, impact policies and
      exclusions, a WCAG 2.2 criterion map, a findings register, a waiver
      register, and a matrix that separates a row a machine can fill from a row
      that needs a person. None of that is a run. Across the five axes this cell
      names, the answers are 0, 0, 3-of-4, 0 and 0.

      A SCAN CONFIGURED FOR 2.1 CANNOT REPORT A 2.2 FAILURE. axe-core 4.11.4
      ships exactly ONE rule tagged `wcag22aa` — `target-size`, SC 2.5.8 — and
      ships it `enabled: false`, so an untagged run evaluates 0 of 1 and only a
      tag selection naming `wcag22aa` turns it on. The DEFAULT gate, the
      `axeAfterEach` fixture 620 Oshun specs inherit, selects
      `['wcag2a','wcag2aa','wcag21a','wcag21aa']`. 2 of the 8 registered
      harnesses select 2.2 and both are opt-in helpers. Of the three specs named
      `wcag-aa-signoff`, two pass the 2.1 list back into the 2.2-capable helper
      by hand; the third (admin, 3512) inherits the default and is the estate's
      only sign-off that evaluates `target-size` — under a title that reads
      "WCAG 2.1 AA signoff". None of this is dishonest: those specs were built
      for a 2.1 TODO and say so. It is simply not 2.2.

      .a — A SIGN-OFF OVER TWELVE SHELL SURFACES IS NOT AN INVENTORY OF TEN
      STATE CLASSES. The three sign-offs enumerate 12 surfaces (Home, Library,
      Search, Notifications, Settings, six domain entries, and the admin
      dashboard/inbox/operators/review-queue/audit/compliance/reports) against
      719 member-app routes. 9 of the 10 classes .a names appear in none of
      them; the one that does is `operators`. Of 646 Oshun e2e specs, 54 run a
      scan, and AAA, curated, live-update, media, graph and 3D-alternative
      states are named by 0 of them.

      .b — A THEME NOBODY SETS CANNOT BE SCANNED. The product ships three
      variants (`cream`, `dusk`, `cream-hc`). `cream-hc` appears in 0 specs.
      `forcedColors` emulation: 0. And 0 specs run a scan inside the test that
      sets a variant — the file-scope control says 1, and that one spec scans in
      the test at line 60 and switches to dusk in the test at line 284.

      .c/.d/.e — 49 specs drive the keyboard, 0 name a drag alternative (WCAG
      2.2 SC 2.5.7), 0 assert focus-visible. Scans run at 3 of 4 viewport
      classes — 320x720, 390x844, 1440x960, 1600x960 — with no tablet width at
      all; 1 spec touches zoom or device scale, 1 names reflow. The 5 locales
      passed to Playwright's `locale` option are de/en/en-US/es/fr, all Latin
      and left-to-right; 2 specs reach RTL another way, 1 names `ja-JP`, 0
      override text spacing, and 0 of the 13 locale-setting specs runs a scan.

      .f/.g — 1 spec names a semantic alternative and 0 of .f's four operations
      (inspect/comment/compare/decide) is paired with one. The gate fails on 2
      of axe's 4 impact levels and reads an unset impact as `moderate`, the
      level it passes. 0 axe report artifacts are retained in the repository.

      FIXED: the estate's own `axe-policy-scan` was RED. It re-derives its reach
      counts every run and fails on drift — by design — and commit `62564a5ca6`
      added a 561st web spec (and a 51st scan call site across a 21st file)
      under a register that still read 560 and "50 call sites across 20 files".
      It runs on every push in `v1-workbench-kit-verification.yml` and is not on
      that workflow's KNOWN_FAILING list, so CI had been failing on it. Three
      reach strings are current and the scan reports "the register agrees with
      the engine and the source, 17 negative controls detected". A REGISTER THAT
      RECORDS A COUNT GOES STALE, which is exactly why that one re-derives.

      TRAPS THIS CELL PAID FOR. A HELPER IS NOT A SPEC: the first `withTags`
      census ran over `*.spec.ts` only, found one call site in the whole estate,
      and would have reported that nothing in Oshun ever asks for 2.2 — both
      Oshun helpers do, in `e2e/support/accessibility.ts`, which is not a spec
      file. A VERSION TAG AND A CRITERION TAG SHARE A PREFIX: axe encodes
      success criteria as `wcag<major><minor><criterion>`, so `wcag222` is SC
      2.2.2 Pause Stop Hide, a WCAG 2.0 rule — a `/^wcag22/` prefix test
      reported FIVE 2.2 rules with three enabled by default and inverted the
      cell's central fact. A FILE IS NOT A TEST (the dusk measure above). A
      PLURAL IS NOT A WORD-BOUNDARY MATCH: `\boperator\b` rejects `operators`,
      so the one covered inventory class read as zero. A LOCALE PREFIX IS A
      COMMON IDENTIFIER FRAGMENT: a bare `\bar-` matched `data-isis-ar-boundary`
      and reported an Isis analytics panel as an RTL spec. And `axe` is a
      substring of `relaxed` and `maxed`: across the 995-spec e2e corpus an
      unanchored search finds 302 and a word-bounded one finds 127. -->
- [ ] I9.14 Run performance and load profiles for comparison, large galleries,
      queue views, and high-volume jobs. Evidence: budgets pass.
  - [ ] I9.14.a Ratify client/server/end-to-end latency, interaction, memory,
        CPU, network, payload, query, throughput, concurrency, and error budgets
        per scenario/device/network. Evidence: performance budget spec.
  - [ ] I9.14.b Profile image/video/audio/3D comparison
        load/sync/scrub/annotation with large candidates and semantic
        alternatives. Evidence: comparison profile.
  - [ ] I9.14.c Profile gallery search/filter/pagination/preview/lineage/
        virtualization with production-scale assets/collections/metadata.
        Evidence: gallery profile.
  - [ ] I9.14.d Load queue/dashboard/run explorer live updates, reconnect,
        filtering, pagination, and operator actions at production job/event
        volume. Evidence: queue view profile.
  - [ ] I9.14.e Load
        plan/dispatch/worker/provider/callback/gates/storage/outbox/
        search/release paths with high-volume mixed jobs/failures/cancels.
        Evidence: backend load report.
  - [ ] I9.14.f Measure backpressure/fairness/tail latency/resource/cost/error/
        stale behavior and stop tests before unsafe host pressure. Evidence:
        resource-safe test logs.
  - [ ] I9.14.g Compare against ratified budgets, capture profiles/traces/query
        plans, assign regressions, and rerun to current passing reports.
        Evidence: performance gate.
      <!-- I9.14 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-performance-load-profiles.mts`
      (evidence `evidence/v1-workbenches/oshun-performance-load-profiles.json`,
      10 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 7 children earned; every box stays unchecked.

      THE VEIN: A PAGE LOAD IS NOT A LOAD, AND THERE IS NO LOAD GENERATOR HERE.
      Four of the seven children ask for something to be put UNDER LOAD — large
      candidate sets, production-scale galleries, production job and event
      volume, high-volume mixed jobs with failures and cancels. The estate's
      entire performance apparatus is Lighthouse, and a real one: a committed
      per-scenario budget file, numeric assertions, and a BLOCKING
      `lighthouse-web` job in `oshun-ci.yml` that fires whenever `@oshun/web` is
      affected. Lighthouse loads a page three times and measures the paint. Of
      eight known load generators — k6, @grafana/k6, autocannon, artillery,
      loadtest, clinic, wrk, vegeta — 0 are declared in any package manifest and
      0 are installed. The gap in .d/.e/.f is one missing tool, not four missing
      efforts.

      .a — A BUDGET PER SCENARIO IS NOT A BUDGET PER DEVICE AND NETWORK.
      `lighthouse-budget.json` is better than a first read suggests: three path
      patterns each with their own tightened numbers (`/domains/nisaba` gets FCP
      1500 where `/*` gets 1800). What it has one of is the other two axes — one
      device preset (`desktop`) and one throttling profile (rtt 150ms,
      1638.4kbps, 4x CPU), across both apps. 6 of .a's 12 dimensions carry a
      number anywhere: client latency, end-to-end latency, interaction, CPU,
      network and payload. Server latency, memory, query cost, throughput,
      concurrency and error budgets have none. The V3 first-load budget does
      carry two device/network profiles (M1 Mac broadband, Pixel 7 cellular)
      with recorded p95 measurements — one dimension, and for V3.

      .b/.c/.d — 8 routes are profiled (3 web, 5 admin). 0 are a comparison
      surface, 0 are a gallery, and 4 are queue-shaped: the member home
      dashboard and the admin review, moderation and incident queues. AND A PAGE
      LOAD OF AN EMPTY QUEUE IS NOT A PROFILE AT PRODUCTION VOLUME: neither
      Lighthouse config and neither Lighthouse workflow contains a seed step, a
      database service or a fixture load, so what those four measure is the
      pages with nothing in them.

      .f — 0 Oshun tests read host pressure, though CLAUDE.md makes it an
      operating rule for humans and agents. There is no load to measure
      backpressure, fairness or tail latency under.

      .g — the blocking client gate is real and the only Lighthouse workflow
      that auto-triggers is the admin one (the web workflow was deliberately
      demoted to `workflow_dispatch` so the identical budget check does not run
      twice per PR). What is not retained is any run output: 0 profile, trace or
      flamegraph artifacts and 0 captured query plans across `apps/oshun` and
      `libs/oshun`. The error-budget half of .g is settled elsewhere — I9.3
      measured the SLO and error-budget apparatus and earned 0 of 8 — and this
      report reads that count out of that cell's committed evidence rather than
      re-deriving it.

      TRAPS THIS CELL PAID FOR. AN ARRAY OF 501 ITEMS IS NOT 501 CONCURRENT
      REQUESTS: counting bare `Array.from({ length: N })` reported the estate's
      largest load level as 501, and it is a 501-element request BODY in a
      reminders route test. A fan-out now counts only when the literal-length
      array is built inside a `Promise.all` that issues a request — which makes
      the declared fan-out 0 — and the dynamic form
      (`Promise.all(rows.map(...))`, 23 sites) is reported beside it, because
      reporting only the literal form would say "zero concurrent requests" about
      an estate that has some. AND READ THE WHOLE BUDGET FILE: the first forty
      lines show one `/*` entry, and the cell was one edit away from recording
      "a budget for one path pattern" about a file that carries three. -->
- [ ] I9.15 Run adversarial prompt, graph, upload, SSRF, provenance, auth,
      tenant, spend, and fixture scans. Evidence: zero release blockers.
  - [ ] I9.15.a Test prompt/reference/import/retrieved-content injection against
        system-policy separation, tool grants, data exfiltration, hidden
        control, and approval claims. Evidence: prompt adversarial suite.
  - [ ] I9.15.b Test graph/node/subgraph/parameter/custom package injection,
        unsafe deserialization/code, dependency confusion, cycles, resource
        abuse, and sandbox escape. Evidence: graph suite.
  - [ ] I9.15.c Test media/archive/model/package/document uploads for malware,
        parser bombs, traversal/symlink, polyglot/spoofed type, oversize, and
        metadata/script payloads. Evidence: upload suite.
  - [ ] I9.15.d Test URL/provider/webhook/deep-link/import fetch paths for SSRF,
        DNS rebinding, redirects, credential metadata, internal networks,
        scheme/ port/host bypass, and response bombs. Evidence: SSRF suite.
  - [ ] I9.15.e Test provenance hash/signature/manifest/ingredient/watermark/
        ledger stripping, substitution, collision, replay, and false recovery.
        Evidence: provenance suite.
  - [ ] I9.15.f Test authentication/session/API key, route/function/object/
        property authorization, mass assignment, enumeration, CSRF, replay,
        step-up, and confused deputy. Evidence: auth suite.
  - [ ] I9.15.g Test two-tenant identifiers/cursors/search/counts/cache/blob/
        queue/events/logs/export/recall timing and content isolation. Evidence:
        tenancy suite.
  - [ ] I9.15.h Test quota/budget/reservation/cost/priority/batch/cache/refund/
        marketplace/chargeback evasion and concurrency. Evidence: spend suite.
  - [ ] I9.15.i Scan fixture/demo/fallback/placeholder/mock state for production
        reachability, unlabeled data, simulated success, and release
        eligibility. Evidence: fixture honesty suite.
  - [ ] I9.15.j Triage by exploitability/impact with reproducible evidence,
        owners, fixes, regression tests, and zero unresolved release blockers.
        Evidence: security gate report.
      <!-- I9.15 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-adversarial-matrix.mts`
      (evidence `evidence/v1-workbenches/oshun-adversarial-matrix.json`,
      10 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 10 children earned; every box stays unchecked.

      THE VEIN: A VERB IS NOT AN ATTACK, AND `traversal` IS USUALLY A KEYBOARD.
      The cell names roughly eighty attack classes across ten suites, and the
      tempting measurement is to grep the corpus for each word. That method is
      worthless here and the corpus proves it: 21 test titles say "traversal"
      and three are a path escaping a directory — the rest are KEYBOARD
      traversal and tree walks. 79 say "enumerat" and two are account
      enumeration — the rest are `lists the geography enumerations`. Every probe
      in the matrix therefore comes in two forms, a STRICT one requiring the
      attack's context and a LOOSE one that is the naive word, and both counts
      are reported per class because the difference IS the finding. Every family
      is overstated by the naive word, and a test locks that.

      The population is test TITLES, not files: 74,045 of them across 7,679 test
      files in apps/oshun, libs/oshun, apps/isis, libs/isis and tests/security.
      A title is where a test says what it tests, and "the word appears
      somewhere in this 900-line file" has been the wrong denominator four times
      in this phase.

      AND A SUITE OUTSIDE THE APP TREE IS STILL A SUITE. `tests/security/` sits
      at the repository root and holds a real, good adversarial suite — 6 files,
      47 titles, over cross-domain token audiences, per-domain API-key vaults,
      tenant-scoped queries including the prefix-collision case, a PII scanner
      and a secret-shaped-string scan. An `apps/**`-scoped corpus would have
      missed all of it and reported .f and .g as empty. They are not empty.

      THE RESULT: 52 of 75 attack classes are named by a test title, and the
      shape of the gap is the point. The estate is strongest exactly where a
      web product is normally strongest — .f auth 10/10 and .i fixture honesty
      4/4 — and thinnest exactly where THIS phase is named: .a prompt
      adversarial 3/7 (no retrieved-content injection, no system-policy
      separation, no tool-grant abuse), .b graph 4/8 (no graph/node injection,
      no dependency confusion, no custom-package injection, no unsafe code
      execution), .h spend 5/9, .g tenancy 6/10, .e provenance 6/9, .c upload
      6/7 (no parser bomb), .d SSRF 6/7 (no response bomb), .j gate 2/4.

      .f IS THE CLOSEST AND STILL NOT EARNED, for a reason worth stating: A
      TITLE NAMING AN ATTACK IS NOT AN ADVERSARIAL TEST. The matrix measures
      whether the estate has SOMETHING pointed at each class, not whether that
      something attacks — and .j, the gate that would certify a suite complete,
      names two of its four classes. Every class in the artifact carries an
      example title so a reader can check the claim rather than take it.

      TRAPS THIS CELL PAID FOR, and it paid for many. A PORT IS A SUBSTRING OF A
      REPORT (also support, export, transport, important): the unanchored
      scheme/port/host probe reported 141 SSRF bypass tests, and the first
      example was "reports block mode when cost is unavailable". A CROSS IS A
      SUBSTRING OF ACROSS: every tenancy probe matched "moves the cursor across
      rows". `search` is a substring of `research`, `count` of `counterclaim`
      and `account`, `cost` of `costume`, `log` of `catalog`. A REVIEW CYCLE IS
      NOT A GRAPH CYCLE, a FALSE-POSITIVE RATE is not a FALSE RECOVERY, a
      MISSING config is not a STRIPPED manifest, an IDENTITY collision is not a
      HASH collision, a REFUND FEATURE is not CHARGEBACK EVASION, and "fires the
      simulator successfully" is not a SIMULATED SUCCESS. Three tightening
      rounds took the count from 60 to 52.

      AND TWO TRAPS IN THE INSTRUMENT ITSELF. A DOUBLED BACKSLASH IS A LITERAL
      BACKSLASH: a patch script that emitted `\\b` into the probe table produced
      regexes matching a literal backslash-b, which no title contains, and three
      families silently read zero; a test now compiles every probe and rejects
      one. A TABLE-DRIVEN TEST STILL HAS A TITLE: 91 files use
      `it.each([...])('title', ...)`, the first extractor expected the quote
      immediately after the opening paren, and every one of those titles was
      dropped — the argument list is now skipped by balancing brackets, which
      recovered 277 titles. -->
- [ ] I9.16 Re-run generation-tier and Studio-boundary suites. Evidence: no raw
      machinery is reachable in contemplative Oshun Studio.
  - [ ] I9.16.a Enumerate contemplative/curated/AAA/operator host routes,
        operations, controls, contracts, capabilities, bundles, and expected
        tier availability from canonical policy. Evidence: boundary manifest.
  - [ ] I9.16.b Crawl/render contemplative Oshun Studio routes and bundles for
        raw workflow/model/provider/node/sampler/LoRA/graph/training/fleet/
        operator identifiers and commands. Evidence: static/runtime leak scan.
  - [ ] I9.16.c Send direct/guessed/linked/aliased routes and crafted
        query/body/ nested/unknown fields for every higher-tier operation.
        Evidence: route/ function authorization tests.
  - [ ] I9.16.d Test capability/health/error/not-configured/degraded/telemetry/
        export/deep-link/search/autocomplete channels for hidden feature detail.
        Evidence: side-channel tests.
  - [ ] I9.16.e Verify approved curated cards map only high-level inputs through
        governed APIs and AAA surfaces remain Yemaya-scoped. Evidence: positive
        tier journeys.
  - [ ] I9.16.f Test actor tier/entitlement change mid-session, stale client
        state/cache, back navigation, service worker/offline cache, and revoked
        links. Evidence: dynamic boundary tests.
      <!-- I9.16 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-studio-tier-boundary.mts`
      (evidence `evidence/v1-workbenches/oshun-studio-tier-boundary.json`,
      11 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: THE BOUNDARY IS NAMED FOR ONE DOMAIN AND THE STUDIO HOSTS
      TWENTY. The §24.11 boundary is genuinely good and better than its own
      documentation says. `STUDIO_ISIS_ALLOWED_ROUTE_SEGMENTS` is EMPTY and
      `isAaaOnlyRoute` returns true for every segment not in it, so all 87
      segments mounted under `/studio/isis/` are fail-closed for a contemplative
      session — AN INVERTED ALLOWLIST IS NOT A DENYLIST WITH GAPS, and this cell
      nearly recorded the opposite. What the boundary does not reach is the
      other 416 Studio routes: the member app mounts 504 pages under `/studio/`
      and 88 are under the governed prefix. `aja`, `bellona`, `hathor`, `neith`,
      `nous`, `concordia` and more are outside it, as is
      `/studio/generation-gallery`, which I9.12 measured shipping a Model
      filter, a Generator filter and a guidance-scale override with no tier
      condition on them.

      .a — the boundary matrix (BASE.5.e) is real, passes, and enumerates 31
      route segments for ONE host (Yemaya AAA) and ONE of .a's seven dimensions.
      .a asks for four host tiers and for operations, controls, contracts,
      capabilities and bundles besides routes.

      .b — A DOOR THAT IS LOCKED STILL HAS A SIGN ON IT. The Studio index
      (`app/studio/page.tsx`, 1,490 lines) is not under the governed prefix,
      contains ZERO tier or entitlement references, and renders 87 links to
      governed `/studio/isis/*` routes — 9 of whose visible labels name raw
      machinery: "Open Isis LoRA Training workspace", "Open Isis GPU Worker
      Architecture workspace", "Open Isis Civitai Integration workspace", "Open
      Isis ComfyUI Nodes workspace". The destinations 404 for a contemplative
      session. The names are printed to it. Beyond the index, 37 of the 504
      Studio routes name raw machinery within one import hop and 6 of those are
      outside the boundary. 0 bundle scans exist anywhere in the repository, so
      the "and bundles" half of .b has no instrument at all.

      .c/.d/.f — 85 specs drive a governed route and 82 assert a boundary
      outcome, which is real coverage of the prefix that is already fail-closed.
      At LINE scope, 1 of .d's ten side channels and 1 of .f's five dynamic
      conditions are paired with the boundary by any spec, and 0 specs name a
      mid-session tier or entitlement change at all.

      .e — 0 of the 10 approved curated card kinds is walked by a Playwright
      spec; read out of I9.12's committed evidence rather than re-derived.

      FIXED: A FUNCTION NAMED FOR A CATALOG IT DOES NOT READ.
      `AAA_ONLY_STUDIO_ROUTES` lists 31 segments and enforces nothing — the
      kit's own `route-integrity.ts` says so in as many words — while
      `isAaaOnlyRoute`, which sounds like its reader, consults the empty
      allowlist instead. Two docstrings on `AaaGate.tsx` told readers an unknown
      segment renders its children as a "defensive default"; it does not, it is
      gated, and that is the safe behaviour. A comment describing the opposite of
      the enforced behaviour on the estate's #1 no-regress boundary is an
      invitation to "fix" the code toward it and open the other 56 segments.
      Both docstrings are corrected and `isAaaOnlyRoute` now carries the warning
      at its definition. No behaviour changed; 30 entitlements tests and 1,478
      studio component tests stay green.

      TRAPS THIS CELL PAID FOR. EVERY ROUTE REACHES EVERYTHING THROUGH THE
      SHELL: the first static leak scan walked each route's whole import closure
      and reported 501 of 503 routes reaching raw machinery — through
      `components/library/librarySavedItems.ts`, where the word was `checkpoint`
      and it meant a STUDY checkpoint. A CHECKPOINT IS A LEARNING MILESTONE and
      A SAMPLER IS ALSO A DEPTH SENSOR (an Aja depth-sensing catalog field). The
      report now scans comment-stripped sources at two depths and prints both. A
      WORD SOMEWHERE IN A SPEC IS NOT A PAIRING: file-scope pairing reported 70
      specs testing the "error" side channel against the boundary, which is only
      the observation that 85 specs touch the boundary and most files contain
      the word "error"; line scope reports none, and both are kept. AN `export`
      KEYWORD IS NOT AN EXPORT CHANNEL: every TypeScript module begins with one,
      and the bare probe paired 19 specs on lines that were declarations. -->
- [ ] I9.17 Publish Isis operator and incident runbooks. Evidence: game-day
      validates provider disable, model revoke, and output recall.
  - [ ] I9.17.a Inventory operational scenarios, owners/on-call/escalation,
        prerequisites, permissions, dashboards/queries, commands, evidence,
        communications, and recovery criteria. Evidence: runbook index.
  - [ ] I9.17.b Publish provider degradation/disable/drain/failover/restore
        procedure with in-flight/reservation/cost and verification steps.
        Evidence: provider runbook.
  - [ ] I9.17.c Publish model/workflow security/license/safety
        quarantine/revoke/ replacement/blast-radius procedure. Evidence:
        model/workflow runbook.
  - [ ] I9.17.d Publish output/bundle/gallery/marketplace/release quarantine/
        recall/downstream verification/notification procedure. Evidence: recall
        runbook.
  - [ ] I9.17.e Publish queue/worker/storage/cache/database/search/outbox/
        credential/provenance/dependency outage and recovery procedures.
        Evidence: infrastructure runbooks.
  - [ ] I9.17.f Publish child/identity/voice/biometric/rights/privacy/security/
        spend incidents with restricted evidence and authority/escalation.
        Evidence: sensitive incident runbooks.
  - [ ] I9.17.g Execute timed game days for provider disable, model revoke, and
        output recall; capture gaps, update runbooks/automation, and verify
        closure. Evidence: game-day reports.
      <!-- I9.17 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-isis-runbooks.mts`
      (evidence `evidence/v1-workbenches/oshun-isis-runbooks.json`,
      7 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 7 children earned; every box stays unchecked.

      THE VEIN: A GAME DAY FOR THE INFRASTRUCTURE IS NOT A GAME DAY FOR THE
      GENERATION LIFECYCLE. The parent names three drills as its evidence —
      provider disable, model revoke, output recall — and the estate HAS run a
      real game day. On 2026-08-14 it injected five faults against a disposable
      estate through `scripts/operations/v1-fault-control.mjs` and recorded
      timings, an acceptance matrix, a follow-up register and retests:
      `gd:database-loss`, `gd:queue-loss`, `gd:blob-store-loss`,
      `gd:regional-loss`, `gd:relational-corruption`. Every one is a piece of
      infrastructure going away. 0 of the 3 named drills appears in the report,
      and `model revoke` is described by 0 runbooks anywhere in the estate.

      A FOLLOW-UP REGISTER FOR ONE SCENARIO IS NOT A REGISTER FOR THE GAME DAY.
      The register carries 30 follow-ups, all scoped to `gd:regional-loss` — one
      of the five exercised — and stamped `estate: production` while the
      executions ran on `estate: disposable`.

      The runbook estate itself is good. `docs/runbooks/index.md` is a real
      on-call index with owner, applies-to, incident shape and severity per row,
      and the directory holds procedures for provider failover, model/workflow
      rollback, provenance failure, watermark verification failure, privacy
      incidents and ten infrastructure losses. 19 of the 29 procedures .b–.f
      name are described by some runbook.

      AN UNINDEXED RUNBOOK IS INVISIBLE TO ON-CALL. The index tells its own
      readers "you're writing a new runbook — add a row here in the same PR",
      and 14 of the 49 runbooks beside it have no row — nine of them the Yemaya
      study family, including `yemaya-study-model-replacement.md` and
      `yemaya-study-provider-change.md`, which are the closest things in the
      estate to two of the three drills the parent asks for.

      AND A DIRECTORY NAMED RUNBOOKS IS NOT A DIRECTORY OF RUNBOOKS.
      `docs/domains/isis/runbooks/` holds 1,001 files; 7 are operational
      procedures. The rest are per-asset-kind design documents — workflow
      graphs, typed request contracts, budget guardrails, endpoint sizing
      policies — filed under a path that makes any count of "Isis runbooks"
      meaningless until it is split.

      Per child: .a — the index carries 3 of .a's 9 dimensions (scenarios,
      owners/on-call, permissions); commands, dashboards, evidence and
      communications live in the procedures instead, and recovery criteria are
      stated by 4 runbooks. .b 4/5 (no provider DISABLE, only degradation and
      failover). .c 3/4 (no model revoke). .d 1/4 — the recall child is the
      thinnest in the cell: `recall` appears in one runbook and `marketplace` in
      none. .e 7/9 (no worker-stall or cache-loss procedure). .f 4/7 (no
      identity-incident, spend-incident or restricted-evidence procedure).

      A TRAP THIS CELL PAID FOR: A KEY THAT DOES NOT EXIST AND AN EMPTY LIST
      READ THE SAME. The follow-up register's array is `followUps`; guessing
      `entries`/`register` reported 0 follow-ups for a register that holds 30,
      and a zero from a wrong key looks exactly like a measured absence. -->
- [ ] I9.18 Produce engineering-readiness evidence. Evidence: all required
      reports are current and linked.
  - [ ] I9.18.a Define the required readiness index by requirement, capability,
        host/audience, asset kind, provider/endpoint, target, standard, owner,
        evidence type, and freshness rule. Evidence: evidence schema.
  - [ ] I9.18.b Collect current contract/schema/migration/unit/integration/E2E/
        accessibility/performance/security/evaluation/DR/runbook reports by
        immutable commit/artifact links. Evidence: automated evidence index.
  - [ ] I9.18.c Validate artifact existence/hash, test result/pass state,
        environment/dependency versions, coverage scope, timestamp/freshness,
        and no fixture/live ambiguity. Evidence: evidence validator.
  - [ ] I9.18.d Map every Isis parent/child task and ratified capability/matrix
        row bidirectionally to evidence or explicit not-applicable decision.
        Evidence: zero-orphan traceability report.
  - [ ] I9.18.e Summarize open blockers/risks/exceptions/limitations/owners/
        deadlines without allowing summary success to hide failed cells.
        Evidence: readiness dashboard/report.
  - [ ] I9.18.f Reproduce the packet from repository and approved external
        records, sign/version it, and retain prior packets immutably. Evidence:
        reproducibility test.
      <!-- I9.18 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-readiness-evidence.mts`
      (evidence `evidence/v1-workbenches/oshun-readiness-evidence.json`,
      10 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: A LEDGER FOR ANOTHER CHECKLIST IS NOT THIS CHECKLIST'S LEDGER.
      Almost everything this cell asks for has been built, carefully, and points
      at a different programme. `ISIS_V2_EVIDENCE_LEDGER.schema.json` is a real
      evidence schema — nine required fields per entry including `commitSha`,
      `verificationCommands`, `artifacts` and `owner`. The ledger beside it
      holds 1,262 entries with every field populated, citing 5,490 distinct
      artifact paths of which 5,440 exist. A validator beside THAT checks the
      ledger against the ticket set and against the filesystem. Its
      `sourceChecklist` is `ISIS_TODOS_2.md`, its ids are `DOD2-###` and
      `MXD-####`, and 0 of the 1,262 match this checklist's `I<n>.<m>`
      numbering. This checklist holds 181 parents and 1,391 children, 760 of
      them ticked, and 8 carry a measurement record a machine can find.

      .b — A REQUIRED FIELD SATISFIED BY A PLACEHOLDER IS NOT AN IMMUTABLE LINK.
      All 1,262 rows carry the same `commitSha` and its value is the string
      `PENDING_COMMIT`. 0 of the 1 distinct value is a sha.

      .c — A GATE NOBODY RUNS IS NOT A GATE.
      `scripts/isis/validate-v2-compliance-gate.mjs` exits 1 today with 50
      errors, every one an evidence entry citing a script that no longer exists.
      It is named by 0 package scripts and 0 workflows, so nothing has read
      those 50 errors. Of .c's seven validations it performs one — artifact
      existence. Its commit check is `isNonEmptyString(entry.commitSha)`, which
      `PENDING_COMMIT` passes: the same shape as the NaN that cleared a range
      check written as two comparisons in I9.5.

      .a — the entry schema carries 3 of .a's 10 dimensions (requirement, owner,
      evidence type). Capability, host/audience, asset kind, provider/endpoint,
      target, standard and the FRESHNESS RULE — the field that decides whether a
      report is still current — are absent.

      .e — A LEDGER WHERE EVERY ROW IS DONE HAS NO FAILED CELL TO HIDE. The
      only status value across all 1,262 rows is `DONE`, which is precisely the
      shape .e asks a report not to have.

      .f — the estate proves its artifacts are CURRENT and not that they are
      SIGNED: 295 of the 309 generators under `scripts/v1-workbenches/` carry a
      `--check` invocation in `verify:inventory:v1-workbenches`, which is a real
      reproducibility proof per artifact, while 21 of the 320 artifacts carry a
      hash or signature field and 2 prior packets are retained.

      WHAT IS ACTUALLY ANSWERED: .b's vocabulary half. 10 of the 12 report kinds
      .b names are named by some artifact path in the ledger (`unit` and
      `evaluation` by none), and 8 of 12 by the workbench evidence tree — though
      that measure is about the INDEX's vocabulary and not about whether the
      estate has that kind of test, since a unit-test report is not filed under
      a path containing `unit`.

      TRAPS THIS CELL PAID FOR. A `*` IN A GIT PATHSPEC CROSSES A SLASH:
      `evidence/v1-workbenches/*.json` returns all 415 JSONs under the tree, not
      the 320 at the top level. AND `git ls-files` CANNOT SEE THE FILE THIS RUN
      IS WRITING: counting the top level with git made the census change the
      instant it was committed — 319 while its own artifact was untracked, 320
      after — so the two self-referential counts are read from the filesystem
      and the tree control stays a git read. -->
- [ ] I9.19 Obtain named security/privacy, trust-and-safety, rights, and
      engineering approvals. Evidence: human approval records.
  - [ ] I9.19.a Define each approver role/person, authority/scope, required
        packet sections, independence/conflict rules, decision options,
        conditions, and expiry. Evidence: approval matrix.
  - [ ] I9.19.b Freeze the exact commit/build/config/environment/host/audience/
        capability/target readiness packet presented for approval. Evidence:
        approval subject manifest.
  - [ ] I9.19.c Obtain security/privacy decision over threat model, tests,
        findings/exceptions, data flows, retention, tenancy, biometrics, and
        incident readiness. Evidence: named decision.
  - [ ] I9.19.d Obtain trust-and-safety/rights decision over child/identity/
        voice/moderation/originality/consent/licenses/provenance/recall
        evidence. Evidence: named decisions.
  - [ ] I9.19.e Obtain engineering decision over architecture, migrations,
        reliability/SLO, capacity/performance, operations/DR, tests, and known
        limitations. Evidence: named decision.
  - [ ] I9.19.f Record approve/reject/conditional/request-changes separately,
        bind conditions/exceptions/expiry, and invalidate on material subject or
        evidence change. Evidence: approval lifecycle tests.
      <!-- I9.19 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-release-approvals.mts`
      (evidence `evidence/v1-workbenches/oshun-release-approvals.json`,
      18 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: THE TEMPLATES ASK WHO; THE RECORDS ANSWER WHAT ROLE.
      The parent's evidence sentence is "human approval records", and the estate
      has a corpus of them: 9 records under `docs/releases/v1/verification/`,
      71 decision rows, all dated 2026-05-15, each written in exactly one commit
      and never revisited. `docs/launch/go-no-go.md` — the template they were
      filled in from — asks for five columns: Role, SIGNATORY, Decision, TS,
      Evidence. The record filed under `verification/go-no-go/` has three: Role,
      Decision, Evidence. The two it dropped are the two that make an approval
      attributable to a person at a moment. `docs/launch/safety-review.md` asks
      for `signatories: <name> (role)`; the three board records carry a column
      headed "Required role", which is the ROSTER table from its §2.1 rather
      than its signoff block.

      AND WHERE A NAME WAS ACTUALLY ASKED FOR, THE ANSWER IS A MACHINE. Across
      every role-and-name signature table in the repository — 47 cells in 11
      files — 0 name a person: 37 are blank underscore rules, 6 are the literal
      `<name>` placeholder in the template itself, 2 read `Automated Audit` and
      `Automated Validation`, and 2 name a team. The zero is falsifiable:
      anything the classifier cannot place lands in the person bucket and is
      listed, and that list is empty.

      A ROLE CANNOT BE CHECKED FOR INDEPENDENCE. .a asks for independence and
      conflict rules; the 9 records mention independence, conflict, recusal and
      expiry 0 times, and carry 0 identifiers that resolve to an account. You
      cannot ask whether the Privacy Lead who approved the dogfood drill is the
      same person as the Privacy Lead on the Privacy Review Board when neither
      is a person. Every one of the 71 decisions is APPROVE or PASS.

      THE ONE GATE THAT DEMANDS A NAME IS RED, AND THE GA APPROVAL SAID GO
      ANYWAY. `docs/security/pentest/signoff.md` is the artifact this cell asks
      for, built properly: 10 fields per engagement, 3 of them "Name + ISO
      date", append-only rows, six release-gate rules including a 14-day signoff
      expiry, and a CI step in `.github/workflows/release.yml` that refuses the
      tag. Its engagements table reads `_none yet_`; the gate exits 1 today. The
      GA approval recorded on the day that gate was wired says "Security and
      compliance: pass", "waivers: none", "resulting decision: GO".

      FIXED, IN THAT GATE: A MISSING FOOTER IS NOT A CLEAN ONE. Rule 4 blocks a
      release on an open AuthN, RBAC or privacy finding, and ran only `if
      (tagsMatch)` — so an absent `tags-summary:` footer and a footer declaring
      every category clear reached the same verdict, the shape of I9.17's key
      that does not exist reading the same as an empty list. AND THE THIRD
      SIGNATURE WAS OPTIONAL IN CODE: rule 5 checked `signoff_sec` and
      `signoff_eng` and never `signoff_priv`, so the head of privacy was the one
      of the three the document names whose signature the gate did not want.
      Both enforced now, the record's rule text aligned with what the gate
      actually anchors on, and 9 tests that fail four ways against the previous
      code.

      .b — A SUBJECT NAMED BY A BRANCH IS NOT FROZEN. 1 of 8 subject dimensions
      appears anywhere in the corpus, and the one that does is
      `release commit: main` against a template whose field is `<sha>` — the
      shape of the `PENDING_COMMIT` that satisfied a required commitSha in
      I9.18. The tag it names, `v1-launch-rc`, is not one of the repository's 1
      tags. 20,390 commits have landed since (measured 2026-08-26; not in the
      artifact, because a HEAD-distance would go stale on every commit anywhere
      and `--check` would fail for reasons having nothing to do with approvals).

      .a — the estate's approval matrix,
      `docs/domains/isis/remediation-v2/ISIS_V2_SIGNOFF_MATRIX.md`, carries 5 of
      .a's 7 dimensions, and it is scoped to `ISIS_TODOS_2.md` — the same
      different-checklist trap as I9.18's ledger. The 2 dimensions it lacks are
      the 2 that make an approval revocable: independence/conflict and expiry.
      Its "Primary Owner" column names 7 team handles and no person, and its
      decision-record template is quoted in exactly 1 file: itself.

      .c — A PRIVACY BOARD IS NOT A SECURITY BOARD. There is no security board
      record; the privacy board's names 2 of .c's 8 subjects (tests, tenancy),
      with threat model, findings/exceptions, data flows, retention, biometrics
      and incident readiness absent. The Security Lead appears only as a
      one-line APPROVE row with a phrase for evidence. .d — the two safety
      boards name 3 of 9 (child, moderation, provenance). .e — the records the
      Engineering Lead signs name 4 of 7, missing architecture, capacity and
      known limitations.

      .f — NO VOCABULARY IN THE ESTATE CAN SAY ALL FOUR THINGS. 219 literal
      lists across `libs/` and `apps/` carry both an approval and a rejection
      spelling; 173 carry 2 of .f's 4 options, 46 carry 3, and 0 carry 4. Only 3
      can say `conditional` and none of those 3 can say `changes_requested`. THE
      ESTATE BUILT THE LIFECYCLE .f DESCRIBES, TWICE, AND IMPORTS NEITHER:
      `libs/oshun/workbench-kit/src/signoff.ts` has `validForHours`, declared
      independence dimensions and material-change detection, and
      `libs/shared/review-persistence/` publishes a decision-lifecycle and a
      release-validity service — 0 product importers each.

      A SCENARIO LIST IS NOT AN EXERCISE REPORT. §3 of the safety-review
      template makes 10 contents mandatory before a board may sit; 2 name no
      path, and 4 of the remaining 8 are absent — the moderation backlog, the
      persona-tone audit, the DSAR packet, and the red-team exercise report:
      `docs/security/red-team/` holds the scenario set and 0 dated reports.

      WHAT IS ACTUALLY ANSWERED: the estate does check its approval records.
      `libs/oshun/analytics/src/v1-33-launch-readiness-evidence.ts` indexes 11
      of them and a green vitest suite opens each file and asserts its decision
      string. All 11 strings are pass strings — so a board that changed its mind
      would not be recorded as a rejection, it would break the build.

      TRAPS THIS CELL PAID FOR. A NAME THAT COLLIDES IS NOT A CONSUMER, twice:
      `signoff.js` is a substring of `accountable-signoff.js`, which is Yemaya's
      own module, and `signoffStatus` is a field on Annapurna's safety task
      type — a substring probe and a bare-identifier probe each reported 2
      importers where there are 0. A SIGNATURE TABLE IS ONE WITH A THIRD COLUMN:
      a name-and-role header alone admits `V1/architecture/glossary.md`, whose
      "Role" is a description of a domain library, and an escalation contact
      list; requiring a date, signature, decision or status column cut 61 cells
      to the 47 that are somewhere a person signs. AND A RULE THAT NAMES WHAT IT
      WATCHES COUNTS ITSELF, the fourth time on this checklist: wiring this
      census into `verify:inventory:v1-workbenches` put the string
      `verify-signoff` into a package script, and the probe promptly reported
      that script as running the gate. It runs the gate's tests. -->
- [ ] I9.20 Obtain named product launch approval per audience and host.
      Evidence: human signoff remains separate for operator, AAA, and curated
      surfaces.
  - [ ] I9.20.a Define distinct launch cells for Isis operator host, Yemaya AAA
        integration, Oshun curated Studio, and Oshun gallery with audience,
        regions, capabilities, providers, targets, flags, and rollout plan.
        Evidence: launch matrix.
  - [ ] I9.20.b Assemble cell-specific product evidence for user journeys,
        utility/usability/accessibility, honest states, documentation/support,
        analytics, limitations, rollback, and prerequisite governance approvals.
        Evidence: launch packets.
  - [ ] I9.20.c Obtain a named authorized product decision independently for
        each cell rather than inheriting another audience/host’s signoff.
        Evidence: separate human approval records.
  - [ ] I9.20.d Bind approval to exact release subject, enablement/flag/region/
        provider/target scope, conditions, success/stop metrics, and expiry.
        Evidence: scope validation.
  - [ ] I9.20.e Revalidate every cell immediately before rollout and pause only
        affected cells when approval/evidence/dependency changes. Evidence:
        prelaunch gate tests.
  - [ ] I9.20.f Record phased rollout/hold/rollback/outcome and require new
        approval for material scope expansion. Evidence: launch history.
      <!-- I9.20 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-oshun-launch-cells.mts`
      (evidence `evidence/v1-workbenches/oshun-launch-cells.json`,
      10 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 6 children earned; every box stays unchecked.

      THE VEIN: A SUBSTRATE IS NOT AN AUDIENCE.
      .a asks for four launch cells — Isis operator host, Yemaya AAA
      integration, Oshun curated Studio, Oshun gallery — each with its own
      audience, regions, capabilities, providers, targets, flags and rollout, so
      .c can approve each separately and .e can pause one without the others.
      The estate's rollout plan partitions the release 7 ways and every one is a
      SUBSTRATE: customer web, iOS, Android, BFF, domain services, workers, ML
      inference. That is a partition by what runs the bytes, not by who receives
      them. All four cells ride on customer web, and 0 of the 4 is a row in the
      plan, so the smallest thing it can hold back is all four at once.

      AND AN ENTITLEMENT IS NOT A LAUNCH SWITCH. The estate knows exactly who a
      user is: `libs/isis/entitlements/src/generation-tier.ts` resolves an
      entitlement bundle to `operator-admin`, `aaa-creator`, `curated-creator` or
      `contemplative` and returns a surface allowlist, and 37 modules spell the
      same four values. None of that vocabulary reaches the enablement layer.
      The 4 flag modules that decide what is switched on target a tenant, a
      domain, a locale, a country, a platform, a rollout percentage and a
      SUBSCRIPTION tier — a billing band, not a generation tier. Generation
      tier, region, provider and launch cell are targetable in 0 of them, and
      the kill switch's two scopes are `global` and `agent_family`. No cell has
      a switch of its own, which is .e's whole request.

      .c — A DOMAIN PM IS NOT A CELL PM. `exit-criteria/2026-05-15.md` is the
      closest thing: 11 owner signoffs, 6 of them product managers for a named
      surface. There is no Isis row — Isis is the subject of this entire phase —
      no Yemaya row and no gallery row. One row names a cell's host without
      saying which of its audiences it approves ("Studio PM"), and above all 11
      sits a single aggregate `GO / NO-GO = GO`, which is exactly the inherited
      signoff .c asks each cell not to take. All 11 decisions are APPROVE.

      .f — A SAMPLE OF ONE STAGE IS NOT A LAUNCH HISTORY. The plan approves 28
      stages across the 6 substrates whose schedule is enumerable (domain
      services says "per-service ECS schedule" and states none). The
      verification tree holds 1 stage record, titled "Canary Analysis Sample",
      whose verdict is a recommendation to promote once a manual review
      completes. 0 promotions, 0 holds, 0 rollbacks, 0 outcomes recorded, and no
      rule requiring a new approval for material scope expansion.

      .a/.b/.d — 2 of .a's 7 per-cell dimensions (flags, rollout) are named by
      the launch documents at all; 0 of .d's 9 binding dimensions are, including
      the exact release subject, conditions, success and stop metrics and
      expiry. There is 1 launch packet rather than 4, and it names 7 of .b's 8
      subjects — honest states is the one it does not.

      WHAT IS ACTUALLY ANSWERED: the cell concept exists, once, built for
      something else. `libs/contracts/src/content-coverage/seed-registry.ts`
      declares a surface classification carrying BOTH `introducingTier` and
      `productShell`, and `content-coverage/seed-registry.v1.json` fills it in
      over 28 rows. Its 4 distinct pairs are `aaa-creator x yemaya-studio`,
      `curated-creator x oshun-studio`, `operator-admin x oshun-admin` and
      `contemplative x oshun-contemplative-products` — 2 of .a's 4 cells, with
      the operator host named `oshun-admin` rather than Isis and no gallery pair
      at all. Every tier occurs with exactly one shell, so it is a bijection
      recorded as data rather than a matrix somebody can add a cell to, and it
      is a content-coverage registry: it says which shell first introduces a
      surface at which tier, and nothing about launching.

      TRAPS THIS CELL PAID FOR. A VALUE IS NOT A FILE: asking whether the shell
      contract "names" the Isis operator host by testing `isis` and `admin`
      against its 665 lines returns true for every cell, because the two words
      occur in different places. Scoped to the enum's 4 values, 0 name a cell —
      they are host names with no audience half. Scope the probe to the thing,
      not to the file that contains it. -->
- [ ] I9.21 Run matrix-generated contract, UI, workflow, runtime, bundle, and
      release tests for every asset family, conditioning mode, consistency
      strategy, provider/endpoint class, and delivery target ratified in
      I0.14-I0.17. Evidence: the Cartesian coverage report has no silent,
      inherited, or untested supported cell.
  - [ ] I9.21.a Generate canonical matrix dimensions/values/versions and
        explicit supported/unsupported/not-applicable constraints from I0
        decisions and source-of-truth registries. Evidence: matrix manifest.
  - [ ] I9.21.b Generate only valid constrained cells and require a reasoned
        disposition for every excluded combination. Evidence: cell enumeration
        tests.
  - [ ] I9.21.c Map each supported cell to typed contract/schema/round-trip and
        audience/host property-authorization proof. Evidence: contract coverage.
  - [ ] I9.21.d Map each exposed cell to UI control/state/accessibility/
        responsive and curated/AAA/operator tier proof. Evidence: UI coverage.
  - [ ] I9.21.e Map each cell to exact workflow/model/conditioning/continuity/
        provider capability negotiation, execution, cancellation, and failure
        proof. Evidence: workflow/runtime coverage.
  - [ ] I9.21.f Map each publishable cell to technical/safety/rights gates,
        deterministic target bundle, write/read-back, and lifecycle proof.
        Evidence: bundle/release coverage.
  - [ ] I9.21.g Execute with bounded sharding/resource limits, collect per-cell
        evidence/failures/skips, and prohibit aggregate pass from hiding a cell.
        Evidence: resource-safe test report.
  - [ ] I9.21.h Fail release for supported cells with missing/stale/inherited/
        generic-only proof and publish the complete Cartesian coverage report.
        Evidence: zero-silent-cell gate.
      <!-- I9.21 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-isis-matrix-cells.mts`
      (evidence `evidence/v1-workbenches/isis-matrix-cells.json`,
      8 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 8 children earned; every box stays unchecked.

      THE VEIN: FIVE ALPHABETS AND NO PRODUCT.
      All five dimensions this cell names are ratified and separately measured —
      32 asset families in `isis-asset-family-matrix.json`, 31 canonical
      conditioning controls and 8 consistency dimensions in
      `isis-conditioning-mode-matrix.json`, 9 provider kinds in
      `ISIS_PROVIDER_KINDS`, 8 delivery targets in the target-profile catalogue.
      They multiply to 571,392 cells before constraints. Nothing multiplies
      them: 0 files declare all five, 0 artifacts enumerate a cell, and so 0
      excluded combinations carry the reasoned disposition .b asks for. There is
      no cell for .h's report to call silent.

      AND NO TEST IN THE REPOSITORY NAMES TWO DIMENSIONS. Across 959,226 test
      titles in 27,332 spec files (the workbench instrument tree excluded): 497
      name an asset family, 176 a provider kind, 10 a conditioning control, 3 a
      consistency strategy, and 0 a delivery target. Exactly 1 title names two dimensions, and it does so
      because `identity-lock` is both a canonical control and the lock form of
      the `identity` consistency dimension — one dimension counted twice. 0
      titles name two DISTINCT dimensions. The estate tests along the axes and
      never at a point, which is what .c through .f each ask for.

      THE CLOSEST THING TO A MATRIX MANIFEST IS A ROUTE FILE. Two files name
      three of the five alphabets. The product one is
      `apps/isis/generation-api/src/routes/jobs.ts`, 9,821 lines, carrying all
      32 families, 11 of 31 controls, all 8 consistency dimensions, 2 of 9
      providers and 0 delivery targets — the endpoint that accepts a job, which
      is where three alphabets meet because a request mentions all three. The
      other is `evidence/v1-workbenches/inventory/tests.json`, an index.

      .f — THE DELIVERY-TARGET ALPHABET LIVES IN THE EVIDENCE TREE. Its 8 values
      are declared across 18 modules under
      `evidence/v1-workbenches/target-profiles/`, which 0 product files import.
      The one dimension along which a bundle is actually delivered is, in the
      product, a dimension of one — the same finding I8.12 recorded when it
      looked for a partial failure to recover from.

      .g — A SHARD OF A SUITE IS NOT A SHARD OF A MATRIX. 5 workflows shard a
      test suite (e2e, iris-ci, oshun-admin-e2e, oshun-web-e2e, tara-ci) and 0
      shard an Isis matrix. .h — 0 zero-silent-cell gates and 0 Cartesian
      coverage reports exist.

      WHAT IS ACTUALLY ANSWERED: the asset-family axis, completely. All 32 of
      the 32 ratified families are named by at least one test title, and 498
      titles name one. Against that: 4 of 31 conditioning controls, 2 of 8
      consistency strategies, 7 of 9 provider kinds and 0 of 8 delivery targets
      have a test title that names them.

      TRAPS THIS CELL PAID FOR. A SUBSTRING IS NOT THE CONCEPT: seven of the
      eight consistency dimensions are ordinary English words — identity,
      environment, motion, prop, lens, lighting, wardrobe — and `prop` sits
      inside `character-prop-interaction-sheet`. Matched bare they report 13,002
      titles and 69 apparent two-dimension tests, every one of them an asset
      family whose name contains a common noun; matched in the form that names a
      strategy (`<dimension> lock`, `<dimension> consistency`) they report 3.
      AND MEASURE TITLES, NOT FILES — with the `.each([...])` argument skipped
      before the title is read, so a table-driven test contributes its template
      rather than its table. AND A RULE THAT NAMES WHAT IT WATCHES COUNTS
      ITSELF, the fifth time on this checklist: this census's own known-value
      tests live in a `.test.mts` file, so the instant they were staged the
      corpus grew by one file and eight titles and the artifact went stale for a
      reason that had nothing to do with the estate — and I9.22's instrument
      bumped it again the next day. The subject is the ESTATE's test titles, so
      the whole of `scripts/v1-workbenches/` is excluded rather than just this
      file. -->
- [ ] I9.22 Run full journeys for specialized 3D, cinematic sequence,
      voice/dubbing, foley/music, marketplace, model intake/training, and
      parity-monitor workflows. Evidence: each live/exposed capability has
      current host-specific proof rather than relying on generic generation
      coverage.
  - [ ] I9.22.a Run reconstruction-to-semantic-edit/material/LOD/rig/splat/
        engine-DCC-package/read-back journey with non-destructive lineage and
        target gates. Evidence: specialized 3D E2E.
  - [ ] I9.22.b Run storyboard/animatic/shot continuity/render passes/patch
        rerender/editorial interchange/release journey with stable unaffected
        shot identities. Evidence: cinematic E2E.
  - [ ] I9.22.c Run verified consent through voice enrollment/TTS/clone/dub/
        pronunciation/sync/multitrack mix/release/revocation. Evidence: voice/
        dubbing E2E.
  - [ ] I9.22.d Run SFX/foley/music cue/generation/timeline/stems/loudness/mix/
        rights/output bundle journey. Evidence: foley/music E2E.
  - [ ] I9.22.e Run creator enrollment/submission/gates/review/list/purchase/
        entitlement/use/attribution/payout/takedown journey. Evidence:
        marketplace E2E.
  - [ ] I9.22.f Run external model
        intake/quarantine/license/security/evaluation/ compare and
        fine-tune/LoRA/merge lineage through promotion/revoke. Evidence: model
        supply E2E.
  - [ ] I9.22.g Run coverage-source ingest/divergence detection/traceability/
        disposition/expiry/regression/release blocking journey. Evidence:
        parity-monitor E2E.
  - [ ] I9.22.h Execute each journey in its real authorized host/audience with
        live or explicitly hermetic dependencies, negative tier/tenant cases,
        accessibility, trace/audit, and current per-capability evidence. Evidence:
        per-journey live trace bound to the current evidence index.
      <!-- I9.22 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-isis-specialized-journeys.mts`
      (evidence `evidence/v1-workbenches/isis-specialized-journeys.json`,
      8 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 8 children earned; every box stays unchecked.

      THE VEIN: NOT ONE OF THE SEVEN JOURNEYS IS WALKED FURTHEST BY ITS OWN HOST.
      The parent's evidence sentence asks for host-specific proof "rather than
      relying on generic generation coverage". Isis owns all seven of these
      workflows and has 13 e2e specs. Across the estate's 1,013-spec Playwright
      corpus, the deepest any Isis spec gets into any of the seven journeys is
      ONE stage. The cinematic, voice/dubbing and foley/music journeys are each
      led by the same file — `apps/yemaya/studio-web/e2e/studio-web.smoke.spec.ts`,
      1,727 lines, which really does drive a storyboard, a shot scene graph, an
      F5-TTS provider selection and a SyncNet lip-sync, and is a smoke test for
      another host.

      A JOURNEY IS A WALK, and 0 of 7 are walked. Deepest single spec per
      journey, against the stages the checklist spells out: specialized 3D 2 of
      8, cinematic 3 of 7, voice/dubbing 4 of 10, foley/music 4 of 9,
      marketplace 2 of 11, model supply 3 of 9, parity monitor 1 of 7. 16 of the
      61 stages are reached by no spec in the corpus at all — semantic mesh
      edit; animatic and editorial interchange; voice enrollment; the final mix;
      creator enrollment, submission, listing gates and entitled use; model
      licence, security scan and revoke; and four of the parity monitor's seven,
      including divergence detection itself.

      THE CORPUS IS ONE APP. 751 of the 1,013 e2e specs are `apps/oshun/web`;
      `apps/isis/web` has 13, and 11 of those are per-family generation control
      panels.

      .h — 1 spec of 1,013 opens a second host and 1 asserts a negative tier
      case; 9 assert a negative tenant case; 3 declare themselves hermetic. The
      two conditions the estate does meet broadly are accessibility (85 specs
      run axe) and trace/audit evidence (75).

      TRAPS THIS CELL PAID FOR. A NAME THAT COLLIDES IS NOT A DCC: `\bmaya\b`
      matched "Maya Torres", a teacher in a visual-regression fixture, and made
      a page-screenshot spec the deepest walker of the 3D journey at 3 stages.
      Requiring the application to be doing something to a 3D asset — or the
      match to be a file format — drops it to 2. The same discipline is why
      `rig` is anchored (it is inside `trigger` and `original`), `clone` is
      anchored (`structuredClone`), and `mix` is matched only as `mixdown` or
      `mix bus`. AND A URL IS NOT A COMMENT — the comment stripper leaves `//`
      alone when a colon precedes it, which is what I9.11 paid for. -->
        Evidence: specialized journey index.

---

## Phase M — Metis courseware and assessment workbench

Metis must support source-to-course authoring, accessible assessment-item
production, psychometric review, adaptive sequencing, integrity governance, and
standards-compliant institutional delivery. Learner delivery remains a separate
surface from authoring and operator adjudication.

### M0 — Product scope, host, and domain decisions

- [ ] M0.1 Reconcile `/metis/*`, `/domains/metis`, `/operator/metis`,
      `apps/metis/admin`, `apps/metis/web`, and proposed Studio routes.
      Evidence: one canonical route/host map.
      <!-- partial: current route inventory (a) done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/metis-route-inventory.json. b-f (audience/host
                                                                                                                                                                                                                                                                      classification, canonical target map, dispositions, boundary review,
                                                                                                                                                                                                                                                                      ratification) are semantic/design/human and stay unchecked. -->
      <!-- SPEC 2026-09-11: .c is a product decision (Section 1 table:
      "canonical host to ratify in P0"), and M9 was built before it was taken,
      so authoring now lives in two apps (§2.5.1). An agent drafts
      `docs/adr/ADR-M0.1-metis-canonical-host.md` (Proposed) enumerating the
      three real options — keep the split (`apps/metis/web/create` +
      `apps/metis/admin`), consolidate authoring into `apps/metis/admin` and
      role-gate or retire `/create/*`, or an Oshun Studio facade in V1.2 — and
      builds .f's drift check against the option the ADR proposes; .c–.f flip
      only after a named human accepts the ADR. The M0.1 generators need no
      re-run. -->
  - [x] M0.1.a Inventory every current route, app entry point, navigation link,
        deep link, API consumer, audience, mutation, and ownership for the named
        Metis surfaces. Evidence: current route inventory.
        <!-- evidence: .routes — 39 tracked pages across all 4 named surfaces
                                                                                                                                                                                                                                                                        (metis-admin 15, metis-web 13, oshun /metis 10, /operator/metis 1), each
                                                                                                                                                                                                                                                                        with source/host/mount/Next.js-route-path, apiConsumers (20 distinct
                                                                                                                                                                                                                                                                        /api/metis|admin|auth), navLinks, dynamic flag, and test. /domains/metis has
                                                                                                                                                                                                                                                                        no tracked pages. audience/mutation/ownership are the M0.1.b classification
                                                                                                                                                                                                                                                                        (semantic). generate-metis-route-inventory.mjs + 7 tests, wired into inventory/verify. -->
  - [x] M0.1.b Classify each surface as authoring, institutional setup, educator
        operations, learner delivery, observer, or operator adjudication.
        Evidence: audience/host classification.
        <!-- evidence: metis-audience-classification.json — all 39 routes classified
                                                                                                                                                                                                                                                                        into the 6 canonical audiences via auditable host+path rules (see classify()):
                                                                                                                                                                                                                                                                        oshun-web /metis/* =learner-delivery except the two that author,
                                                                                                                                                                                                                                                                        /operator/metis=operator-adjudication,
                                                                                                                                                                                                                                                                        metis-web=learner-delivery(+authoring on /create), metis-admin spans
                                                                                                                                                                                                                                                                        institutional-setup/operator-adjudication/educator-operations/observer by path.
                                                                                                                                                                                                                                                                        No blank cells. Canonical target-host selection (M0.1.c), owner/migration
                                                                                                                                                                                                                                                                        disposition (M0.1.d), and named ratification (M0.1.f) are measured in
                                                                                                                                                                                                                                                                        evidence/v1-workbenches/metis-canonical-host-map.json.
                                                                                                                                                                                                                                                                        generate-metis-audience-classification.mjs + 8 tests.

                                                                                                                                                                                                                                                                        CORRECTED 2026-08-26 — THE KEY SAID STUDIO AND THE HOST FIELD BESIDE IT SAID
                                                                                                                                                                                                                                                                        DOMAIN SURFACE. The route inventory keyed this surface `oshun-studio-metis`
                                                                                                                                                                                                                                                                        while its own `host` field read `oshun-web (/metis/* domain surface)`, and the
                                                                                                                                                                                                                                                                        classifier read the key: one branch returned `authoring` for all ten routes
                                                                                                                                                                                                                                                                        with the rationale "Oshun Studio /metis authoring workbench". Nothing in the
                                                                                                                                                                                                                                                                        estate mounts a `/studio/metis` route. They are top-level `/metis/*` on the
                                                                                                                                                                                                                                                                        consumer web app, every one rendering a Lilith surface and titled "the
                                                                                                                                                                                                                                                                        schoolroom", "lesson", "lessons", "today's session", "tutor", "assessment" or
                                                                                                                                                                                                                                                                        "BYOM — bring your own materials". Two of the ten author and say so:
                                                                                                                                                                                                                                                                        /metis/ingest calls itself a "Creator-facing surface", /metis/courses/new is
                                                                                                                                                                                                                                                                        "propose a course". The key now says what the host field always said and the
                                                                                                                                                                                                                                                                        classifier reads the page; authoring fell 11 to 3, learner-delivery rose 12 to
                                                                                                                                                                                                                                                                        20. Two traps paid for on the way: A LINK TO AN AUTHORING SURFACE IS NOT AN
                                                                                                                                                                                                                                                                        AUTHORING SURFACE — /metis/lessons matched "propose a course" on the text of a
                                                                                                                                                                                                                                                                        button pointing at /metis/courses/new — and A REDIRECT IS NOT A SURFACE:
                                                                                                                                                                                                                                                                        /metis/upload renders nothing, permanently redirecting to /metis/byom, and now
                                                                                                                                                                                                                                                                        takes its target's audience. -->
  - [ ] M0.1.c Choose canonical route/host for every retained capability and
        distinguish embedded Studio facade from standalone app/service owner.
        Evidence: target route map.
  - [ ] M0.1.d Assign existing routes reuse, redirect-with-expiry, compatibility
        shim, retire, or net-new disposition with owner and migration date.
        Evidence: route disposition ledger.
  - [ ] M0.1.e Validate identity/tenant/context/deep-link/navigation boundaries
        and prohibit learner/answer-key/integrity/operator leakage. Evidence:
        route boundary review.
  - [ ] M0.1.f Ratify the canonical map and add route integrity/drift checks.
        Evidence: approved map and automated route test.
      <!-- M0.1.c-f measured 2026-08-26 by
      `scripts/v1-workbenches/generate-metis-canonical-host-map.mts`
      (evidence `evidence/v1-workbenches/metis-canonical-host-map.json`,
      11 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 4 children earned; every box stays unchecked.

      THE VEIN: THE ENDPOINTS THAT RETURN THE ANSWER KEY ARE THE ONLY TWO IN
      THEIR FILE THAT DO NOT ASK WHO IS CALLING.
      .e asks the boundary review to prohibit learner, answer-key, integrity and
      operator leakage. `apps/metis/service/src/metis/api/assessments.py` has 14
      endpoints; 12 take `Depends(get_current_instructor_user)` or
      `Depends(get_current_user)`. The 2 that take `db` and nothing else are
      `list_assessments` and `get_assessment`, whose `AssessmentResponse` nests
      `QuestionResponse` carrying `correct_answer` and `explanation`, and whose
      docstring is "Get full assessment details including all questions".

      The committed contract says it without being asked: in
      `apps/metis/service/openapi/metis.openapi.json` those two operations are
      the only ones on the assessment paths with no `security` requirement while
      every neighbouring mutation declares `HTTPBearer`. Of the API's 183
      operations, 37 declare no security and exactly 2 of the 37 return a schema
      carrying `correct_answer` — those two. And
      `apps/metis/web/src/app/assessments/[assessmentId]/page.tsx` is
      `'use client'` and fetches that endpoint from the browser, so the key is in
      the client for the whole attempt; the client only declines to DRAW it until
      the results view (`showCorrectAnswers ? question.correctAnswer : null`).

      NOT FIXED HERE, DELIBERATELY. The repair is an authorization change on a
      Python service whose dependencies are not installed in this environment,
      on the read path the learner UI depends on. An unverified auth change
      there is worse than a measured one; the fix wants an environment that can
      run its tests. 0 of .e's 4 leakage classes is closed in that file.

      .c — A CAPABILITY ON TWO HOSTS WITH NO CANONICAL CHOICE IS NOT A MAP. The
      39 routes cover 28 capabilities across 4 surfaces, and 4 capabilities sit
      on more than one: `courses` on three (metis-admin, metis-web,
      oshun-web-metis), plus `assessments`, `login` and the host root. 0
      artifacts in the estate record which host is canonical for any of them,
      and none tells the embedded facade apart from the standalone owner.

      .d — A REDIRECT WITHOUT AN EXPIRY IS NOT A DISPOSITION. 0 of 39 routes
      carry one of .d's 5 dispositions, an owner or a migration date. The estate
      has exactly 1 redirect — `/metis/upload` to `/metis/byom` — and it is a
      `permanentRedirect` with no expiry, which is the disposition .d names
      `redirect-with-expiry` missing its second half. The Isis programme has a
      real disposition ledger and gate in 4 files; Metis has none.

      .f — A DRIFT CHECK OVER AN UNRATIFIED MAP KEEPS AN UNRATIFIED MAP CURRENT.
      Both Metis inventories genuinely carry a `--check` gate and both run in
      `verify:inventory:v1-workbenches` — that half of .f is real. There is no
      ratified map for them to keep current, and 26 of the 39 routes have no
      test: metis-web, oshun-web-metis and oshun-operator-metis have no tested
      route at all, so the 13 that are tested are all metis-admin.

      TRAPS THIS CELL PAID FOR. A DECORATOR IS NOT A SIGNATURE: the guard lives
      in the function's parameter list, so a probe reading `@router.get(...)`
      finds `current_user` in no endpoint and reports all 5 GETs as
      unauthenticated where the answer is 2. AND A WORD IS NOT A LEDGER:
      `metis-package-inventory.json` contains the string `retire`, which makes a
      naive probe report a disposition ledger where there is a package list. -->
- [ ] M0.2 Resolve the domain-registry `planned` status and launch path before
      exposing the workbench in the shell. Evidence: approved registry decision.
      <!-- SPEC 2026-09-11: the registry facts are in M10.19's note. .d is
      buildable now (shell boundary tests over `release-scope.ts` and the
      `apps/oshun/web` middleware); .a–.c and .e are the decision record and
      stay open until ratified (§2.5.9). -->
  - [ ] M0.2.a Inspect registry status semantics, current Metis deployments/
        routes/capabilities, shell visibility logic, feature flags, and consumer
        assumptions. Evidence: status current-state report.
  - [ ] M0.2.b Define objective entry/exit criteria for planned, internal,
        preview, limited availability, generally available, paused, and retired
        states. Evidence: lifecycle vocabulary.
  - [ ] M0.2.c Choose Metis launch state/path by host/audience/tenant/region and
        list prerequisites, dependencies, flags, rollback, and owners. Evidence:
        registry decision record.
  - [ ] M0.2.d Ensure shell navigation/capabilities derive from the canonical
        registry decision and never expose planned routes as usable. Evidence:
        shell boundary tests.
  - [ ] M0.2.e Define transition approval/evidence and automatic regression/
        incident behavior before changing the registry. Evidence: ratified
        launch gate.
      <!-- M0.2 measured 2026-08-26 by
      `scripts/v1-workbenches/generate-metis-registry-launch-state.mts`
      (evidence `evidence/v1-workbenches/metis-registry-launch-state.json`,
      9 known-value tests, wired into `verify:inventory:v1-workbenches`).
      0 of 5 children earned; every box stays unchecked.

      THE VEIN: THREE REGISTRIES, AND THEY DO NOT AGREE THAT METIS EXISTS YET.
      `domains.json` carries a `status` field and Metis's value is `active` — so
      is every one of the other 57 domains, which makes a field with exactly one
      value across the whole file a column rather than a decision. The real cut
      sits in a neighbouring key on the same entry:
      `customerReleaseScope: "deferred-to-v1.2"`.
      `libs/oshun/domain-registry/src/registry.ts` carries a different field,
      `availability`, whose union is `'active' | 'beta' | 'planned'`, and Metis
      is `planned` there. `libs/oshun/navigation/src/release-scope.ts` carries a
      third answer: `V1_DEFERRED_DOMAIN_IDS` is `['veritas','metis']`.

      AND THE RECONCILIATION GATE READS TWO OF THE THREE SOURCES.
      `generate-registry-reconciliation.mjs` is real and runs in
      `inventory:v1-workbenches`, comparing `domains.json` against a ledger
      classification table; it reports 4 contradictions today. It never reads
      `availability`, so the one contradiction that decides whether this
      workbench may be exposed is the one it cannot see.

      FIXED: A ROOM HELD OUT OF THE RELEASE WAS REACHABLE THROUGH /operator.
      .d asks that shell navigation never expose planned routes as usable, and
      `apps/oshun/web/src/proxy.ts` — the Next middleware — enforces exactly
      that: it resolves a deferred room from the path, 404s the API and
      redirects the page to `/release-scope/<domain>`, before auth, so the
      refusal is identical for everyone. Its prefix list was
      `['', '/domains', '/d', '/api']`, and
      `apps/oshun/web/src/app/operator/metis/page.tsx` mounts "Metis · operator"
      at `/operator/metis` — inside the proxy's matcher, guarded by nothing in
      `app/operator/layout.tsx`, and invisible to the resolver. `/operator` is
      now a prefix; 11 of the 11 Oshun-hosted Metis routes are covered, and four
      tests in `libs/oshun/navigation/src/release-scope.spec.ts` fail against the
      previous list. The same list is what caught 31 e2e-mined journeys in
      August, so this is the second time the gap was in a prefix nobody thought
      of, not in the rule.

      .b — A STATE A CONSOLE CANNOT REPRESENT IS A STATE A DOMAIN CANNOT BE IN.
      All 7 of .b's lifecycle states are spelled somewhere: 37 declarations
      across `libs/` and `apps/` carry two or more, and `general_availability`
      and `limited_rollout` both appear in `libs/contracts/src/metis/index.ts`.
      No single declaration carries more than 3 of the 7, and the union the
      shell actually consults carries exactly 1 — `planned`. There are no
      entry/exit criteria for any of them.

      .c — A COMMENT FIELD IS NOT A DECISION RECORD. The decision as written is
      `$comment_customerReleaseScope` on the `domains.json` entry. Across 15
      candidate documents all 9 of .c's dimensions are named somewhere, which
      says only that the estate writes about Metis; 0 documents name all 9, and
      the best single one falls short.

      .e — A DRIFT REPORT IS NOT AN APPROVAL. The reconciliation runs in 2
      package scripts and CODEOWNERS covers the registry, which is the half of
      .e that exists. 0 workflows gate a registry change on an approval, the
      report's own summary says contradictions "require human resolution", and
      nothing regresses automatically when the status moves.

      TRAPS THIS CELL PAID FOR. A DIMENSION NAMED SOMEWHERE ACROSS FIFTEEN
      DOCUMENTS IS NOT A DECISION RECORD — asked that way all 9 are present, so
      the measure is the best single document. AND A TWO-LETTER TOKEN IS NOT A
      LIFECYCLE STATE: `ga` is a substring of dozens of identifiers, so
      `generally available` is matched as a phrase or an explicit enum member
      and never as `ga`. -->
- [ ] M0.3 Define creator, instructional designer, teacher, SME,
      psychometrician, accessibility reviewer, integrity reviewer, publisher,
      and tenant-admin roles. Evidence: permission matrix.
      <!-- SPEC 2026-09-11: the service knows three JWT roles (`student`,
      `instructor`, `admin`); the nine roles this cell names exist only as M7
      assignment records. .a–.e are the matrix, drafted as Proposed; .f is
      buildable now as a parametrized suite over the served runtime and is the
      first thing to write, because it will fail on the M0.15.d leak. -->
  - [ ] M0.3.a Define each role’s purpose, eligible actor type, tenant/project/
        course scope, prerequisites, delegation, conflicts, and lifecycle.
        Evidence: role definitions.
  - [ ] M0.3.b Map create/read/edit/comment/review/approve/publish/export/
        configure/adjudicate operations for course/source/lesson/item/rubric/
        assessment/credential records. Evidence: operation matrix.
  - [ ] M0.3.c Map protected answer/rationale/grade/learner/profile/integrity/
        accommodation/PII/psychometric fields to property-level visibility.
        Evidence: property matrix.
  - [ ] M0.3.d Define requester/author/reviewer/approver/publisher separation,
        conflict-of-interest, quorum, substitute, and break-glass rules.
        Evidence: separation policy.
  - [ ] M0.3.e Define tenant-admin boundaries that permit local configuration
        and assignment but not platform floors, other tenants, or hidden
        learner/ integrity data. Evidence: admin boundary tests.
  - [ ] M0.3.f Test direct route/object/property/function, role combination,
        mid-session revocation, and two-tenant authorization. Evidence:
        permission test matrix.
      <!-- M0.3 measured 2026-08-27 by
      `scripts/v1-workbenches/generate-metis-role-permission-matrix.mts`
      (evidence `evidence/v1-workbenches/metis-role-permission-matrix.json`,
      9 known-value tests plus the public-registration schema regression,
      wired into `verify:inventory:v1-workbenches`). 0 of 6 children earned;
      every box stays unchecked.

      THE VEIN: THE PUBLIC DOOR LET A VISITOR WRITE "INSTRUCTOR" ON THEIR OWN
      BADGE. `RegisterRequest` accepted `student | instructor`; the unauthenticated
      `/api/auth/register` route passed `body.role` to `AuthService.register_user`,
      which persisted it and signed `user.role` into the access token.
      `CurrentUser.is_instructor()` treats either `instructor` or `admin` as
      privileged, so a new visitor could mint an instructor token and reach the
      service's instructor-guarded authoring operations. FIXED THIS PASS: public
      registration accepts exactly `student`; `instructor`, `admin`, and
      `super_admin` each fail validation. Administrator/institutional role
      assignment remains separate. The test follows the whole downstream chain
      so widening the public enum cannot later look like an innocent contract
      change.

      .a — SIX IDENTITY ALPHABETS, AND NONE DEFINES THE NINE PEOPLE THE
      WORKBENCH ASKS FOR. The Python principal has student/instructor/admin; the
      internal user schema has the same three; admin management adds moderator;
      the TypeScript gateway has student/instructor/content_creator/admin/
      super_admin; agent visibility has learner/instructor/admin/operator; and
      the learner web profile adds teaching_assistant. Across those six are 9
      distinct values. Only creator (as content_creator) and teacher (as
      instructor) have an identity/authorization spelling. SME and
      psychometrician appear only as curriculum WORKFLOW seats
      (`discipline_methods`, `assessment_calibration`), not assignable
      principals; the other five required roles have neither. 0 declarations
      carry all 9. Across 40 Metis-named document candidates, 0 documents name
      all nine roles and all seven definition dimensions.

      .b — WRITE IS NOT CREATE, EDIT, COMMENT, REVIEW, APPROVE, PUBLISH,
      EXPORT, CONFIGURE, AND ADJUDICATE. The gateway's real RBAC table has 5
      roles and 48 cells over 14 permissions. Its only action vocabulary is
      access/admin/manage/read/write: exactly 1 of the 10 requested operations
      (`read`) is represented by its own decision. Of the 7 requested record
      families, exactly course and assessment are represented directly; source,
      lesson, item, rubric, and credential are not.

      .c — ONE SAFE PROJECTION IS NOT A PROPERTY-BY-ROLE MATRIX. Credit where
      it is due: `QuestionResponse` carries `correct_answer` and `explanation`,
      while `SourceRenderedAssessmentQuestionResponse`, the signed learner
      publication projection, carries neither. That is one correct projection.
      There are 0 complete declarations mapping the 9 protected families to
      roles, and the ordinary assessment reads identified in M0.1 still return
      the answer-bearing response.

      .d — A ROLE CHECK IS NOT SEPARATION WHEN ONE ADMINISTRATOR MAY REVIEW AND
      PUBLISH. The curriculum reviewer pool genuinely requires a quorum of at
      least two and a conflict-free result. The source-publication path limits
      candidate creation to instructor/admin and review/publication to admin,
      but compares creator, reviewer, and publisher identities in 0 of the 3
      required pairings. One administrator may review and publish, and an admin
      creator may do all three. There is no substitute or break-glass rule.

      .e — THE GATEWAY TOKEN NAMES AN ORGANIZATION AND THE SERVICE PRINCIPAL
      DROPS IT. `MetisJwtClaims` requires `organizationId`, but that field is
      never consulted in gateway authorization. The Python `CurrentUser` has
      exactly id/email/role/name, the user aggregate has no tenant/organization
      owner, and tenant-admin is not an authorization role. Agent-runtime
      records do carry tenant IDs; that does not tenant-scope the human
      principal or admin authority.

      .f — 5 of 7 requested test dimensions have at least one textual candidate:
      direct route, object, function, role combination, and two-tenant. Property
      authorization and mid-session role revocation have none, and no suite
      generates the required role x operation x object x property x tenant
      matrix. The new public-registration regression closes one escalation path;
      it does not make this matrix complete.

      TRAPS THIS CELL PAID FOR. A ROLE WORD IS NOT AN IDENTITY ROLE — SME and
      psychometrician exist as review-seat labels but cannot authenticate. A
      WORD IN A DOCUMENT IS NOT A ROLE DEFINITION — the best Metis document
      names only 3 of 9 roles and 4 of 7 definition dimensions. AND THREE
      PROTECTED FIELD NAMES BESIDE THREE ROLE WORDS IS NOT A PROPERTY MATRIX:
      broad co-occurrence finds many long schemas/services, so the artifact
      records those as rejected lexical candidates and reports 0 complete
      matrices. -->
- [ ] M0.4 Separate authoring data, learner data, assessment evidence, grades,
      integrity records, and institutional configuration. Evidence: data
      ownership and classification map.
  - [ ] M0.4.a Inventory entities/fields/stores/events/APIs/search/indexes/
        exports/analytics/backups for each data category. Evidence: data
        inventory.
        <!-- evidence: metis-data-model.json — 14 SQLAlchemy entities (141 fields,
                                                                                                                                                                                                                                                                        16 FKs, 14 relationships) with per-field type/nullability/index/foreign-key,
                                                                                                                                                                                                                                                                        grouped by M0.4 data category (learner-data 5, assessment-evidence 3,
                                                                                                                                                                                                                                                                        authoring-data 3, identity 1, institutional-config 1). exports/analytics/
                                                                                                                                                                                                                                                                        backups + owner/classification are the M0.4.b-e governance half.
                                                                                                                                                                                                                                                                        generate-metis-data-model.mjs + 6 tests. -->
  - [ ] M0.4.b Assign canonical owner/store and classification, purpose,
        lawful/policy basis, access roles, encryption, retention/deletion, and
        residency per category. Evidence: ownership/classification map.
  - [ ] M0.4.c Define minimal versioned projections from
        learner/grade/integrity/ institutional owners into authoring previews
        and simulations. Evidence: projection contracts.
  - [ ] M0.4.d Prohibit copying raw learner history, grades, diagnoses,
        accommodations, integrity evidence, or institution secrets into course/
        item authoring records. Evidence: schema/property tests.
  - [ ] M0.4.e Define cross-owner identifiers, transactions/events, correction/
        deletion/legal-hold propagation, and audit without shared authority.
        Evidence: boundary flow tests.
  - [ ] M0.4.f Threat-test search, export, logs, analytics, fixtures, backups,
        tutor prompts, and previews for cross-category leakage. Evidence: data
        separation suite.
      <!-- M0.4 re-audited 2026-08-27 by
      `scripts/v1-workbenches/generate-metis-data-ownership-boundary.mts`
      (evidence `evidence/v1-workbenches/metis-data-ownership-boundary.json`,
      10 known-value/trap tests plus 2 ORM boundary tests, wired into
      `verify:inventory:v1-workbenches`). 0 of 6 children earned; every box is
      unchecked, including M0.4.a, whose earlier evidence did not cover the
      surfaces named by the cell.

      THE VEIN: RESPONSE OMISSION HAPPENED AFTER PROTECTED DATA HAD ALREADY
      CROSSED THE BOUNDARY. `User` used eager `selectin` loading for authored
      courses, enrollments, submissions, tutoring sessions, and achievements.
      `Course.enrollments`, `Lesson.progress_records`, and
      `Assessment.submissions` did the same. A login/identity lookup therefore
      fetched the learner's whole adjacent graph; an authoring course read
      fetched enrollments/progress/submissions even when its DTO omitted them.
      FIXED THIS PASS: all 8 cross-owner relationships use `lazy="raise"` and
      require a caller to opt in with an explicit loader. Same-owner authoring
      edges remain explicitly eager, and all 443 Metis tests pass.

      TWO MORE CONTAINMENT FIXES. The tutor prompt previously received exact
      progress percentage, completed/total lesson counts, time spent, latest
      score, and per-attempt score/pass strings. It now receives versioned
      `progress_band` and `performance_band` projections plus purpose-relevant
      aggregate/topic signals; regression assertions prove raw metrics are not
      rendered. The `assessment_submitted` application log no longer records
      learner ID, score, pass state, or attempt number.

      .a — A MODEL PARSER IS NOT THE INVENTORY THE CELL NAMES. The checked
      `metis-data-model.json` reads only `models/*.py`. It covers entities,
      fields, tables/stores, and field indexes: 4 of the required 9 surfaces.
      It does not inventory events, APIs, exports, analytics, or backups. Its
      module-wide categories also hide important contradictions: `Submission`
      is labeled assessment-evidence while one table co-locates learner
      answers, grades, and an academic-integrity verdict; grades and integrity
      therefore have no independent category. `XapiStatementRecord` is labeled
      institutional-config even though it stores learner identity/activity and
      result JSON. The old M0.4.a check is revoked rather than grandfathered.

      .b — 0 documents assign all 9 required owner/classification dimensions
      across all 6 categories. Metis deployment notes mention fragments such as
      classification, retention, and residency; they do not assign canonical
      owner/store, purpose and basis, access roles, encryption, deletion, and
      residency per category.

      .c — The new tutor projection is genuinely versioned and minimal, but its
      destination is a learner tutor prompt. There are 0 versioned contracts
      from learner/grade/integrity/institutional owners into authoring previews
      or simulations.

      .d — Making 8 ORM edges explicit prevents accidental graph traversal; it
      is not a record/property prohibition. There are 0 schema/property suites
      rejecting raw protected payloads placed into authoring records, so this
      remains open.

      .e — The current estate has 20 identifier-like cross-record fields, but 0
      cross-owner event contracts and 0 legal-hold, deletion-propagation, or
      correction-propagation declarations. Foreign keys and cascading deletes
      are shared database authority, not a boundary protocol.

      .f — Textual test candidates touch 5 of 8 named channels, but logs,
      fixtures, and backups have none and there is no unified leakage suite.
      This pass closes the observed application-log and tutor-prompt leaks; it
      does not claim that two repairs threat-test the estate.

      TRAPS THIS CELL PAID FOR. AN ENTITY'S MODULE NAME IS NOT ITS DATA OWNER:
      a learner xAPI record does not become institutional configuration because
      it lives in `institutional_delivery.py`. A FIELD ENUMERATION IS NOT A
      SURFACE INVENTORY: explicitly naming exports/analytics/backups as later
      "governance" work contradicted M0.4.a itself. AND DTO OMISSION IS NOT
      SEPARATION when the ORM eagerly fetched the protected graph first. -->
- [ ] M0.5 Decide Metis persistence ownership and database strategy. Evidence:
      ADR resolves shared `oshun_dev` versus a dedicated database.
      <!-- 2026-08-27 audit and decision pass: 4/5 child properties are now
      evidenced by metis-persistence-inventory.json,
      metis-persistence-strategy.json, and
      docs/adr/ADR-M0.5-metis-persistence-ownership.md. The selected target is a
      bounded hybrid: a dedicated Metis database for Metis-owned aggregates,
      with externally owned identity and source records crossing the boundary
      only by canonical ID, event, or rebuildable projection. The ADR is
      deliberately Proposed with 0/6 approvals, so ratification and this parent
      cell remain open. -->
  - [x] M0.5.a Inventory current schemas/tables/migrations/connections/
        repositories/data volumes/tenancy/backups and production consumers.
        Evidence: persistence inventory.
        <!-- evidence: metis-persistence-inventory.json v2 explicitly covers all
        9 named surfaces: one 2-migration Alembic chain, 13 tables in implicit
        public on shared oshun_dev, 4 source engine consumers, 4 Compose
        consumers, no repository layer, the shared PostgreSQL volume plus Redis/
        object/local adjacent stores, 0/13 tenant-keyed tables, global backup/
        restore posture, and 5 production source/deployment consumers. Production
        row and byte volumes are explicitly unknown rather than reported as zero.
        generate-metis-persistence-inventory.mjs + 9 tests. -->
  - [x] M0.5.b Define aggregate ownership and transaction/consistency/
        performance/retention/residency/security requirements for Metis
        authoring and protected adjacent data. Evidence: requirements matrix.
        <!-- evidence: ADR-M0.5 requirements matrix has 10 complete aggregate
        rows across canonical owner/store, classification, purpose, policy basis,
        access, transaction, consistency, performance, retention/deletion,
        residency, and security; parsed into metis-persistence-strategy.json. -->
  - [x] M0.5.c Compare shared database, dedicated database, and bounded hybrid
        options for isolation, operational cost, migrations, joins/events,
        backup/restore, scaling, and ownership. Evidence: option analysis.
        <!-- evidence: ADR-M0.5 compares exactly the three requested options
        across all 7 requested dimensions and selects bounded-hybrid; the parsed
        option table has one selected row. -->
  - [x] M0.5.d Decide canonical IDs and cross-database references/events,
        avoiding distributed transactions and shadow copies where possible.
        Evidence: integration design.
        <!-- evidence: ADR-M0.5 records 8 integration decisions: canonical IDs,
        cross-database references, event envelope, transactional outbox/inbox,
        projections, correction/deletion/hold propagation, shadow-copy
        prohibition, and the mixed Submission split. -->
  - [ ] M0.5.e Ratify an ADR selecting the owner/database strategy, migration/
        rollback path, operational owner, and rejected alternatives. Evidence:
        approved ADR.
        <!-- open honestly: the Proposed ADR includes a 6-phase migration/
        rollback plan, 6 operational ownership rows, and rejected alternatives,
        but all 6 named human reviewer approvals remain pending. Proposed is not
        Accepted. -->
        _2026-09-18: the packet exists (the Proposed ADR, its migration plan and
        its rejected alternatives); what is left is the six named approvals,
        which no agent may supply (rule 9)._ `blocked:human`
- [x] M0.6 Map all `libs/metis/*` packages and canonical contracts to workbench
      capabilities. Evidence: complete package ledger.
      <!-- evidence: metis-package-inventory.json v2 records all 28 packages and
           6,651 reviewed public capabilities with complete four-audience
           mappings, owner/API/ledger links, dispositions, and bidirectional
           drift controls. package-capability-decisions.json is the explicit,
           hash-ratcheted decision source. -->
  - [x] M0.6.a Enumerate package manifests/exports/source/tests/dependencies/
        consumers/build targets/owners and detect nested or untracked packages.
        Evidence: deterministic package inventory.
        <!-- evidence: all 28 direct libs/metis/* manifests with public exports,
             source, 341 tests, declared/source dependencies, consumers, build
             targets, and owners; 0 nested, untracked, or duplicate manifests. -->
  - [x] M0.6.b Extract public contracts/entities/operations/events/jobs/gates/
        adapters from each package and link their exact source locations.
        Evidence: package capability catalog.
        <!-- evidence: all 6,651 reviewed public symbols classified across the
             seven requested kinds and linked to exact declaration/re-export
             file and line; 0 fallback or unanchored entries. -->
  - [x] M0.6.c Map every package capability to workbench/learner/operator/
        institutional feature, canonical owner contract, route/API, and ledger
        parent task. Evidence: package ledger.
        <!-- evidence: every capability inherits an explicit workbench, learner,
             operator, and institutional feature profile plus canonical owner
             contract, resolvable API surface, and resolvable parent task. -->
  - [x] M0.6.d Assign reuse, refactor, move, merge, deprecate, retire, or
        net-new gap disposition with owner/evidence for duplicates and orphan
        packages. Evidence: disposition review.
        <!-- evidence: 9 reuse, 9 refactor, 5 move, 4 merge, and 1 retire
             decisions cover all packages, including 12 no-consumer candidates
             and 8 reviewed semantic duplicate groups; 0 unresolved orphans. -->
  - [x] M0.6.e Add bidirectional drift checks so new exports/packages or ledger
        capabilities cannot remain unmapped. Evidence: zero-orphan audit.
        <!-- evidence: 28/28 package decisions and all 6,651 public surfaces are
             count/hash pinned; missing, extra, changed, unanchored, broken-
             reference, and unmapped audits are all zero. -->
- [x] M0.7 Map existing course creation, BYOM, ingest, assessment, tutor,
      gradebook, and integrity routes. Evidence: reuse/retire/gap dispositions.
      <!-- evidence: metis-api-inventory.json v2 maps all 254 real service,
           BFF, and frontend routes through 28 reviewed, hash-pinned flow
           profiles. The ledger explicitly preserves all observed control gaps;
           completing M0.7 records their dispositions, not their remediation. -->
  - [x] M0.7.a Inventory frontend/BFF/service routes, methods, schemas,
        handlers, auth/roles, data owners, consumers, tests, and live/fixture
        status for each capability family. Evidence: route inventory.
        <!-- evidence: Python AST extraction finds 183 canonical Metis service
             operations plus 2 separate Manim-service routes, excluding 2 old
             docstring false positives; 30 BFF registrations and 39 frontend
             pages complete the 254-route inventory with exact lines, tests,
             consumers, schemas/roles, and honest live/fixture status. -->
  - [x] M0.7.b Trace each route through owner service/store/provider and
        classify authoring, learner delivery, educator operations,
        institutional, or adjudication authority. Evidence: route flow map.
        <!-- evidence: 254/254 rows map canonical contract, owner service/store,
             providers, and one of the six authority classes (including the
             separately required infrastructure class). -->
  - [x] M0.7.c Compare routes against canonical contracts and requirements for
        tenancy, property auth, idempotency, audit, protected data, and honest
        failure states. Evidence: gap analysis.
        <!-- evidence: all 254 rows compare 8 controls. Gaps remain explicit:
             authentication 35, tenancy 184, property authorization 84,
             idempotency 97, audit 92, protected projection 11, canonical
             contract 28, and honest failure 62. -->
  - [x] M0.7.d Assign per-route reuse, refactor, facade, redirect, retire,
        replace, or net-new disposition with compatibility/consumer migration.
        Evidence: route disposition ledger.
        <!-- evidence: every route has a target/rationale, compatibility plan,
             and consumer migration: 233 refactor, 19 facade, 2 reuse. Two
             reviewed BFF helper modules with zero HTTP registrations retire. -->
  - [x] M0.7.e Add route/consumer/ledger traceability checks and negative
        boundary tests for answer keys, grades, integrity records, and BYOM
        providers. Evidence: route coverage gate.
        <!-- evidence: 28/28 source/surface profiles are count/hash pinned; all
             20 frontend API calls resolve by method/path exactly once; missing,
             extra, duplicate, drift, reference, unresolved/ambiguous-consumer,
             and sensitive-boundary violations are zero. Negative tests cover
             6 answer-key, 6 grade, 11 integrity, and 3 BYOM-sensitive routes. -->
- [x] M0.8 Define content hierarchy: source collection, course, module, lesson,
      activity, assessment, section, item, stimulus, rubric, objective,
      standard, and prerequisite. Evidence: hierarchy contract.
      <!-- evidence: @oshun/contracts/metis content-hierarchy.ts defines all 13
           kinds in one strict versioned contract; generated evidence reports
           all 6 M0.8 controls present with zero drift. -->
  - [x] M0.8.a Define stable identity/version/tenant/owner/lifecycle/common
        metadata for every hierarchy entity. Evidence: base entity schema.
        <!-- evidence: the full golden carries all 13 kinds (16 version rows),
             and every row parses through the shared 8-field base envelope plus
             9-field common metadata contract. -->
  - [x] M0.8.b Define allowed containment/cardinality/order/reuse relations from
        source collections through courses/modules/lessons/activities/
        assessments/sections/items. Evidence: containment matrix.
        <!-- evidence: 10 allowed ordered parent/child rules state min/max and
             reuse; validators enforce allowed pairs, required cardinality,
             dense zero-based order, unique edges, reference-only multi-parent
             reuse, exact revisions, tenant binding, and acyclic containment. -->
  - [x] M0.8.c Define stimulus-to-item and rubric-to-item/assessment reuse with
        exact revision pinning and no implicit answer exposure. Evidence: reuse
        invariants.
        <!-- evidence: exact content-hash/revision pins cover stimulus→item and
             rubric→item/assessment; item/assessment answer bytes are replaced
             by metis-assessment-security never-inline refs. Strict schemas and
             negative goldens reject inline answers and drifted hashes. -->
  - [x] M0.8.d Define objective/standard alignment and prerequisite graph edges,
        scope, weight/coverage, versions, and cycle/orphan semantics. Evidence:
        graph contracts.
        <!-- evidence: alignment edges pin objective, standard, and scope with
             weight/coverage ≤1; versioned prerequisite entities pin from/to/
             scope with required/recommended strength. Orphans, self-edges,
             cycles, and over-coverage fail. -->
  - [x] M0.8.e Define clone/reference/embed/fork/supersede/archive/delete/
        restore behavior and downstream invalidation per relationship. Evidence:
        lifecycle invariants.
        <!-- evidence: an 8-row policy fixes target identity/revision, upstream
             tracking, lifecycle state, and exact downstream invalidation for
             every named operation; invalidation drift fails validation. -->
  - [x] M0.8.f Publish schemas/goldens for minimal/full/reused/version-diverged/
        invalid hierarchies and reconcile existing shapes. Evidence: hierarchy
        contract suite.
        <!-- evidence: 4 valid goldens and 6 intended-invalid goldens pass their
             expected acceptance/rejection checks. A 13-row reconciliation maps
             every kind to current TS/Python shapes or an explicit net-new gap;
             all source/symbol anchors resolve. Package tests (10) + generator
             tests (7) and a root inventory --check gate keep the contract current. -->
- [x] M0.9 Define the authoring maturity model independently from learner
      progress and grade states. Evidence: vocabulary collision review.
      <!-- evidence: @oshun/contracts/metis authoring-maturity.ts plus the
           generated collision review inventory 27 current vocabularies in all
           9 requested categories, with 30 normalized collisions and zero drift. -->
  - [x] M0.9.a Inventory current authoring, review, publication, learner
        progress, attempt, mastery, completion, grade, and integrity status
        vocabularies with owners. Evidence: vocabulary inventory.
        <!-- evidence: every row names its declaring owner, canonical owner,
             authority/disposition, source, symbol, labels, and rationale; all
             27 source/symbol/label anchors resolve. -->
  - [x] M0.9.b Define authoring maturity states/transitions for draft, in
        review, changes requested, approved, scheduled, published, superseded,
        archived, quarantined, and retired as applicable. Evidence: state
        machine.
        <!-- evidence: the independent 10-state machine has 14 named policies,
             reaches every state, starts at draft, and terminates at retired. -->
  - [x] M0.9.c Define transition actors, prerequisites, guards, concurrency,
        version effects, audit, rollback/supersession, and invalidation.
        Evidence: transition matrix.
        <!-- evidence: every transition fixes allowed actors, exact prerequisite
             evidence, tenant/version/idempotency/separation guards, content-
             revision effect, audit binding, rollback effect, and exact
             downstream invalidations. All 14 conforming transitions parse;
             stale version, missing evidence, forged creator, self-approval,
             and invalidation drift fail. -->
  - [x] M0.9.d Keep learner progress/attempt/mastery/completion and grade/
        integrity state in their canonical owner vocabularies and projections.
        Evidence: separation contracts.
        <!-- evidence: 6 protected owners keep revisioned projections only:
             learning progress, assessment attempts, mastery, completion,
             gradebook, and Themis integrity. Each has a distinct analytics
             namespace; cross-domain mutation tuples are structurally empty. -->
  - [x] M0.9.e Detect identical/ambiguous labels, enum/code reuse, API/UI
        mapping, analytics conflation, and migration risks. Evidence: collision
        review.
        <!-- evidence: the artifact reviews all 30 case/separator-normalized
             collisions, publishes 27 complete legacy API/UI mappings, requires
             owner+vocabulary analytics dimensions, and records 7 concrete
             migration risks/mitigations. -->
  - [x] M0.9.f Test no authoring transition can directly mutate learner/grade/
        integrity state and vice versa. Evidence: boundary tests.
        <!-- evidence: strict authoring and protected-owner event schemas reject
             foreign mutation fields in both directions; package tests (9) and
             deterministic generator tests (8) cover the boundary and zero-
             drift evidence. -->
- [x] M0.10 Threat-model prompt injection, poisoned source material, answer-key
      leakage, test theft, bias, inaccessible items, model cheating, PII,
      minors, BYOM abuse, and grade tampering. Evidence: mitigation/test map.
      <!-- evidence: @oshun/contracts/metis threat-model.ts plus generated
           metis-threat-model.json define 60 owned threat cases in 7 categories,
           23 anchored controls, 7 incident playbooks, and 7 launch gates with
           zero vector/reference/anchor drift. Six gates remain honestly blocked
           and the academic-integrity gate conditional; the model does not turn
           partial controls into a false launch green. -->
  - [x] M0.10.a Map assets, actors, trust boundaries, entry points, providers/
        models/tools, data stores, exports, integrations, and high-impact
        actions. Evidence: threat model context.
        <!-- evidence: 78 owned context items cover all 9 named dimensions:
             9 assets, 12 actors, 10 trust boundaries, 11 entry points, 7
             providers/models/tools, 8 stores, 6 exports, 6 integrations, and
             9 high-impact actions. -->
  - [x] M0.10.b Model injection/poisoning through uploads, URLs, LMS/repos,
        extracted text, citations, prompts, BYOM responses, tutor context, and
        generated media/code. Evidence: injection threat cases.
        <!-- evidence: 11 explicit cases cover upload, URL, LMS, repository,
             extracted text, citation, prompt, BYOM response, tutor context,
             generated media, and generated code paths. -->
  - [x] M0.10.c Model answer/rationale/rubric/test-bank exposure and assessment
        theft through
        UI/API/search/export/cache/logs/analytics/tutor/accessibility
        projections. Evidence: content security cases.
        <!-- evidence: 9 cases each bind all 4 protected material classes to one
             named exposure surface; protected references exist, while the gate
             blocks until cross-surface no-secret tests cover all 9 surfaces. -->
  - [x] M0.10.d Model bias/unfair/inaccessible items, psychometric misuse,
        disability inference, translation drift, cultural harm, and automation
        overclaim. Evidence: fairness/accessibility cases.
        <!-- evidence: 8 cases cover bias, unfair and inaccessible items,
             psychometric misuse, disability inference, translation drift,
             cultural harm, and automation overclaim with explicit limitations. -->
  - [x] M0.10.e Model learner model-assisted cheating, impersonation, collusion,
        integrity evidence manipulation, false accusation, and appeal denial.
        Evidence: integrity cases.
        <!-- evidence: 6 cases bind learner-safe evidence, probabilistic verdict
             limitations, independent human review, non-conflicted appeal, and
             grade correction recovery; the launch gate is conditional. -->
  - [x] M0.10.f Model PII/minor/guardian/educational record/biometric/voice data
        exposure, overretention, cross-purpose use, and cross-tenant access.
        Evidence: privacy cases.
        <!-- evidence: 9 critical cases cover direct PII, minor, guardian,
             educational, biometric, voice, retention, purpose, and tenant
             boundaries; M0.11 lifecycle/policy ratification remains a launch
             blocker rather than being silently assumed. -->
  - [x] M0.10.g Model BYOM credential/model/provider/endpoint/SSRF/spend/
        availability/supply-chain abuse and unsafe outputs. Evidence: BYOM
        cases.
        <!-- evidence: 8 cases cover credentials, model, provider, SSRF, spend,
             availability, supply chain, and unsafe output. Existing sandbox,
             interlock, audit, quarantine, and kill controls are mapped; private-
             network SSRF, secret storage, and hard spend ceilings block launch. -->
  - [x] M0.10.h Model grade/credential/approval/publication tampering, replay,
        mass assignment, concurrency, confused deputy, and audit deletion.
        Evidence: authority cases.
        <!-- evidence: 9 cases cover every named authority vector and map guarded
             authoring transitions, tenant/idempotent grade corrections, signed
             credentials, and Merkle audit anchors; unresolved M0.3 authority and
             write-path atomicity remain explicit launch blockers. -->
  - [x] M0.10.i Assign prevention/detection/response/recovery control, owner,
        residual risk, verification, incident playbook, and launch gate to every
        threat. Evidence: mitigation/test traceability matrix.
        <!-- evidence: every one of 60 cases has nonempty prevention, detection,
             response, and recovery assignments; owned controls with exact source
             and test anchors; residual risk/review trigger; verification IDs;
             a category-matched 7-step incident playbook; and a derived launch
             gate. Package tests (14) + generator tests (11) reject dangling,
             phase-incompatible, missing-vector, duplicate, and false-green data. -->
- [ ] M0.11 Define regional education/privacy obligations and institutional
      policy hooks. Evidence: legal/privacy review with data-retention rules.
      <!-- evidence: `metis-regional-policy.json` is the deterministic technical
           review packet. M0.11.a/c/d/e are complete, but the parent remains open:
           all 8 applicability cells and the approval record fail closed pending
           the real named counsel/privacy review required by M0.11.b/f. -->
  - [x] M0.11.a Inventory intended regions/jurisdictions, learner age groups,
        institution types, data subjects, delivery modes, vendors, and data
        transfers. Evidence: applicability inventory.
        <!-- evidence: 38 owned scope items across all 7 named dimensions, with
             candidate/pending status, explicit assumptions, unknown-age and
             unknown-jurisdiction fail-closed entries, and 8 launch cells. -->
  - [ ] M0.11.b Record counsel/privacy-reviewed education, child/minor,
        accessibility, assessment, records, consent, AI, biometric/voice,
        residency, and deletion obligations per applicability cell. Evidence:
        obligation matrix.
        <!-- blocker: every cell carries all 11 review categories and open review
             questions, but none is mislabeled reviewed; no named counsel/privacy
             reviewer or reviewed contract version has been supplied. -->
        _2026-09-18: the matrix carries every category and its open questions;
        what is left is a named counsel or privacy reviewer reading it (rule
        9)._ `blocked:human`
  - [x] M0.11.c Define code-owned nonwaivable floors and UI-governed institution
        policy hooks for retention, access, approvals, proctoring, integrity,
        providers, exports, locales, and communications. Evidence: policy
        schema.
        <!-- evidence: 16 strict nonwaivable rules cover all 9 domains, each with
             a code owner, UI hook, constraint direction, and platform value. -->
  - [x] M0.11.d Define precedence/merge semantics so institution/region policies
        can tighten but never loosen applicable platform/legal floors. Evidence:
        monotonicity tests.
        <!-- evidence: platform→region→institution merge rejects maximum,
             minimum, allowed-set, required-set, invariant, duplicate-set,
             unknown-rule, missing-floor, and reversed-precedence violations;
             14 package tests plus 10 generator tests pin the behavior. -->
  - [x] M0.11.e Define retention/deletion/legal hold/correction/export/guardian/
        consent workflows and downstream processor propagation. Evidence:
        lifecycle rules.
        <!-- evidence: 7 lifecycle state workflows define triggers,
             prerequisites, conflicts, audit events, deadlines, receipts, and 31
             propagation actions across 8 processor classes; destructive deletion
             is refused under legal hold and rights workflows require authority. -->
  - [ ] M0.11.f Obtain named legal/privacy review with version/effective date,
        assumptions, open issues, and reevaluation triggers. Evidence: approval
        record.
        <!-- blocker: the versioned packet records 3 assumptions, 4 open issues,
             and 5 reevaluation triggers, but intentionally has no fabricated
             reviewer, review date, effective date, or approval decision. -->
        _2026-09-18: the versioned packet exists; what is left is the named
        reviewer, the dates and the decision (rule 9)._ `blocked:human`
- [ ] M0.12 Define success measures for authoring quality, accessibility,
      psychometrics, time-to-publish, interoperability, and learner outcomes.
      Evidence: metric definitions avoid incentivizing harmful shortcuts.
      <!-- evidence: `metis-success-metrics.json` is the deterministic proposed
           catalog. M0.12.a-f are technically complete with 32 metrics across 6
           families, 12 exact source/test anchors, 4 outcome hypotheses, and
           fail-closed privacy/staleness/guardrail evaluation. The parent remains
           open because M0.12.g requires real accountable-owner ratification. -->
  - [x] M0.12.a Define authoring quality measures tied to source grounding,
        objective coverage, correctness, review revisions, gate outcomes, and
        post-publication corrections. Evidence: quality metric specs.
        <!-- evidence: 6 authoring-quality metrics separately measure grounding,
             objective coverage, correctness, review revision, release-gate
             passage, and post-publication correction, each with countermetrics. -->
  - [x] M0.12.b Define accessibility coverage/defect/learner-use measures by
        item kind, content type, accommodation, locale, severity, and
        resolution. Evidence: accessibility metric specs.
        <!-- evidence: 5 accessibility metrics all require all 6 named slicing
             dimensions; coverage, open/critical defects, resolution time, and
             consented learner-use success are distinguished. -->
  - [x] M0.12.c Define psychometric measures with sample/uncertainty/privacy,
        difficulty/discrimination/reliability/DIF/exposure and no small-group
        misuse. Evidence: psychometric metric specs.
        <!-- evidence: 5 psychometric metrics pin difficulty, discrimination,
             reliability, DIF, and exposure with required uncertainty methods,
             aggregate N >= 100, subgroup N >= 30, privacy suppression, and a
             prohibition on individual high-stakes automation. -->
  - [x] M0.12.d Define time-to-publish stage durations/rework/wait/abandonment
        with quality/safety/accessibility guardrails so speed cannot reward
        bypass. Evidence: flow metric specs.
        <!-- evidence: 5 flow metrics cover stage cycle, wait, rework,
             abandonment, and accepted throughput. Every result is withheld on
             correctness, release-gate, or critical-accessibility failure and
             every metric rejects standalone incentive use. -->
  - [x] M0.12.e Define standards/package/LMS/import/export compatibility and
        semantic round-trip measures per version/profile. Evidence:
        interoperability metric specs.
        <!-- evidence: 6 interoperability metrics cover profile conformance,
             package validation, import/export fidelity, semantic round-trip,
             and LMS exchange, sliced by pinned standard/profile/vendor version. -->
  - [x] M0.12.f Define learner-outcome hypotheses and measures with baseline,
        confounders, consent/privacy, subgroup uncertainty, causal limitations,
        and no individual high-stakes automation. Evidence: outcome metric
        review.
        <!-- evidence: 5 aggregate outcome metrics and 4 explicit hypotheses pin
             baselines, at least 5 confounders, consent, privacy thresholds,
             uncertainty, no causal claim, and no individual high-stakes use. -->
  - [ ] M0.12.g Define owners, sources, formulas/denominators, freshness,
        privacy thresholds, targets, countermetrics, and review cadence.
        Evidence: ratified metric catalog.
        <!-- blocker: all 32 proposed specs contain the required catalog fields,
             but all targets remain proposed and the versioned approval record
             intentionally has no fabricated product, learning-science,
             psychometric, accessibility, privacy, interoperability, or data-
             governance reviewer/effective date. -->
        _2026-09-18: all 32 specs carry the catalog fields; what is left is
        ratification of the proposed targets by named reviewers (rule 9)._
        `blocked:human`
- [ ] M0.13 Author and ratify
      `V1/METIS_COURSEWARE_WORKBENCH_DESIGN_2026-07-23.md` with current-state,
      host, roles, pedagogy, data, API, standards, UI, trust, migration,
      rollout, and decision records. Evidence: approved doc and resolved review
      comments.
      <!-- evidence: the generated design now exists with 11 explicit current-
           state rows, 6 governed journeys, 7 unresolved ADRs, and 33 hashed
           source links. M0.13.a-e are technically complete; parent/f remain
           open because no named cross-functional reviewers approved an
           effective revision or resolved a review-comment log. -->
  - [x] M0.13.a Establish document scope, requirements traceability, owners,
        reviewers, source links, terminology, decision log, and evidence format.
        Evidence: design-doc outline.
        <!-- evidence: §1 defines scope, accountable team functions, terminology,
             evidence/comment formats, a-f traceability, ADR format, and a
             content-addressed source ledger without inventing named reviewers. -->
  - [x] M0.13.b Document current apps/routes/packages/contracts/stores/flows/
        tests/deployments and explicitly separate live, partial, fixture, and
        proposed behavior. Evidence: current-state review.
        <!-- evidence: §2 classifies 11 rows across all 4 required truth states,
             including 39 routes/4 surfaces, 28 packages/6,651 capabilities,
             183 API operations, 13 tables, 547 app/library test files, proposed
             hierarchy/maturity/policy/metrics, and explicit deployment gaps. -->
  - [x] M0.13.c Document canonical hosts/audiences/roles/boundaries, pedagogy/
        content model, data ownership/contracts/APIs/jobs/events, and standards
        profiles. Evidence: architecture sections.
        <!-- evidence: §3 records all 6 audiences and 9 required roles, denies a
             false canonical-host claim, defines pedagogy/content/data/API/job/
             event boundaries, and separates implemented standard operations
             from still-pending conformance profiles. -->
  - [x] M0.13.d Document UI workbench journeys/responsive/accessibility/offline/
        localization, trust/safety/privacy/integrity/rights, and evaluation/
        observability/operations. Evidence: experience/trust sections.
        <!-- evidence: §4 follows the restrained operational-app thesis and
             defines 6 end-to-end journeys plus desktop/tablet/mobile, keyboard,
             screen-reader, reduced-motion, offline, localization, trust,
             metric, observability, incident, and operations behavior. -->
  - [x] M0.13.e Document migration/compatibility/rollout/flags/deprecation/
        rollback/readiness and all unresolved options as explicit ADRs.
        Evidence: migration/decision sections.
        <!-- evidence: §5 defines a 7-phase reversible rollout, compatibility and
             default-off flag rules, deprecation evidence, and 7 owned ADRs for
             host, roles, persistence, data, standards, experience, and rollout. -->
  - [ ] M0.13.f Review against feature inventory/deep dives/code/contracts and
        resolve or disposition every comment with named domain/security/privacy/
        accessibility/engineering/product ratification. Evidence: approved doc.
        <!-- blocker: §6 pins all source versions and six required review roles,
             but approvals=0, effective date=none, and no review comments may be
             fabricated by the generator. -->
        _2026-09-18: the document pins its sources and names six review roles;
        what is left is the six approvals (rule 9)._ `blocked:human`
- [ ] M0.14 Reconcile `V1/features/domain-metis.md`, the V1 feature inventory,
      deep-dive specifications, standalone apps, service OpenAPI, and every
      `libs/metis/*` capability at atomic-feature level. Evidence: explicit
      dispositions include course authoring, curriculum discovery, assessments,
      tutoring/live voice, mastery/adaptation, knowledge graphs, credentials,
      prerecorded lectures, agentic media, correctness verification, gradebook,
      integrity, institutional interoperability, administration, and
      training-data flywheel.
      <!-- evidence: metis-feature-reconciliation.json + its hashed rows.jsonl
           map 10,021 source-native feature rows bidirectionally to 30 canonical capabilities with 0
           source or canonical orphans. M0.14.a-f are technically complete;
           M0.14.g and the parent remain open because the zero-orphan report has
           no real owner approval or effective date. -->
  - [x] M0.14.a Build deterministic atomic-feature inventories from each named
        document, app/route, OpenAPI operation/schema, package export,
        job/event, and test with source anchors. Evidence: source inventories.
        <!-- metis-code-census.json: 11 artifact classes with source anchors and
             exact file lists where the upstream evidence does not already own
             atomic rows: 51 documents; 39 app routes; 183 OpenAPI operations,
             174 with response schemas; 558 schemas; 6,651 exports across 28
             packages; 14 entities / 141 fields over 13 tables / 2 migrations;
             296 atomic job/event entries across 63 source files (286 public
             capabilities + 10 runtime modules); 547 app/library tests; 906 implementation files
             (180 py + 717 ts + 9 js); 5 unique apps + 28 libs. Generator + 8
             known-value tests are wired into inventory/verify. -->
  - [x] M0.14.b Normalize aliases only through reviewed mappings while retaining
        source-specific identity and semantics. Evidence: alias map.
        <!-- evidence: feature-alias-decisions.json defines 30 unique, repository-
             reviewed canonical mappings grounded in six source documents. The
             generator retains original source/ref/line/text and permits only a
             reviewed alias, reviewed package profile/API family, or explicit
             platform fallback; M0.14.g owner approval is not implied. -->
  - [x] M0.14.c Map course/curriculum/assessment/item/rubric/source and
        standards authoring features bidirectionally to canonical capabilities.
        Evidence: core authoring rows.
        <!-- evidence: 1,323 source rows map bidirectionally to 8 core-authoring
             capabilities with 1,042 implementation rows and explicit authoring/
             delivery authority boundaries; every named capability is nonempty. -->
  - [x] M0.14.d Map tutoring/live voice, learner modeling/mastery/adaptation,
        knowledge graphs, simulations, correctness verification, and agentic
        media features with authoring-versus-delivery authority. Evidence:
        intelligent learning rows.
        <!-- evidence: 3,418 source rows map bidirectionally to 8 intelligent-
             learning capabilities with 2,967 implementation rows; every row
             resolves to distinct authoring and learner/runtime authorities. -->
  - [x] M0.14.e Map credentials, prerecorded lectures, gradebook, integrity,
        institutional interoperability/administration, analytics, and training-
        data flywheel features with data/authority boundaries. Evidence:
        institutional rows.
        <!-- evidence: 886 source rows map bidirectionally to all 8 named
             institutional capabilities with 709 implementation rows and explicit
             grade, integrity, credential, institution, analytics, and governed-
             training authority boundaries. -->
  - [x] M0.14.f Assign reuse/refactor/move/merge/retire/net-new/defer/reject
        disposition, canonical owner, parent/child task, dependencies, and proof
        to every source feature. Evidence: disposition ledger.
        <!-- evidence: all 10,021 source rows carry an allowed disposition,
             canonical owner, M0.14 child, downstream parent tasks, dependency
             list, and exact proof anchor; 111 broad/future/unsupported document
             assertions are explicitly deferred rather than counted as code. -->
  - [ ] M0.14.g Review collisions/orphans/unsupported assertions and add a
        deterministic bidirectional zero-orphan drift gate. Evidence: approved
        reconciliation report.
        <!-- blocker: the deterministic gate resolves 1,466 collisions with a
             fixed reviewed rule, reports 111 unsupported/broad assertions, and
             proves 0 source/canonical orphans in both directions. It remains
             pending real owner approval and an effective date; the generator
             cannot approve its own report. -->
        _2026-09-18: the deterministic gate exists and proves zero orphans both
        ways; what is left is the owner's approval of its report and an
        effective date (rule 9)._ `blocked:human`
- [ ] M0.15 Define the boundary among authoring, institutional setup, educator
      operations, learner delivery, guardian/observer views, and operator
      adjudication for every Metis feature. Evidence: no learner progress,
      grade, answer key, integrity evidence, or credential authority is exposed
      through an authoring convenience route.
      <!-- evidence: metis-surface-boundaries.json + its hashed ownership JSONL
           define all 6 logical surfaces and assign all 10,021 reconciled feature
           rows plus all 39 current routes to one mutation owner. M0.15.a-c/e are
           technically complete. The parent/d/f remain open: 3 open sensitive
           API operations expose answer-key or grade classes, learner submission
           shapes can carry detailed integrity evidence, and 0/324 required
           served-runtime adversarial cells cover all 36 ordered surface pairs. -->
  - [x] M0.15.a Define each surface’s actors, purpose, canonical host/routes,
        owner services/stores, permitted subject projections, and high-impact
        actions. Evidence: surface boundary matrix.
        <!-- evidence: 6 strict surface rows define actors, purpose, logical
             canonical host/route namespace, current routes, owner services and
             stores, permitted projections, and high-impact actions. Logical
             host effectiveness remains explicitly pending M0.1 ratification. -->
  - [x] M0.15.b Assign every reconciled atomic feature and route to exactly one
        mutation owner and any read-only facades/deep links. Evidence: ownership
        ledger.
        <!-- evidence: metis-surface-ownership.rows.jsonl contains 10,060 unique
             rows: 10,021 feature assignments + 39 route assignments, each with
             exactly one mutation owner/service/store, command surfaces,
             read-only facades, canonical command route, deep link, and proof;
             0 current routes are left as boundary violations. -->
  - [x] M0.15.c Define minimal authoring projections of learner
        progress/profile, grades, integrity, accommodation, and credentials for
        preview/planning with purpose/field/aggregation limits. Evidence:
        projection schemas.
        <!-- evidence: @oshun/contracts/metis exports 6 strict discriminated
             projection schemas with preview/planning purpose, exact coarse-band
             fields, source owner/policy/revision context, N >= 30, small-cell
             suppression, coarse precision, and bounded freshness. Property tests
             inject every forbidden field family and fail closed. -->
  - [ ] M0.15.d Keep answer keys/rationales/items, detailed integrity evidence,
        grade corrections, learner PII, and credential issuance/revoke authority
        within their approved surfaces. Evidence: property/function tests.
        <!-- blocker (re-measured 2026-09-11): the 8-class policy and property
             tests deny protected data on every authoring convenience route, but
             runtime wiring is not complete. `GET /api/assessments` and
             `GET /api/assessments/{id}` (`api/assessments.py`) now require a
             bearer token (since 2026-09-06) but only `get_current_user`, whose
             default role is `student`, and `AssessmentResponse` still nests
             `QuestionResponse.correct_answer` and `explanation` — a learner token
             reads every answer key. The learner app also serves the whole
             authoring tree `apps/metis/web/src/app/create/*` behind login-only
             middleware (no role gate). The open LTI score route carrying grades
             and the learner submission shapes carrying integrity evidence were
             not re-measured. Closing order: project the learner-facing
             assessment read through M9.15's projection, role-gate or relocate
             `/create/*`, then the LTI score route and submission shapes; M10.16.a
             must be red on all of these before this cell closes. -->
        <!-- one of the four measured 2026-09-12 (M10.6.e): the LTI score
             route is closed. `POST /lti/assignments/scores` is unreachable
             without a bearer token, refuses a token that bought any other
             scope, and the grades it writes read back only with the AGS
             result scope — measured over the served app in
             `tests/test_lti_services.py::
             test_the_score_route_is_reachable_only_with_a_bearer_token_that_bought_the_scope`.
             The other three vectors (learner-readable answer keys, `/create/*`,
             submission shapes) are untouched and this cell stays open. -->
  - [x] M0.15.e Define signed handoffs with current authorization, tenant/
        institution/course/subject/revision context, expiry, replay, return
        intent, and immutable audit. Evidence: handoff contracts.
        <!-- evidence: MetisSignedSurfaceHandoffSchema signs every claim with
             Ed25519, caps TTL at 300s, rechecks current principal/session/authz
             revision and full context, consumes tenant+subject+session+nonce
             once, constrains target/return namespaces, and fails closed unless
             an append-only externally anchored audit record is appended. -->
  - [ ] M0.15.f Test direct routes, crafted fields, search/export/logs/
        notifications, deep links, role changes, and two-tenant leakage across
        all surface pairs. Evidence: adversarial boundary suite.
        <!-- blocker: contract tests cover crafted fields, policy, tamper,
             current authorization, context, expiry, replay, return namespace,
             and audit failure. They do not prove direct routes plus all 8
             secondary vectors against the served runtime: 36 ordered surface
             pairs x 9 vectors = 324 required cells, 0 complete. -->
        <!-- SPEC 2026-09-11: generate the 36 ordered surface pairs × 9 vectors
        as a parametrized pytest from
        `libs/contracts/src/metis/surface-boundaries.ts` (export the pair list
        as JSON through the existing generator) against the served runtime;
        never hand-write 324 tests. Each row records pass, fail or not-drivable
        with reason; the parent needs 324 rows none of which is not-drivable.
        -->
- [x] M0.16 Map discipline-specific capabilities for mathematics, physical
      science, life science, social science, language/humanities, and
      computing—or the current canonical discipline registry—to common and
      specialized editors, verifiers, media, simulations, and accessibility
      needs. Evidence: no discipline inherits unsupported generic behavior.
      <!-- evidence: metis-discipline-capabilities.json + its hashed 540-row
           JSONL bind 36 capability requirements to all 15 taxonomy 1.0.0
           entries (6 headline core disciplines + 9 supporting subjects).
           Exactly 144 cells admit proven semantics; all 396 other specialist
           cells deny generic fallback. Missing institution mappings remain an
           explicit zero-count, deny-unmapped state rather than an invented map. -->
  - [x] M0.16.a Reconcile named disciplines to the current canonical registry,
        hierarchy/aliases/versions, institution mappings, and extensibility.
        Evidence: discipline registry map.
        <!-- evidence: a stdlib-AST extractor compares every ID, title,
             placement, alias, taxonomy ID/version, and core/supporting order
             byte-for-byte with learning_subject_taxonomy_helpers.py. Extension
             policy requires versioning/non-shadowing/capability coverage; 0
             observed institution mappings fail closed as deny-unmapped. -->
  - [x] M0.16.b Define common
        content/objective/source/item/rubric/review/export/ accessibility
        capabilities and declare their real cross-discipline support. Evidence:
        common capability rows.
        <!-- evidence: 8 shared typed capabilities x 15 exact registry entries =
             120 supported common rows, each with implementation + automated
             test proof and an explicit warning that shared structure does not
             imply specialist semantics. -->
  - [x] M0.16.c Map mathematics requirements for semantic math, proofs/worked
        steps, symbolic/numeric verification, graphs, manipulatives, and
        accessible notation. Evidence: mathematics rows.
        <!-- evidence: 6 requirements distinguish the bounded algebra/process
             verifier and parameterized function plots from deferred semantic
             notation, manipulatives, and accessible math. Unsupported grammar
             fails loud; no CAS/MathML claim is made. -->
  - [x] M0.16.d Map physical/life science requirements for equations/units,
        datasets, labs/field evidence, simulations, diagrams, uncertainty,
        safety/ethics, and accessible alternatives. Evidence: science rows.
        <!-- evidence: 8 requirements map data figures/diagrams, exactly 3
             physics simulation templates, and neuroscience/astronomy safety
             policies while deferring absent unit, lab/field, measurement-
             uncertainty, discipline-template, and equivalent-access semantics. -->
  - [x] M0.16.e Map social science/language/humanities requirements for primary/
        secondary sources, claims/perspectives, chronology/maps, text/audio/oral
        performance, interpretation, and cultural/linguistic review. Evidence:
        humanities rows.
        <!-- evidence: 6 requirements preserve the narrow anthropology
             emic/etic policy and four core reviewer pools; general primary/
             secondary classification, chronology/maps, oral performance,
             interpretation, and supporting-subject review remain deferred. -->
  - [x] M0.16.f Map computing requirements for code/editor/runtime/tests/
        dependency/sandbox/security, interactive systems, and nonvisual output.
        Evidence: computing rows.
        <!-- evidence: 8 requirements admit only structural code validation and
             bounded JavaScript expected-case execution. The code explicitly
             disclaims node:vm as an untrusted security sandbox; editor,
             dependencies, isolated runtime, interactive systems, and nonvisual
             output remain denied/deferred. -->
  - [x] M0.16.g Map specialized editor, verifier, media/Isis/Yemaya/Euterpe,
        simulation, item type, export loss, accessibility, and reviewer role for
        every supported discipline capability. Evidence: discipline matrix.
        <!-- evidence: every supported cell has all requested facet bindings,
             real proof anchors, explicit export loss, and reviewer roles; absent
             Isis/Yemaya/Euterpe or accessible-specialist bindings are recorded
             as missing/not-required, never implied by a package name. -->
  - [x] M0.16.h Mark unsupported/not-applicable/deferred cells explicitly with
        reasons and fail generic fallback when specialized semantics are
        required. Evidence: no-inherited-behavior coverage test.
        <!-- evidence: all 540 registry/capability pairs have exactly one state:
             144 supported, 77 deferred, 1 unsupported, 318 not-applicable.
             Schema, example, and fast-check coverage prove only exact supported
             cells admit execution; every other specialist cell denies and sets
             genericFallback=forbidden. -->

### M1 — Contracts, standards, and content model

- [ ] M1.1 Verify all canonical Metis contracts and remove duplicate workbench
      shapes. Evidence: contract ownership matrix.
      <!-- current M1.1 evidence: metis-contract-ownership.json plus the 1,370-row
           field ledger prove M1.1.b-d. The parent remains open because 92
           app-local declarations across 91 logical shapes still require migration
           and public parse/serialize/round-trip goldens cover only 80/558 schemas. -->
  - [x] M1.1.a Inventory all Metis entities/schemas/types/OpenAPI/event/job/
        database/workbench shapes and their definitions, versions, owners,
        consumers, serializers, and tests. Evidence: contract inventory.
        <!-- superseded M1.1.a v1 evidence: metis-contract-inventory.json: inventory of the CURRENT Metis contract
                                                                                                                                                                                                                                                                        shapes across representations with DUPLICATE DETECTION (M1.1's goal). From
                                                                                                                                                                                                                                                                        the committed OpenAPI spec (apps/metis/service/openapi/metis.openapi.json,
                                                                                                                                                                                                                                                                        read once) + shared TS contracts (libs/contracts/src/metis) + DB entities
                                                                                                                                                                                                                                                                        (metis-data-model evidence). 558 OpenAPI component schemas collapse to 518
                                                                                                                                                                                                                                                                        logical shapes (429 Response / 68 Request / 57 base / 4 Input).
                                                                                                                                                                                                                                                                        DUPLICATE-SHAPE FINDING: 40 logical shapes have >1 OpenAPI variant (the
                                                                                                                                                                                                                                                                        Response/Request/base duplication M1.1 targets, e.g. AgentExecutionIntervention/
                                                                                                                                                                                                                                                                        AgentRubricScore); 0 cross-representation duplicates (the shared TS
                                                                                                                                                                                                                                                                        contracts cover a different canonical slice than the API DTOs,
                                                                                                                                                                                                                                                                        so no OpenAPI base name matches a shared type). 7 shared-contract files (115
                                                                                                                                                                                                                                                                        Zod schemas + 108 types), 14 SQLAlchemy entities. baseName() strips DTO
                                                                                                                                                                                                                                                                        suffixes for logical grouping. generate-metis-contract-inventory.mjs + 6
                                                                                                                                                                                                                                                                        tests, wired into inventory/verify + CI. M1.1.b (group by aggregate) +
                                                                                                                                                                                                                                                                        M1.1.c+ (consolidation/duplicate removal) are design and stay unchecked. -->
        <!-- current M1.1.a v2 evidence: 558 OpenAPI schemas collapse to 518
             logical shapes; 40 multi-variant groups are Request/Response pairs,
             not competing OpenAPI owners. The AST-backed cross-representation
             scan finds 92 app-local declarations across 91 logical shapes, zero
             shared-contract collisions, 25 shared files (364 Zod schemas and 351
             types), and 14 SQLAlchemy entities. Seven tests cover determinism,
             normalization, import exclusion, and known inventory values. -->
  - [x] M1.1.b Group shapes by semantic aggregate/operation and distinguish
        valid command/read/export projections from competing canonical
        definitions. Evidence: equivalence analysis.
        <!-- evidence: all 40 multi-variant OpenAPI groups map to non-empty
             request/response operation usage and classify as valid directional
             projections; zero competing OpenAPI groups and all 92 app-local
             declarations are explicitly classified as competing definitions. -->
  - [x] M1.1.c Choose one canonical owner contract per entity/operation and map
        every duplicate field/enum/version/unit/optional/default difference.
        Evidence: ownership matrix.
        <!-- evidence: 131 logical-shape ownership rows name one Metis service
             OpenAPI/Pydantic owner; 1,370 field rows map presence, requiredness,
             type, enum, version, unit, optionality, default, and format with zero
             missing owners or incomplete comparisons. -->
  - [x] M1.1.d Assign reuse/adapter/migrate/deprecate/remove disposition and
        consumer migration/compatibility window for every duplicate. Evidence:
        disposition ledger.
        <!-- evidence: 172 dispositions retain/reuse 80 directional wire
             projections and freeze 92 app-local declarations against new
             consumers pending generated-client migration before M1.1.e closure. -->
  - [ ] M1.1.e Add public parse/serialize/round-trip/schema goldens and drift/
        forbidden-import checks preventing new workbench-local canonical shapes.
        Evidence: contract ownership gate.
        <!-- blocker: 80 reviewed public schemas have valid parse/serialize/
             round-trip/schema-hash goldens, leaving 478 public schemas uncovered;
             92 forbidden workbench-local declarations also remain to migrate. -->
        <!-- SPEC 2026-09-11: the 478 uncovered public schemas are closed by a
        generator, not by hand: emit one golden per component schema from
        `openapi/metis.openapi.json` (minimal and full instance,
        parse/serialize/round-trip, schema hash), commit the goldens, gate
        drift. The 92 workbench-local declarations migrate to
        `libs/contracts/src/metis` one logical shape at a time with the
        forbidden-import check switched on for that shape as it lands. The
        evidence block reports both counts before and after. -->
- [x] M1.2 Define immutable course and item revisions with stable identifiers.
      Evidence: versioning and restore invariants.
      <!-- evidence: revision-history.ts + metis-revision-invariants.json/JSONL
           define and exercise an append-only, hash-sealed course/item history;
           all 6 child invariants and 6 negative/positive probes pass. -->
  - [x] M1.2.a Define stable logical IDs separately from immutable revision IDs,
        tenant/owner, parent/base revision, sequence/label, state,
        author/reason, timestamps, and integrity hash. Evidence: revision
        schemas.
        <!-- evidence: strict Zod envelopes separate logicalId/revisionId and
             require owner, lineage, state, authorship, semanticHash, and full
             integrityHash; tampering and historical ID reuse fail. -->
  - [x] M1.2.b Define which course/module/lesson/activity/assessment/item/
        stimulus/rubric/objective/metadata relations are captured by a course or
        item revision manifest. Evidence: revision boundary map.
        <!-- evidence: 20 boundary rows cover all 10 relations for both subject
             kinds with exact revision+content-hash pin, inline immutable
             metadata snapshot, or explicit exclusion. -->
  - [x] M1.2.c Create revisions by copy-on-write or explicit references with
        optimistic concurrency and prohibit in-place changes to consumed/
        reviewed/published revisions. Evidence: immutability tests.
        <!-- evidence: commitMetisContentRevision is append-only and checks the
             expected logical/branch head, dense sequence, unique revision ID,
             exact refs, locked states, consumer pins, and integrity hashes. -->
  - [x] M1.2.d Define fork/branch/merge/conflict/supersede/archive/restore/
        compare semantics without reusing a historical revision ID. Evidence:
        lifecycle invariants.
        <!-- evidence: 8 operation rows enforce parent/base/identity/state rules;
             merges require a common ancestor and explicit conflict resolutions,
             while comparison reports deterministic manifest-level differences. -->
  - [x] M1.2.e Pin assessments/builds/delivery/attempts/grades/evidence to exact
        revisions and preserve resolvability after supersession. Evidence:
        downstream linkage tests.
        <!-- evidence: all 6 consumer kinds pin logicalId, revisionId,
             semanticHash, and integrityHash; the historical target remains
             resolvable after a superseding revision is appended. -->
  - [x] M1.2.f Restore by creating a new revision semantically equal to the old
        one with restoration provenance, not by mutating history. Evidence:
        restore invariants.
        <!-- evidence: restore creates sequence 3 under a new revision ID,
             preserves the source semantic hash, creates a distinct integrity
             hash, records exact source/actor/time/reason, and leaves all 3
             historical records byte-stable and resolvable. -->
- [x] M1.3 Define learning objectives with taxonomy, level, measurable verb,
      criteria, prerequisite, standard alignments, and evidence requirements.
      Evidence: schema tests.
      <!-- evidence: learning-objectives.ts + metis-learning-objectives.json;
           6 contract test cases and 8 independent invariant probes cover all children
           and reject scope, taxonomy, threshold, graph, review, and coverage faults. -->
  - [x] M1.3.a Define objective identity/version/scope/locale/title/description/
        owner/lifecycle and course hierarchy membership. Evidence: base schema.
        <!-- evidence: stable objectiveId is separate from revisionId/revision/
             contentHash; strict envelopes pin tenant, exact course hierarchy
             membership, locale, owner, lifecycle, creation, and descriptive fields. -->
  - [x] M1.3.b Define taxonomy identity/version plus objective classification,
        level, measurable verb, subject/content, context, and allowed
        extensions. Evidence: taxonomy field tests.
        <!-- evidence: generic classifications require taxonomyId/version,
             classification/level codes, measurable verb, subject/content/context,
             and only namespaced x-owner:key extensions; pedagogy is not hard-coded. -->
  - [x] M1.3.c Define observable performance criteria, conditions, mastery/
        quality thresholds, units, rubric refs, and acceptable evidence kinds.
        Evidence: criteria tests.
        <!-- evidence: criteria require observable statements, unique conditions,
             bounded mastery/quality thresholds and units, exact rubric revisions,
             and at least one of 11 explicit acceptable evidence kinds. -->
  - [x] M1.3.d Define prerequisite/requisite/related objective edges with
        version, strength, rationale, cycle/orphan/reachability rules. Evidence:
        prerequisite graph tests.
        <!-- evidence: three edge-policy rows pin exact objective revisions,
             edge version/strength/requirement/rationale, reject dependency cycles
             and orphans, and require publication reachability from declared roots. -->
  - [x] M1.3.e Define standard/framework/version/jurisdiction/grade-band/code
        alignments with coverage/strength/rationale/evidence and human review.
        Evidence: alignment tests.
        <!-- evidence: strict alignment records include framework/version,
             jurisdiction, grade bands, code, coverage, strength, rationale,
             exact evidence refs, and complete approved/rejected human review. -->
  - [x] M1.3.f Define content/activity/item/assessment/evidence coverage links
        and prevent publication of required objectives without ratified
        coverage. Evidence: coverage invariants.
        <!-- evidence: publication requires approved exact objective revisions,
             any mandatory approved standards alignment, and human-approved
             content/activity/item/assessment/evidence links; removing any one
             kind produces a blocking invariant. -->
- [x] M1.4 Define Bloom or ratified cognitive-level vocabulary without
      hard-coding it as the only pedagogy. Evidence: extensibility test.
      <!-- evidence: taxonomy-registry.ts + fixtures + metis-taxonomy-registry.json;
           6 contract test cases and 8 independent probes cover all children.
           All fixture approvals are not production ratification; production
           entries still require their own complete human approval evidence. -->
  - [x] M1.4.a Register cognitive/pedagogical taxonomy identity/version/owner/
        lifecycle/source/license/locale and stable level codes. Evidence:
        vocabulary registry schema.
        <!-- evidence: strict registry entries pin immutable version hashes,
             owner/lifecycle/ratification, cited sources, conservative license
             usage, complete locales, and unique stable codes/order/relations. -->
  - [x] M1.4.b Model Bloom levels/verbs/relations as one admitted vocabulary,
        including revised/classic profile distinctions where ratified. Evidence:
        Bloom fixtures.
        <!-- evidence: classic-1956 and revised-2001 are separate fixture-only
             versions/profiles in one family, each with six stable codes,
             localized labels, verbs, and explicit within-profile relations. -->
  - [x] M1.4.c Allow other approved taxonomies and institution mappings through
        versioned registry entries rather than enum/code changes. Evidence:
        alternate taxonomy test.
        <!-- evidence: a bilingual institution-owned fixture admits five novel
             codes through the generic schema and maps them without adding a
             Bloom or institution-specific enum to the contract. -->
  - [x] M1.4.d Define cross-taxonomy mappings as exact/broader/narrower/related/
        unmapped with version/rationale and no false equivalence. Evidence:
        mapping tests.
        <!-- evidence: all five relations are versioned, rationalized, reviewed,
             and exact mappings additionally require full-meaning scope,
             confidence 1, evidence, and one non-conflicting target. -->
  - [x] M1.4.e Make objective/item/UI/API/search/export consume
        taxonomy+version+ code and render locale labels without relying on
        Bloom-specific fields. Evidence: consumer extensibility tests.
        <!-- evidence: all six named consumers resolve the same exact generic
             taxonomy/version/hash/code reference, render requested or default
             locale labels, and strict requests reject bloomLevel fields. -->
  - [x] M1.4.f Handle unknown/deprecated/unavailable taxonomy versions safely
        and preserve historical meaning. Evidence: lifecycle tests.
        <!-- evidence: deprecated/unavailable registry snapshots preserve label,
             version/hash/code, fallback locale, and replacement metadata;
             unknown taxonomy/version/code returns a null-label safe status. -->
- [x] M1.5 Define assessment item kinds including selected response, constructed
      response, essay, math, code, media, simulation, interaction, portfolio,
      and oral/performance evidence. Evidence: kind registry.
      <!-- evidence: assessment-kind-registry.ts + fixtures +
           metis-assessment-kind-registry.json; 7 contract test cases and 10
           independent probes cover 15 kinds and all children. The contract
           registry is not runtime adoption; legacy service/QTI support remains
           separately measured and fixture approvals are not production approval. -->
  - [x] M1.5.a Define versioned kind-registry entry with response/evidence
        model, interaction capabilities, scoring modes, delivery/runtime
        requirements, accessibility, standards mappings, security risk, and
        lifecycle. Evidence: kind schema.
        <!-- evidence: each strict version pins identity/hash/family/owner/review,
             response/evidence models, capabilities, scoring, runtime, WCAG 2.2
             AA requirements, QTI/LTI dispositions, threats/controls, and lifecycle. -->
  - [x] M1.5.b Define selected-response variants for single/multiple choice,
        ordering, matching, hotspot, and other ratified interactions with
        option/key invariants. Evidence: selected-response fixtures.
        <!-- evidence: five mode fixtures enforce unique option/target/region ids,
             exact single/multiple/ordered/matching/spatial key shapes, declared
             references only, accessible labels, and mode-specific fields. -->
  - [x] M1.5.c Define constructed-response/essay response formats, length/
        language/format constraints, scoring guides, exemplars, and moderation.
        Evidence: written-response fixtures.
        <!-- evidence: constructed/essay fixtures bound formats and MIME types,
             ordered length/word limits, locales, exact rubric/exemplar refs,
             and one- or two-assessor moderation rules. -->
  - [x] M1.5.d Define math/code response, semantic/executable artifacts,
        equivalence/tests/sandbox/runtime, partial credit, and accessible
        alternatives. Evidence: math/code fixtures.
        <!-- evidence: math pins formats/equivalence/tolerance/weighted tests;
             code pins language/runtime image/template/tests and a no-network
             bounded deterministic sandbox; both carry partial credit and
             construct-equivalent accessible alternatives. -->
  - [x] M1.5.e Define media/simulation/interaction inputs, states/events/
        recordings, deterministic seeds, telemetry, scoring hooks, fallback, and
        sandbox. Evidence: interactive fixtures.
        <!-- evidence: three fixtures define modalities/state/events, consented
             recordings, required seeds, telemetry, versioned scoring hooks,
             exact fallbacks, origin isolation, and network allowlists. -->
  - [x] M1.5.f Define portfolio and oral/performance evidence submissions,
        artifact/take/observation/assessor/rubric/consent/retention fields.
        Evidence: performance evidence fixtures.
        <!-- evidence: portfolio/oral/observed-performance fixtures pin artifacts,
             take bounds, observation fields, qualified assessors, exact rubrics,
             revocable consent, minor guardianship, and legal-hold-aware retention. -->
  - [x] M1.5.g Require explicit unsupported/not-applicable behavior across
        authoring, delivery, accessibility, QTI/LTI/export, analytics, and
        scoring for every kind. Evidence: registry coverage matrix.
        <!-- evidence: 15 kinds x 8 named dimensions = 120 exact dispositions;
             supported rows require evidence while unsupported/not-applicable
             rows retain explicit rationale and QTI/LTI mapping concordance. -->
- [x] M1.6 Define stimuli and shared passages separately from items. Evidence:
      one stimulus can safely version and serve multiple items.
      <!-- technically complete: a separate immutable stimulus contract covers
           12 required kinds, exact revision links, three-item/two-assessment
           passage reuse, change blast radius, and safe projections. The
           existing hierarchy shape remains a coarse compatibility boundary;
           the immutable stimulus contract is not runtime adoption. -->
  - [x] M1.6.a Define stimulus logical/revision identity, kind, content blocks,
        source/citation/rights, locale, accessibility, metadata, lifecycle, and
        integrity hash. Evidence: stimulus schema.
        <!-- evidence: MetisStimulusRevisionSchema separates stable stimulus and
             immutable revision identity and governs typed blocks, provenance,
             citations, rights, locale, accessibility review, metadata,
             lifecycle, predecessor pins, content hash, and integrity hash. -->
  - [x] M1.6.b Define text/passage, image/diagram, audio/video, dataset/table/
        chart, code, document, and interactive stimulus-specific fields.
        Evidence: kind fixtures.
        <!-- evidence: 12 valid kind fixtures cover text, passage, image,
             diagram, audio, video, dataset, table, chart, code, document, and
             sandboxed interactive content with kind-specific accessibility
             and fallback fields. -->
  - [x] M1.6.c Define item-to-stimulus link with exact revision, role, order,
        excerpt/locator, presentation context, shared instructions, and
        dependency invalidation. Evidence: link schema.
        <!-- evidence: strict MetisItemStimulusLinkSchema pins exact item and
             stimulus revisions, structured non-inline excerpts, presentation,
             exact shared-instruction refs, five invalidation domains, and
             rejects answer/scoring/exposure fields. -->
  - [x] M1.6.d Permit one immutable stimulus revision to serve multiple items/
        assessments while keeping each item key/scoring/exposure separate.
        Evidence: reuse tests.
        <!-- evidence: one passage revision is linked to three exact item
             revisions across two assessments; each item owns three unique
             protected-resource refs outside the links, and missing/shared
             security bindings fail graph validation. -->
  - [x] M1.6.e Version source/content/accessibility/localization changes through
        a new stimulus revision and compute affected items/builds/attempts.
        Evidence: blast-radius tests.
        <!-- evidence: the r1-to-r2 fixture changes all four protected
             dimensions under a new-immutable-revision policy; deterministic
             traversal identifies three items, two assessments, two builds,
             and two attempts while preserving released/historical pins. -->
  - [x] M1.6.f Prevent stimulus delivery/search/export from exposing linked item
        answers or unreleased test membership. Evidence: isolation tests.
        <!-- evidence: strict delivery/search/export projection tests serialize
             zero protected resource values and zero unreleased assessment ids;
             learner/search/export membership is empty and authorized delivery
             can reveal released membership only. -->
- [x] M1.7 Define answer, rationale, distractor rationale, scoring, feedback,
      hint, and exposure metadata. Evidence: answer-key fields are protected.
      <!-- technically complete: strict target contracts separate answer keys,
           rationales, scoring, learner feedback/hints, exposure, and
           property-level access. The current service still persists and can
           serialize inline correct_answer/explanation/hint fields; this
           protected assessment contract is not runtime enforcement. -->
  - [x] M1.7.a Define protected answer/key forms per item kind with exact item
        revision, acceptable variants/equivalence/tolerance, ordering, and
        partial-credit semantics. Evidence: answer schema tests.
        <!-- evidence: 15 exact item-kind fixtures cover seven protected form
             families with immutable key identity, value/equivalence/tolerance/
             ordering semantics, and priority-ordered partial-credit rules. -->
  - [x] M1.7.b Define correct-answer rationale and
        per-distractor/response-pattern rationales separately from
        learner-visible feedback. Evidence: rationale schemas.
        <!-- evidence: the assessment-secret rationale schema has separate
             correct, option-distractor, and response-pattern refs and rejects
             learner feedback; feedback carries a distinct learner-visible
             classification and cannot include protected rationale refs. -->
  - [x] M1.7.c Define deterministic/rubric/model-assisted/manual scoring method,
        steps/rules/weights/rounding, evidence, uncertainty, moderation, and
        version. Evidence: scoring schemas.
        <!-- evidence: four immutable scoring fixtures pin exact item/key
             revisions; contiguous weighted steps sum to one, rounding and
             evidence are explicit, uncertainty is bounded, and model-assisted
             scoring requires human moderation. -->
  - [x] M1.7.d Define preattempt/postresponse/postsubmission/postdeadline/
        instructor feedback and progressive hint ladders with penalty/exposure
        conditions. Evidence: feedback/hint tests.
        <!-- evidence: all five feedback stages are explicit; a three-step hint
             ladder pins availability, exposure, and increasing penalty while
             prohibiting final-answer and protected-rationale inclusion. -->
  - [x] M1.7.e Define item/key/explanation exposure state, audience, window,
        attempts, secure-delivery/export/search/log/cache rules, and breach
        response. Evidence: exposure policy tests.
        <!-- evidence: item/key/explanation rows govern state, audience, window,
             and attempts; five surface rules default-deny/redact/no-store, and
             eight mandatory critical-breach actions seal, purge, rotate,
             notify, and preserve evidence. -->
  - [x] M1.7.f Enforce property-level permissions for answer/rationale/scoring/
        distractor fields across UI/API/tutor/search/export/events/analytics and
        two tenants. Evidence: protected-field suite.
        <!-- evidence: 8 protected properties x 7 surfaces produce 56 explicit
             default-deny rows; 112 same/cross-tenant learner probes deny and
             audit every value, 16 author UI/API grants are purpose-bound, and
             14 safe projections serialize no protected fixture sentinel. -->
- [x] M1.8 Define rubric criteria, levels, descriptors, weights, scoring method,
      moderation, and evidence refs. Evidence: deterministic score invariants.
      <!-- technical evidence: `libs/contracts/src/metis/rubric.ts`, fixtures,
           six contract tests, and deterministic `metis-rubrics.json` define the
           immutable assessment-rubric authority and five exact score outcomes.
           The assessment rubric contract is not runtime adoption; hierarchy
           summaries and agent-evaluation rubrics remain distinct boundaries. -->
  - [x] M1.8.a Define rubric logical/revision identity, scope, owner, locale,
        analytic/holistic/single-point method, lifecycle, applicable items/
        evidence kinds, and integrity hash. Evidence: rubric schema.
        <!-- evidence: three method fixtures validate exact hierarchy projection,
             item/assessment/evidence scope, owner, locale, lifecycle, immutable
             revision identity, content hash, and integrity hash. -->
  - [x] M1.8.b Define stable criteria with objective links, title/description,
        required flag, weight/points, ordering, dependencies, and evidence
        requirements. Evidence: criterion tests.
        <!-- evidence: two stable analytic criteria pin objective revisions,
             localized text, 0.6/0.4 weights, ten-point maxima, contiguous order,
             an acyclic dependency, and exact artifact/annotation requirements. -->
  - [x] M1.8.c Define ordered performance levels with codes, descriptors,
        score/point/range, boundary inclusivity, exemplars, and locale variants.
        Evidence: level tests.
        <!-- evidence: range, point, and rating fixtures enforce one score mode,
             unique contiguous levels, localized descriptors, exemplars, and
             adjacent boundaries with exactly one inclusive side. -->
  - [x] M1.8.d Define weighted/points/custom rule aggregation, missing/N/A/
        unscorable handling, rounding, caps/floors, and total-scale mapping.
        Evidence: exact score fixtures.
        <!-- evidence: deterministic results are weighted 72, N/A-renormalized
             80, missing/incomplete, points 85, and custom 70; wrong revision,
             level, maximum, and custom-rule inputs fail closed. -->
  - [x] M1.8.e Define assessor evidence refs, annotations, confidence, criterion
        decisions, comments, moderation/second marking, reconciliation, and
        audit. Evidence: scoring evidence tests.
        <!-- evidence: the exact evidence bundle resolves artifacts and
             annotations per criterion, captures primary/second confidence and
             comments, reconciles by consensus, and preserves a five-event audit. -->
  - [x] M1.8.f Version rubrics immutably and pin assessment/scoring/grade
        records to exact revisions; changes cannot recalculate historical grades
        without correction workflow. Evidence: versioning invariants.
        <!-- evidence: r1-to-r2 creates a new immutable revision and hashes;
             assessment, scoring-policy, and grade consumers pin r1 exactly,
             while historical r2 use requires correction-not-mutation workflow. -->
- [x] M1.9 Define accommodations and Personal Needs and Preferences references.
      Evidence: support requirements remain separate from disability diagnoses.
      <!-- technical evidence: `libs/contracts/src/metis/accommodations.ts`,
           fixtures, six contract tests, and deterministic
           `metis-accommodations.json` define functional supports only. The
           accommodation contract is not runtime adoption or a standards
           conformance claim; current study preferences remain separate. -->
  - [x] M1.9.a Adopt/version the applicable PNP/accommodation vocabulary and map
        support codes to delivery capabilities without copying diagnoses.
        Evidence: standards mapping.
        <!-- evidence: pins 1EdTech AfA PNP 3.0 Public Candidate Final Draft v2
             (2021-01-18), aligns 17 functional support codes to QTI 3 published
             accessibility supports or a namespaced Metis extension, maps every
             code to a versioned capability, and explicitly claims no conformance. -->
  - [x] M1.9.b Define authoring-side support requirement for presentation,
        content, navigation, timing/scheduling, response, setting, language, and
        assistive technology compatibility. Evidence: requirement schema.
        <!-- evidence: 17 immutable requirements cover all eight categories with
             exact support/vocabulary/capability refs, activation and semantics,
             alternatives, evidence, previewability, and fail-closed behavior. -->
  - [x] M1.9.c Define purpose-bound learner PNP projection with opaque learner/
        profile ref, requested/approved support codes, source authority,
        effective term, and no medical detail. Evidence: projection contract.
        <!-- evidence: restricted synthetic projection uses opaque learner/profile
             refs, four requested and three approved functional codes, exact
             institutional authority/effective context, and rejects unknown,
             unrequested, non-opaque, or diagnosis-bearing fields. -->
  - [x] M1.9.d Define conflicts, mutually exclusive supports, item invalidation,
        equivalent alternatives, human review, and cannot-deliver behavior.
        Evidence: compatibility tests.
        <!-- evidence: evaluator proves deliver, equivalent-alternative,
             human-review, and cannot-deliver outcomes; theme conflict, item
             invalidation, missing capability, and unsupported switch input fail
             closed with exact reasons. -->
  - [x] M1.9.e Enforce field-level access/retention/audit and prohibit inference
        or storage of disability diagnosis from support selection/use. Evidence:
        privacy tests.
        <!-- evidence: seven protected fields have exact retention/delete rules;
             98 author/cross-tenant probes deny values, 22 learner/delivery grants
             are explicit and audited, and diagnosis/inference/training use are
             prohibited across seven surfaces. -->
  - [x] M1.9.f Verify authoring preview and learner delivery apply the same
        supported semantics while protected learner records remain external.
        Evidence: preview/delivery parity tests.
        <!-- evidence: preview and delivery deep-equal the same three-row semantics
             plan; preview contains no learner/profile ref, delivery embeds only
             an opaque projection ref, and any semantic drift fails validation. -->
- [x] M1.10 Define locale variants, bidirectional text, MathML, SSML,
      transcripts, captions, long descriptions, tactile refs, and sign-language
      assets. Evidence: QTI accessibility mapping.
      <!-- technically complete 2026-08-28: `accessible-content.ts`, fixtures,
           17 contract tests, the deterministic generator/test, and
           `metis-accessible-content.json` cover all eight children and 106
           canonical QTI field/asset paths. QTI 3.0.1, MathML 3, SSML/Data-SSML,
           WebVTT draft status, and BCP 47/UAX #9 are pinned as contract profiles;
           no standards conformance/certification is claimed. Protected reviewer,
           presenter, and consent refs are omitted from packages and evidence.
           This accessible-content contract is not runtime adoption; the current
           stimulus/lecture/QTI implementations remain a documented migration. -->
  - [x] M1.10.a Define locale/language/script/region/direction variant identity,
        source revision, translation status, fallback, reviewer, and change
        lineage. Evidence: locale variant schema.
        <!-- evidence: two immutable exact-ref variants exercise canonical
             language/script/region tags, LTR/RTL direction, source/translation
             linkage, human review, fallback, source revision, and acyclic change
             lineage; stale hashes and fallback cycles fail validation. -->
  - [x] M1.10.b Define Unicode/bidirectional isolation/base direction/mixed-
        script/numeral/punctuation behavior for content, answers, code, math,
        metadata, and input. Evidence: bidi fixtures.
        <!-- evidence: six exact context rows require markup/plain-text isolates,
             context-specific code/math/input behavior, neutral punctuation and
             numeral policies; embeddings, overrides, duplicates, and unbalanced
             isolates are rejected. -->
  - [x] M1.10.c Define semantic MathML source, presentation/content profile,
        speech/braille alternatives, equation refs/labels, and fallback image/
        text. Evidence: MathML fixtures.
        <!-- evidence: stable MathML 3 parallel presentation/content markup binds
             semantic source, equation/label, reviewed speech and braille, image/
             text fallback, and exact objectives; active/external/nonallowlisted
             markup fails the renderer profile. -->
  - [x] M1.10.d Define allowlisted SSML profile, language/voice/pronunciation/
        prosody/mark semantics, text equivalent, and safe renderer behavior.
        Evidence: SSML fixtures.
        <!-- evidence: eight instruction kinds cover Data-SSML plus profiled
             language/voice/mark semantics, always bind reviewed text fallback,
             and prohibit external audio/lexicons/scripts; unknown and unordered
             instructions fail validation. -->
  - [x] M1.10.e Define transcript and caption tracks with language, speaker,
        timebase/cues, non-speech audio, review, synchronization, and formats.
        Evidence: timed-text fixtures.
        <!-- evidence: versioned caption/transcript tracks bind exact media and
             locale refs, speakers, millisecond cues, non-speech audio, human
             review, <=500ms sync evidence, and required WebVTT/structured-HTML
             primaries; unresolved speakers/formats and overrun cues fail. -->
  - [x] M1.10.f Define alt text, extended/long description, tactile graphic/
        braille-ready refs, data/source linkage, and equivalent learning
        outcome. Evidence: nonvisual alternative tests.
        <!-- evidence: the nonvisual fixture binds short/long descriptions,
             tactile graphic/tour, braille-ready rendition, exact source data and
             objective refs, and affirmative equivalence review; missing
             tactile/braille form or non-objective linkage fails. -->
  - [x] M1.10.g Define sign-language asset language/community/presenter/consent/
        rendition/time alignment and fallback semantics. Evidence: sign-language
        fixtures.
        <!-- evidence: an `ase` fixture requires declared community, opaque
             presenter, exact consent, one primary rendition, positive source/
             rendition alignment, written locale and transcript fallback;
             missing consent, multiple primaries, and unresolved fallback fail. -->
  - [x] M1.10.h Map every field/asset to QTI accessibility metadata/content,
        document explicit loss/unsupported behavior, and round-trip. Evidence:
        QTI accessibility mapping tests.
        <!-- evidence: all 106 canonical paths map exactly once with exact,
             normalized, lossy, unsupported, or policy-omitted disposition and
             matching round-trip behavior; missing rows, false exactness, or
             reviewer/presenter/consent export fail closed. -->
- [x] M1.11 Define QTI 3 package/import/export profile. Evidence: every
      supported item kind maps or fails with an explicit incompatibility.
      <!-- CLOSED 2026-09-18, all seven children closed and the parent's own
           sentence measured rather than inherited: of the 15 canonical item
           kinds, 7 map and hold a passing non-synthetic receipt from each of the
           two pinned tools, and the other 8 fail with an explicit code the
           coverage manifest names. The conformance gate over real exported
           bytes reads `passed: true`, 0 blockers.
           WHAT THIS DOES NOT SAY: no product route calls the QTI 3 runtime, so
           no runtime adoption is claimed here and M10.5's parent stays open on
           exactly that. Certification by 1EdTech is not claimed either; what is
           held is two pinned tools' own output over our own artifacts. -->
      <!-- technical progress 2026-08-28: `qti-package.ts`, fixtures, 54
           contract tests, the deterministic generator/test, and
           `metis-qti-package.json` complete M1.11.a-f. The profile pins QTI
           3.0.1 and both external validator roles, covers 11 canonical objects,
           all 15 item kinds, 90 capability cells, 11 content/accessibility
           dimensions, 18 hostile imports, and a deterministic schema-validated
           export golden.
           M1.11.g remains blocked because no repository-installed official QTI
           validator bundle or independent consumer has produced the required 14
           non-synthetic receipts. The parent remains open; no conformance,
           certification, or current runtime adoption is claimed. -->
  - [x] M1.11.a Pin supported QTI 3 specification/version/conformance modules,
        namespaces, package/resource/manifest profile, extensions, and validator
        toolchain. Evidence: profile decision.
        <!-- evidence: pins QTI 3.0.1/BPIG document v2.0 (2024-10-01), nine
             modules, QTI/package/MathML/XML/Metis namespaces, three SHA-256-pinned
             root schemas, six resource types, two allowlisted extensions, and
             official-schema plus independent-consumer validator roles. -->
  - [x] M1.11.b Map canonical assessment/test/section/item/stimulus/response/
        outcome/processing/rubric/objective/metadata identities and versions to
        QTI constructs. Evidence: field mapping.
        <!-- evidence: eleven exact-ref rows map every canonical identity once;
             rubric/objective/metadata normalization is explicit, response and
             processing are protected, and no protected values enter evidence. -->
  - [x] M1.11.c Map each item kind/interaction/scoring/feedback/hint/adaptive/
        template capability with exact supported/lossy/extension/unsupported
        disposition. Evidence: item-kind matrix.
        <!-- evidence: all 15 immutable registry kind refs map exactly once over
             90 capability cells; four exact, one normalized, two lossy, five
             unsupported, and three inapplicable kind rows have matching
             round-trip or explicit incompatibility behavior. -->
  - [x] M1.11.d Map media/files/MathML/accessibility/locale/bidi/captions/
        transcripts/PNP/dependencies and package paths securely. Evidence:
        content/accessibility matrix.
        <!-- evidence: eleven exact dimension rows require immutable resource
             hashes, declared offline dependencies, allowlisted MIME types, no
             learner PNP values, and reject absolute/traversal/encoded/URI/
             backslash/control/empty/dot/overlong package paths. -->
  - [x] M1.11.e Import with archive/schema/size/path/resource protections,
        preview/migration/conflict/reporting, immutable source package, and no
        answer-key exposure. Evidence: hostile import tests.
        <!-- evidence: bounded ZIP/XML/resource evaluation rejects traversal,
             symlink, encryption, bombs, case collisions, undeclared/missing or
             external resources, schema/entity failures, mutable source,
             source-hash/MIME mismatch, embedded active/external content, and
             answer/private preview exposure across 18 hostile probes. -->
  - [x] M1.11.f Export deterministic validated packages with exact revisions,
        standards metadata, checksums, rights, declared extensions/loss report,
        and no secrets/private fields. Evidence: golden exports.
        <!-- evidence: a no-wall-clock golden pins three source revisions, sorted
             payload-derived file hashes/bytes, canonical ZIP metadata, descriptor
             hash, QTI metadata, schema receipts, rights, two extensions, explicit
             losses, and nine prohibited private/secret field classes; tamper,
             reorder, rights denial, or exposure fails closed. -->
  - [x] M1.11.g Round-trip every supported item-kind/profile through pinned
        independent validators/consumers and fail explicitly for incompatible
        cells. Evidence: QTI conformance suite.
        <!-- CLOSED 2026-09-18 WITH M10.5.d (§2.5.6: one gate, together).
             14 of 14 non-synthetic passing receipts are held, one per supported
             kind per pinned tool, under
             `evidence/v1-workbenches/metis-conformance/`, and
             `tests/test_qti3_conformance.py` asserts the held set EQUALS the
             set the contract's matrix requires. The two that were missing were
             hotspot's: Metis now has a canonical spatial item, and the
             consumer caught a live defect in its first export (the image
             written as `img`, which the XSD admits inside a hotspot
             interaction and the consumer refuses). All eight incompatible
             cells still fail with explicit codes; the conformance gate
             `evidence/v1-workbenches/metis-qti/conformance-gate.json` reads
             `passed: true` with 0 blockers. -->
        <!-- blocked 2026-08-28: the contract requires a passing non-synthetic
             receipt from both the pinned official QTI 3.0.1 schema toolchain and
             independent QTI consumer for each of seven mapped kinds (14 total).
             Neither tool is installed and no genuine receipt exists; all eight
             incompatible cells already fail with explicit codes. -->
        <!-- still blocked 2026-09-11, at 12 of 14: M10.5.d installed and
             pinned both tools (`apps/metis/service/tools/qti3/`) and retained
             12 passing non-synthetic receipts under
             `evidence/v1-workbenches/metis-conformance/`, verified against the
             contract's own version and integrity pins by
             `tests/test_qti3_conformance.py`. The two that remain are
             hotspot's, and they cannot be taken: Metis has no canonical
             hotspot item, so there is nothing to hand a validator. Closing
             this needs a canonical hotspot model, not another validator run.
             §2.5.6 binds this to M10.5.d. -->
- [ ] M1.12 Define LTI 1.3/LTI Advantage launch, deep-link, roles, and grade
      service profile. Evidence: standards matrix.
  - [x] M1.12.a Pin LTI 1.3/LTI Advantage service versions/conformance,
        deployment/issuer/client/key registration, platforms, privacy profile,
        and supported flows. Evidence: profile decision.
        <!-- evidence: the versioned target profile pins Core 1.3 document v3,
             Deep Linking 2.0 document v2, NRPS 2.0, AGS 2.0 document v3,
             Security Framework 1.1, and certification guide v3; asymmetric
             registration binds tenant/platform/issuer/client/deployment,
             OIDC/token/JWKS/login/redirect endpoints, RS256/2048-bit keys, five
             scopes, six platform targets, no-PII defaults, six flows, and an
             explicit no-conformance/no-runtime-adoption posture. -->
  - [x] M1.12.b Define OIDC login and resource-link launch validation for
        issuer, audience/client, deployment, nonce/state, message type/version,
        target link, roles, context, resource, user, locale, and custom claims.
        Evidence: launch contract tests.
        <!-- evidence: unknown-input validation requires one-time state/nonce,
             issuer/client/audience/authorized-party/deployment/tenant binding,
             RS256 signature/kid/pinned JWKS, bounded iat/exp/skew, LTI 1.3
             type/version, signed target link, subject policy, official role,
             context/resource, BCP47 locale, and string-only custom claims;
             replay, substitution, expiry, HS256, and cross-tenant probes fail. -->
  - [x] M1.12.c Map institutional/course/section/role claims to canonical
        identities and least-privilege authoring/learner/educator operations
        without trusting display fields. Evidence: role mapping matrix.
        <!-- evidence: eleven official system/institution/context/marker role
             URIs map to bounded canonical grants; learner gets launch/own-result,
             mentor gets launch only, and tenant/institution/course/section/
             principal/placement projection uses signed opaque identifiers with
             no trusted display fields. -->
  - [x] M1.12.d Define deep-link request/response for course/content/assessment
        selection, exact revision/resource, placement, availability, grade
        settings, signing, and return validation. Evidence: deep-link tests.
        <!-- evidence: request/response contracts bind deployment, exact HTTPS
             return URL, opaque data echo, accepted item/target/multiplicity/
             line-item capabilities, tool/platform issuer/audience, RS256/kid,
             one-time nonce, bounded iat/exp/skew, exact course/content/assessment
             revision+hash, placement, availability, grade settings, and string
             custom refs while excluding answer keys and learner-private data. -->
  - [x] M1.12.e Define Names and Role Provisioning scope/pagination/freshness/
        privacy plus Assignment and Grade Services line-item/score/result
        directionality, idempotency, correction, and visibility. Evidence:
        service contract tests.
        <!-- evidence: private_key_jwt token exchange binds registered client
             iss/sub, token-endpoint aud, deployment, exact scopes, RS256/kid,
             one-time JTI, and 300-second lifetime; NRPS GET/media/scope/context
             binds role/resource filters, limit/next/differences, origin,
             freshness, minimal fields, and purpose receipts for PII; AGS pins
             line-item revision, score-write/result-read direction, visibility,
             increasing timestamps, correction lineage, and no answer material. -->
  - [x] M1.12.f Define key rotation/JWKS cache, replay/clock skew, launch
        expiry, logout/session, error/redaction, and tenant/deployment
        isolation. Evidence: security tests.
        <!-- evidence: policy requires RS256/2048-bit keys, bounded Cache-Control
             JWKS caching, one refresh/retry for unknown kid, fail-closed stale/
             revoked keys, unique kid with overlap rotation, encrypted private
             keys, durable atomic state/nonce/JTI consumption, 120-second skew,
             600-second launch lifetime, bounded sessions, local logout/token
             revocation, public redacted errors, and tenant/issuer/client/
             deployment/context isolation. -->
  - [ ] M1.12.g Run platform/tool conformance and consumer-driven tests for each
        supported flow/role/failure, declaring unsupported cells. Evidence: LTI
        standards matrix.
        <!-- blocked 2026-08-28: every one of six flow cells declares its role
             and ten failure-class coverage, but the gate requires a passing
             non-synthetic receipt from both the pinned 1EdTech certification
             suite and an independent LTI 1.3 platform per flow (12 total).
             Neither external system has been executed and no genuine receipt
             exists; the target contract is not runtime-adopted or certified. -->
        <!-- half the gate is met 2026-09-12 (M10.6): an independent LTI 1.3
             platform — Moodle 4.5.13+, installed on this server and driving
             Metis through its own API — produced non-synthetic receipts for
             the launch, deep-linking, grade-service and key-rotation flows,
             retained under
             `evidence/v1-workbenches/metis-conformance/moodle-lti-1p3/`. The
             grade is in Moodle's own gradebook table, not in a report of
             ours. The 1EdTech certification suite half is untouched: it is
             hosted and member-only, so §2.5.6 says record the attempt and
             leave the cell open. It is open. -->
- [ ] M1.13 Define OneRoster, Caliper, Open Badges, SCORM, and xAPI profiles.
      Evidence: source-of-truth and directionality for each field.
  - [x] M1.13.a Pin version/conformance/transport/auth/profile/extensions/
        validators and system-of-record ownership for every listed standard.
        Evidence: standards decision matrix.
        <!-- Evidence: `libs/contracts/src/metis/interoperability-profiles.ts`,
             `libs/contracts/src/metis/interoperability-profiles.fixtures.ts`,
             and `evidence/v1-workbenches/metis-interoperability-profiles.json`
             pin OneRoster REST 1.2 + CSV 1.2.1, Caliper 1.2, Open Badges
             3.0 document 1.4.5, SCORM 1.2, SCORM 2004 4th Edition, and
             IEEE 9274.1.1-2023/xAPI 2.0. Every row declares transport,
             authentication, direction, extensions, validator, owner, no
             runtime adoption, and no conformance claim. -->
  - [x] M1.13.b Define OneRoster organization/course/class/term/user/enrollment/
        academic-session/category/line-item/result mappings, directionality,
        identifiers, delta, deletion, and conflict rules. Evidence: OneRoster
        mapping tests.
        <!-- Evidence: the shared contract, its 37-test focused suite, and the
             deterministic evidence cover all ten entity families, tenant +
             connector + sourcedId identity, SIS-owned inbound rosters,
             Metis-owned outbound gradebook records, REST watermarks, complete
             CSV snapshots, tombstones, parent references, and fail-closed
             ambiguity/conflict rules. -->
  - [x] M1.13.c Define Caliper entity/event/action/actor/object/generated/
        membership/assessment/grade semantics, profile versions, privacy, event
        ordering, and delivery. Evidence: Caliper mapping tests.
        <!-- Evidence: Caliper 1.2 Assessment, Grading, Session, and Tool Use
             rules bind actor/object/generated/membership semantics, pairwise
             identity, purpose, eventTime + sequence ordering, TLS Bearer Sensor
             delivery, envelope idempotency, and bounded retry/failure policy. -->
  - [x] M1.13.d Define Open Badges
        issuer/profile/achievement/alignment/criteria/
        evidence/assertion/recipient/status/endorsement mapping and verification
        direction. Evidence: Open Badges tests.
        <!-- Evidence: the Open Badges 3.0 VC Data Model 2.0 target validates
             JSON-LD safe mode, Profile issuer/creator, exact achievement and
             evidence revisions, alignment/criteria, pairwise recipient,
             Bitstring revocation status, endorsement, EdDSA Data Integrity
             proof/key authority, expiry, and presenter-to-verifier direction. -->
  - [x] M1.13.e Define SCORM package/manifest/SCO/asset/sequencing/runtime data/
        completion/success/score/interaction/bookmark/error mappings and
        sandbox. Evidence: SCORM profile tests.
        <!-- Evidence: distinct SCORM 1.2 and 2004 4th Edition package/runtime
             schemas cover root manifests, SCO/assets, 2004 sequencing and
             navigation, API vs API_1484_11, edition-specific CMI status/score/
             interaction/bookmark/error mapping, commit lineage, archive-path
             safety, isolated package origins, CSP, and capability bridging. -->
  - [x] M1.13.f Define xAPI actor/verb/object/result/context/authority/statement
        refs/attachments/state/activity/profile mapping, LRS direction, voiding,
        and privacy. Evidence: xAPI profile tests.
        <!-- Evidence: the IEEE 9274.1.1-2023/xAPI 2.0 contract covers one
             pairwise actor IFI, verb/object/result/context/authority,
             immutable statements and StatementRefs, hash-verified retained
             attachments, ETag-protected State/Activity Profile/Agent Profile
             documents, explicit provider/consumer direction, tenant-bound
             voiding, and a truthful no-full-LRS-conformance boundary. -->
  - [ ] M1.13.g Document lossy/unsupported/conflicting fields, canonical
        precedence, version negotiation, and round-trip/conformance evidence per
        profile. Evidence: cross-standard matrix.
        <!-- Technical matrix complete; external gate open. The deterministic
             artifact classifies 12 canonical fields across all six profiles,
             pins owner-first precedence and fail-closed version negotiation,
             and records synthetic semantic round trips with zero undeclared
             losses. Synthetic results are not conformance receipts. One genuine
             official-validator receipt plus one genuine independent-system
             receipt is still required for each profile (12 genuine external
             receipts total); none exists, so M1.13.g and M1.13 remain open and
             the target contract is not runtime-adopted or conformant. -->
- [x] M1.14 Define psychometric metadata for classical item statistics, 1PL,
      2PL, 3PL, calibration sample, standard error, DIF, exposure, and status.
      Evidence: schema invariants and privacy review.
      <!-- Evidence: `libs/contracts/src/metis/psychometric-metadata.ts`, its
           synthetic fixtures and 39-test focused suite, and
           `evidence/v1-workbenches/metis-psychometric-metadata.json` define
           the complete metadata envelope, exercise 18 fail-closed classes,
           and record 19/19 passing invariant probes. The current runtime audit
           remains explicit: partial CTT/IRT/DIF/exposure helpers exist, but the
           target contract is not runtime-adopted. -->
  - [x] M1.14.a Define analysis/calibration identity/version, exact item
        revision, population/sample/filter/window, method/software/config,
        owner, and lifecycle. Evidence: psychometric run schema.
        <!-- Evidence: the run schema pins tenant, run/version, exact item,
             population/sample, analysis kinds, method, software/version/build,
             config/hash/seed, assumptions, owner, lifecycle, snapshots, and
             chronologically valid terminal output. -->
  - [x] M1.14.b Define classical difficulty/facility, discrimination, point-
        biserial, option/distractor statistics, reliability contribution,
        missingness, counts, and uncertainty. Evidence: CTT schema tests.
        <!-- Evidence: CTT schemas reconcile facility/difficulty and 13 count
             fields, upper/lower and point-biserial discrimination, protected
             keyed/distractor categories, reliability-if-deleted, missingness,
             standard errors, and uncertainty intervals. -->
  - [x] M1.14.c Define 1PL/2PL/3PL ability scale, difficulty, discrimination,
        guessing, information/fit, parameter constraints,
        transformation/linking, standard errors, and convergence. Evidence: IRT
        schema tests.
        <!-- Evidence: exact 1PL/2PL/3PL invariants govern the versioned logit
             scale, a/b/c estimation/fixing, constraints, item information and
             conditional SE, fit/held-out evidence, exact anchor linking and
             transformation uncertainty, and convergence before approval. -->
  - [x] M1.14.d Define calibration sample inclusion/exclusion, size, weights,
        representativeness, administration/item versions, privacy threshold, and
        data quality. Evidence: sample invariants.
        <!-- Evidence: the sample schema pins population/frame/filter/window,
             rules, reconciled counts, weighting/effective n,
             representativeness, exact administration/item versions, quality,
             institution-approved aggregate/subgroup floors, disclosure review,
             suppression, retention, and pseudonymous linkage. -->
  - [x] M1.14.e Define DIF group descriptors through privacy-preserving approved
        dimensions, method, reference/focal groups, effect/significance/
        uncertainty, sample floors, and review outcome. Evidence: DIF tests.
        <!-- Evidence: DIF groups use opaque ids and hashed value tokens with
             approved dimensions/labels/consent; results pin matching, method,
             effect, multiple-comparison significance, uncertainty and floors,
             suppress undersized cells, and require a non-causal human review. -->
  - [x] M1.14.f Define exposure count/rate/window/population/administration/
        security classification and active/field-test/calibrating/review/
        approved/retired/compromised status. Evidence: exposure/status tests.
        <!-- Evidence: exposure binds exact item/window/population/administration
             versions, opportunity count/rate/uncertainty, control policy,
             classification and incident; an append-only, decision-backed state
             machine governs all seven named statuses. -->
  - [x] M1.14.g Separate raw/estimated statistics from policy/quality decisions,
        pin versions, protect small groups/learner data, and prohibit
        unsupported individual inference. Evidence: privacy/invariant review.
        <!-- Evidence: immutable estimate bundles are separate from versioned,
             non-automatic human policy decisions; the privacy review prohibits
             direct identifiers, raw responses/demographics/answer material,
             small-cell release, estimate mutation, and six classes of
             unsupported individual inference with no override. -->
- [x] M1.15 Define academic-integrity evidence, classifier outputs, decisions,
      appeals, and audit references. Evidence: competing integrity vocabularies
      are reconciled without lossy mapping.
      <!-- Evidence: `libs/contracts/src/metis/academic-integrity-evidence.ts`,
           its synthetic fixtures and 43-test focused suite, and
           `evidence/v1-workbenches/metis-academic-integrity-evidence.json`
           define the target envelope, reconcile 12 source vocabularies and 64
           source values with explicit loss status, and exercise 20 fail-closed
           classes through 21/21 invariant probes. The runtime audit remains
           explicit: legacy verdict, detector, incident, appeal, and grade
           projections are partial, and the target contract is not
           runtime-adopted. -->
  - [x] M1.15.a Inventory/reconcile plagiarism, proctoring, authorship,
        similarity, behavior, device/session, assessment-security, incident,
        decision, and appeal vocabularies with versioned mappings. Evidence:
        vocabulary map.
        <!-- Evidence: the registry preserves every namespaced source
             id/version/value/meaning, covers all ten requested concept
             families and seven semantic layers, and marks exact versus
             context-required mappings without permitting automatic
             adjudication. -->
  - [x] M1.15.b Define evidence identity/version/source/collector, learner/
        attempt/item refs, event/time range, integrity hash, classification,
        access, retention, chain of custody, and redaction. Evidence: evidence
        schema.
        <!-- Evidence: versioned evidence pins a pseudonymous learner, exact
             attempt/assessment/item revisions, event and source collection,
             protected artifact/hash, policy-owned access/retention/redaction,
             and chronological hash-linked custody with audit refs. -->
  - [x] M1.15.c Define classifier/rule output with model/code/config/version,
        input refs, scores/features, uncertainty, applicability, limitations,
        threshold, and no direct misconduct conclusion. Evidence: output schema.
        <!-- Evidence: output schemas pin producer/model/code/config, exact
             evidence inputs, source label/mapping, scores/scales, protected
             features, calibrated uncertainty, applicability/OOD, limitations,
             and a review-prioritization-only threshold; four automatic uses
             are prohibited. -->
  - [x] M1.15.d Define human case/decision with allegation/category, evidence
        reviewed, standard/policy, actor/authority, rationale, outcome,
        consequence recommendation, and separation from grade mutation.
        Evidence: decision schema.
        <!-- Evidence: a human-filed allegation is never a finding; adverse
             outcomes require a conflict-checked authorized human and material
             verified/corroborated evidence. Consequences remain
             recommendations and grade mutation requires a separate workflow. -->
  - [x] M1.15.e Define appeal filing/evidence/access/independent reviewer/
        deadlines/stay/outcome/remedy and link it without overwriting the
        original decision. Evidence: appeal contract.
        <!-- Evidence: appeals pin filing/grounds, evidence access, an
             independent authorized reviewer, policy deadlines/timezone, stay,
             outcome/remedy, and a distinct superseding decision while the
             original decision remains immutable. -->
  - [x] M1.15.f Define append-only audit refs for collection/access/redaction/
        sharing/decision/appeal/correction/deletion/legal hold and institution
        boundary. Evidence: audit tests.
        <!-- Evidence: a contiguous, chronological, hash-linked audit trail
             defines all nine actions, resolves custody and target refs, gates
             cross-institution sharing on an agreement/minimum-necessary check,
             and blocks deletion under legal hold. -->
  - [x] M1.15.g Preserve source vocabulary and semantic loss status through
        mappings; reject lossy automatic adjudication. Evidence: reconciliation
        invariants.
        <!-- Evidence: source vocabulary/meaning is mandatory on every mapping;
             non-exact mappings disclose loss and require human interpretation.
             Classifiers cannot open cases, find misconduct, recommend
             consequences, mutate grades, or resolve appeals automatically. -->
- [x] M1.16 Define gradebook correction and visibility contracts. Evidence:
      append-only correction and role-redaction tests.
      <!-- Evidence: `libs/contracts/src/metis/gradebook-corrections.ts`, its
           synthetic fixtures and 49-test focused suite, and
           `evidence/v1-workbenches/metis-gradebook-corrections.json` define
           the target envelope across 10 immutable histories/correction types,
           eight roles, 17 fields, nine actions, six aggregate dimensions, and
           LTI AGS 2.0 plus OneRoster 1.2 receipts. Twenty-nine fail-closed
           classes pass 30/30 invariant probes. The runtime audit remains
           explicit: five legacy grade surfaces are partial or missing, and the
           target contract is not runtime-adopted. -->
  - [x] M1.16.a Define grade record identity/version for learner, course/
        assessment/item/attempt revisions, score/value/status, scale, source,
        grader, timestamps, and evidence refs. Evidence: grade schema.
        <!-- Evidence: every grade record pins a pseudonymous learner, exact
             course/assessment/item and submission revisions, attempt, bounded
             state/scale, source, grader, timestamps, protected evidence, and
             exact-hash contiguous prior-version lineage. -->
  - [x] M1.16.b Define append-only correction command with target grade version,
        reason/category, old/new values, evidence, requester, approver,
        effective time, and idempotency. Evidence: correction schema.
        <!-- Evidence: immutable version-one commands bind the exact expected
             grade, policy, old/new state, protected reason/evidence,
             institution-scoped actors, approval/application chronology, and a
             tenant+grade+version+category idempotency key. -->
  - [x] M1.16.c Define recalculate, regrade, override, void, excuse, late/
        incomplete, moderation, appeal, and import correction semantics
        separately. Evidence: correction-type tests.
        <!-- Evidence: ten discriminated command schemas preserve separate
             formula, rubric, exceptional authority, void, eligibility, late,
             incomplete, panel, appeal-decision, and import-conflict meaning;
             the complete fixture exercises every category exactly once. -->
  - [x] M1.16.d Enforce eligible states/roles, separation of duties, expected
        version, policy, scale/range, downstream aggregate impacts, and audit.
        Evidence: correction guard tests.
        <!-- Evidence: versioned policy rows govern eligible states and actors,
             high-risk commands separate requester/approver, scale/range and
             exact-version guards fail closed, all six downstream dimensions
             receive one impact, and 79 append-only hash-linked events audit
             request/approval/application/release/notification/propagation. -->
  - [x] M1.16.e Define field/action visibility for learner, guardian, teacher,
        assessor, administrator, integrity reviewer, support, and integrations
        by release window/privacy. Evidence: visibility matrix.
        <!-- Evidence: the default-deny matrix defines all 17 fields and nine
             actions for eight roles; every grant requires privacy policy,
             learner/guardian/integration access is release-gated with its
             specific authority, and support requires a legitimate active case
             while sensitive support/integration fields remain denied. -->
  - [x] M1.16.f Project current grade from immutable history, preserve prior
        values/reasons, notify affected actors safely, and propagate LTI/
        OneRoster corrections idempotently. Evidence: end-to-end tests.
        <!-- Evidence: current state is derived from exact immutable history;
             prior state/reasons stay protected; 20 idempotent notifications
             disclose no narrative/evidence/internal actor/direct identifier;
             and 20 exact-lineage LTI/OneRoster receipts use unique keys and
             strictly newer timestamps without claiming platform adoption or
             standards certification. -->
- [x] M1.17 Define course-build and assessment-evidence bundles. Evidence:
      canonical contract goldens validate.
      <!-- Evidence: `libs/contracts/src/metis/canonical-bundles.ts`, its
           synthetic fixtures and focused suite, six byte-canonical JSON
           goldens, and `evidence/v1-workbenches/metis-canonical-bundles.json`
           define distinct publishable course and protected assessment-record
           envelopes across 23 artifact kinds. All six valid bundles and 29
           fail-closed classes pass 35/35 invariant probes. The runtime audit
           remains explicit: six adjacent or legacy surfaces do not adopt the
           target contract, which is not runtime-adopted. -->
  - [x] M1.17.a Define course-build bundle manifest with exact course hierarchy/
        content/objective/standard/item/stimulus/rubric/media/localization/
        accessibility revisions, dependencies, target profile, and hashes.
        Evidence: course bundle schema.
        <!-- Evidence: every course golden binds exact source revision/content
             hashes, exact dependency refs, target consumers/features/locales,
             and canonical payload hashes; the full golden covers assessment,
             item, stimulus, rubric, media, localization, accessibility, and
             interactive artifacts in addition to the base hierarchy. -->
  - [x] M1.17.b Define assessment-evidence bundle manifest with assessment/item/
        stimulus/rubric revisions, delivery config, response/evidence refs,
        scoring/moderation, integrity, accommodations projection, grades/
        corrections, and protected-field partitions. Evidence: evidence schema.
        <!-- Evidence: the protected golden requires all 13 assessment-record
             kinds, exact attempt/root refs, a pseudonymous subject hash, and
             exactly one learner, answer, integrity, and review partition. -->
  - [x] M1.17.c Define encryption/signature/access/retention/redaction/selective
        disclosure for learner/answer/integrity evidence separately from
        publishable course content. Evidence: security tests.
        <!-- Evidence: course payloads are publishable canonical JSON with no
             protected record kinds; every protected payload is ciphertext in
             an A256GCM envelope partition with unique nonce/key-ref hashes,
             default-deny access, retention/redaction policy refs, and
             minimum-necessary disclosures. Detached EdDSA JWS values remain
             explicitly synthetic-unverified, not production crypto proof. -->
  - [x] M1.17.d Resolve exact dependencies, normalize manifest/order/filenames/
        timestamps, compute hashes, and declare external/redacted/missing refs.
        Evidence: determinism tests.
        <!-- Evidence: the profiled I-JSON/JCS serializer rejects ambiguous
             values and pins object/array/ref/path/timestamp/archive ordering;
             manifests, payload bytes, and ordered payload sets are rehashed,
             dependency graphs are exact and acyclic, paths are portable, and
             external/redacted/optional-missing descriptors are explicit. -->
  - [x] M1.17.e Define
        version/migration/compatibility/loss/validator/import/read- back
        semantics for each target consumer. Evidence: compatibility tests.
        <!-- Evidence: every target consumer has an exact, migration-required,
             lossy, or unsupported result with source-preserving migration and
             explicit loss rules, required internal schema/round-trip checks,
             and hash-bound import/read-back receipts. External Common
             Cartridge validation and certification remain explicitly not run. -->
  - [x] M1.17.f Create minimal/full/multilocale/accessibility/interactive/
        protected-evidence goldens and validate independent round-trip.
        Evidence: canonical bundle suite.
        <!-- Evidence: six deterministic files under
             `libs/contracts/src/metis/goldens/canonical-bundles/` cover the
             five named course profiles and protected assessment evidence;
             separate disk readers parse, reserialize, and rehash each file
             byte-for-byte without using the generator write path. -->
- [ ] M1.18 Add machine-readable standards/version metadata to every exported
      package. Evidence: compatibility probe.
      <!-- Partial evidence: `libs/contracts/src/metis/package-metadata.ts`,
           fixtures/spec, ten canonical goldens under
           `libs/contracts/src/metis/goldens/package-metadata/`, and
           `evidence/v1-workbenches/metis-package-metadata.json` complete the
           target schema, native mappings, capability vocabulary, integrity
           binding, and compatibility probe. The parent remains open because
           runtime export/import adoption is not complete. -->
  - [x] M1.18.a Define common package metadata for format/profile/spec version,
        conformance modules, extensions, producer/version, build/contract
        version, target, locale, generated time policy, and manifest digest.
        Evidence: metadata schema.
        <!-- Evidence: the strict common envelope pins current metadata,
             producer/build/contract/target/locale fields, deterministic UTC
             generation policy, exact conformance modules/extensions, and a
             manifest digest whose scope omits the injected metadata node to
             avoid circular hashing. -->
  - [x] M1.18.b Map common metadata into QTI/LTI-deep-link/OneRoster/Caliper/
        Open Badges/SCORM/xAPI/course-build/lecture package-native locations.
        Evidence: per-standard mapping.
        <!-- Evidence: ten registry rows pin the exact container, native path,
             encoding, binding mode, round-trip result, and caveat. OneRoster
             CSV is truthfully modeled as a lossy non-standard sidecar rather
             than an invented standard field; the other external bindings are
             namespaced metadata/extension or declared package-resource
             locations, SCORM sidecar loss is disclosed, and both Metis
             formats are native. -->
  - [x] M1.18.c Declare supported/required/optional/extension/lossy/unsupported
        features and dependency/runtime versions machine-readably. Evidence:
        capability manifest tests.
        <!-- Evidence: all six capability states are schema-pinned, extension
             declarations and exact loss reasons are enforced, required and
             optional dependencies carry resolved versions, and fixtures/tests
             exercise every state. -->
  - [x] M1.18.d Sign/hash metadata with the package and prevent divergence
        between human documentation, filename, manifest, and payload. Evidence:
        integrity tests.
        <!-- Evidence: the logical integrity profile recomputes canonical
             metadata, manifest, ordered payload-set, and native-binding hashes;
             their package digest binds the detached signature and derives the
             filename and exact human-readable projection. Twenty-five hostile
             mutations prove fail-closed divergence checks. Fixture signatures
             remain explicitly synthetic-unverified. -->
  - [x] M1.18.e Build a pre-import compatibility probe returning compatible,
        migration-required, degraded/lossy, unsupported, invalid, or unknown
        with exact reasons. Evidence: probe tests.
        <!-- Evidence: deterministic probes cover every named outcome, validate
             current envelopes before negotiation, identify registered
             migrations, reject required feature/dependency gaps, disclose
             optional/extension/loss degradation, and return unknown rather
             than guessing unrecognized metadata or format versions. -->
  - [ ] M1.18.f Verify every exporter emits current metadata and every importer
        enforces it rather than guessing from content. Evidence: export coverage
        gate.
        <!-- Blocked by implementation: all ten audited runtime adapters still
             lack the common envelope on export and fail-closed compatibility
             enforcement on import. The evidence runtime audit names each
             surface; target fixtures are not runtime-adoption proof. -->
        <!-- SPEC 2026-09-11: the ten runtime adapters are named in the runtime
        audit inside `evidence/v1-workbenches/metis-package-metadata.json`. Each
        exporter emits the common envelope from
        `libs/contracts/src/metis/package-metadata.ts`; each importer refuses a
        package without it (fail-closed, typed reason); the coverage gate fails
        on any adapter not doing both. -->
- [x] M1.19 Define credential template, requirement, evidence, issuer, recipient
      projection, achievement, badge, endorsement, issuance, verification,
      expiry, revocation, and replacement contracts. Evidence: Open Badges
      mapping and authority/privacy invariants.
  - [x] M1.19.a Define credential template/revision identity, title/description/
        image, type, locale, issuer eligibility, achievement alignment,
        criteria, evidence policy, validity, and lifecycle. Evidence: template
        schema.
  - [x] M1.19.b Define nested all/any/count/sequence/time/grade/mastery/course/
        assessment/experience requirements with exact revisions and
        deterministic evaluation. Evidence: requirement tests.
  - [x] M1.19.c Define evidence refs/types/issuer/source/subject/integrity/
        verification/access/retention and minimize learner detail exposed to
        authoring or public verifiers. Evidence: evidence contracts.
  - [x] M1.19.d Define issuer identity/authority/keys/status/endorsements plus
        privacy-preserving recipient identifier/proof and subject binding.
        Evidence: issuer/recipient tests.
  - [x] M1.19.e Define achievement/badge/assertion/endorsement mapping to Open
        Badges with alignments, criteria, evidence, dates, status, signatures,
        hosted/signed forms, and extensions. Evidence: mapping goldens.
  - [x] M1.19.f Define issuance request/evaluation/approval/signing/delivery/
        receipt idempotency and prohibit course authors from self-granting
        issuer or recipient authority. Evidence: issuance tests.
  - [x] M1.19.g Define verification for signature/hash/issuer/status/recipient/
        evidence/expiry/revocation and privacy-safe public responses. Evidence:
        verifier tests.
  - [x] M1.19.h Define expiry/renewal/revocation/replacement/supersession with
        reason, effective time, status lists/notifications, lineage, and no
        history rewrite. Evidence: lifecycle invariants.
        <!-- Completed by the versioned credential-lifecycle schema/evaluators,
             4 canonical Open Badges mapping goldens, 34 hostile probes, and
             generated evidence artifact. These are synthetic target-contract
             evidence, not production runtime adoption, cryptographic proof,
             external validation, or Open Badges certification. The runtime
             audit remains false and names the legacy migration blockers. -->
- [x] M1.20 Define prerecorded lecture packages with script, segment, speaker or
      avatar, voice/likeness consent, slides/diagrams, demonstrations, captions,
      transcript, audio description, translations, media renditions, sources,
      checks, and playback manifest. Evidence: versioned package goldens.
  - [x] M1.20.a Define lecture/package/logical/revision identity, course/lesson/
        objective links, locale, duration/timebase, owner, lifecycle, target,
        and integrity manifest. Evidence: lecture schema.
  - [x] M1.20.b Define versioned script and segment/chapter records with text,
        objectives, sources/citations, timing, speaker, visual/demo cues, and
        revision dependencies. Evidence: script/segment tests.
  - [x] M1.20.c Define human/synthetic/avatar presenter identity, role,
        language, pronunciation, performance, voice/likeness/model/source
        consent scope, disclosure, expiry, and revocation. Evidence: presenter
        contracts.
  - [x] M1.20.d Define slides/diagrams/charts/code/labs/demonstrations/media
        ingredients with exact source/data/code/renderer/rights/provenance and
        segment alignment. Evidence: visual ingredient tests.
  - [x] M1.20.e Define transcript/caption/audio-description/sign-language/
        translation variants with language, timing, speaker/non-speech cues,
        review, and source-revision linkage. Evidence: accessibility contracts.
  - [x] M1.20.f Define video/audio/slide/text rendition profiles with codec/
        dimensions/rate/color/loudness/bitrate/device/bandwidth/offline and
        quality checks. Evidence: rendition tests.
  - [x] M1.20.g Define correctness/source, safety, consent/rights,
        accessibility, sync, technical, target, and reviewer evidence required
        per package. Evidence: check registry.
  - [x] M1.20.h Define playback manifest for chapters/tracks/renditions/
        captions/descriptions/translations/download/streaming/fallback with
        exact hashes and no hidden authority. Evidence: playback tests.
  - [x] M1.20.i Produce deterministic minimal/full/multilingual/accessible/
        avatar/demo goldens and validate package/playback round-trip. Evidence:
        lecture package suite.
        <!-- Completed by the immutable lecture-package target schema/evaluator,
             6 canonical package/playback goldens, 30 named failure classes,
             source decisions, runtime audit, and generated evidence artifact.
             This is synthetic target-contract evidence, not runtime adoption,
             external media/C2PA/HLS/consent validation, WCAG conformance, or
             an operational-readiness claim. -->
- [x] M1.21 Define tutoring configuration for persona, pedagogy, hint ladder,
      source set, course/item revision, learner-context projection, live
      voice/text mode, tools, escalation, safety, integrity mode, and session
      retention. Evidence: authoring settings cannot grant the tutor forbidden
      learner or answer-key access.
  - [x] M1.21.a Define immutable tutor configuration/revision identity, owner/
        approver, applicable course/lesson/objective/item/locale/audience,
        lifecycle, and code/institution floors. Evidence: config schema.
  - [x] M1.21.b Define bounded persona/tone/language/disclosure and versioned
        pedagogy/strategy/scaffolding rules without impersonation or hidden
        human claims. Evidence: persona/pedagogy tests.
  - [x] M1.21.c Define hint ladder stages, eligibility, attempt/timing triggers,
        source/rationale boundaries, penalties, exhaustion, and protected-answer
        nonexposure. Evidence: hint tests.
  - [x] M1.21.d Define authorized source collection/course/item revision set,
        retrieval/citation policy, update/retraction invalidation, and fail-
        closed grounding behavior. Evidence: source-set tests.
  - [x] M1.21.e Define minimized learner-context projection fields/purpose/
        freshness/retention and prohibit raw profile/grade/integrity/diagnosis/
        answer-key access. Evidence: context property tests.
  - [x] M1.21.f Define text/live voice input-output, speaker/voice consent,
        transcript/caption, interruption, latency, recording, disclosure, and
        degraded-mode behavior. Evidence: modality tests.
  - [x] M1.21.g Define allowlisted versioned tools, typed grants, resource/
        network/data limits, confirmation, outputs, audit, and no configuration-
        granted privilege. Evidence: tool authorization tests.
  - [x] M1.21.h Define safety, child/privacy, self-harm/escalation, academic-
        integrity modes per assessment state, refusal, educator handoff, and
        incident paths. Evidence: policy matrix.
  - [x] M1.21.i Define session duration/context/history/summary/retention/
        deletion/export/analytics and exact configuration/course/model versions.
        Evidence: session lifecycle tests.
        <!-- Completed by the immutable tutoring-configuration target schema,
             deny-by-default learner/content/tool authorization evaluator,
             external typed runtime-grant boundary, governed hint evaluator,
             3 session-lifecycle goldens, 32 named failure classes, source
             decisions, runtime audit, and generated evidence artifact. This is
             synthetic target-contract evidence, not runtime adoption, external
             policy/consent/grant validation, crisis integration, deletion
             execution, WCAG conformance, or an operational-readiness claim. -->
- [x] M1.22 Define learner-profile and preference projections used by preview,
      adaptation, accessibility, and tutor simulation without copying protected
      learner records into authoring storage. Evidence: minimization and
      purpose-boundary tests.
  - [x] M1.22.a Inventory candidate learner/profile/preference/PNP/mastery/
        progress/grade/integrity fields, canonical owners, classifications, and
        prohibited authoring uses. Evidence: field inventory.
  - [x] M1.22.b Define separate purpose-bound projections for anonymous author
        preview, authorized adaptation, accessibility delivery, tutor session,
        and synthetic simulation. Evidence: projection schemas.
  - [x] M1.22.c Use opaque subject/profile/simulation identities and expose only
        minimum locale, support capability, prior-knowledge band, preferences,
        and contextual state required for each purpose. Evidence: minimization
        tests.
  - [x] M1.22.d Represent unknown/withheld/not-applicable/stale values
        explicitly and prevent inference of diagnoses, protected traits, grade,
        or integrity status. Evidence: inference tests.
  - [x] M1.22.e Authorize issuer/consumer/purpose/tenant/course/session, bind
        source/freshness/expiry, prevent caching/authoring persistence, and
        audit access. Evidence: purpose-boundary tests.
  - [x] M1.22.f Define correction/revocation/deletion/session-end invalidation
        and prove projections disappear from temporary
        stores/logs/prompts/exports. Evidence: lifecycle tests.
  - [x] M1.22.g Build synthetic profile fixtures for authoring simulations so
        real learners are never required for general preview/testing. Evidence:
        simulation isolation tests.
        <!-- M1.22 completion evidence: `learner-profile-projections.ts` and
             deterministic generated evidence inventory 42 candidate fields,
             define five separately scoped projection profiles, preserve all
             unavailable-value states, deny 17 purpose/storage/inference
             boundary cases, sweep six temporary locations for five lifecycle
             triggers, and provide three hand-authored synthetic profiles. This
             is synthetic target-contract evidence, not runtime adoption,
             operational authorization, production erasure execution,
             FERPA/COPPA compliance, AccessForAll conformance, or legal advice. -->

### M2 — Source ingest, grounding, and curriculum planning

- [x] M2.1 Implement source intake from upload, URL, feed, LMS, repository, DOI,
      and manual record. Evidence: each path records provenance and checksum.
  - [x] M2.1.a Define immutable source/intake/version/provenance/checksum,
        connector, authority, rights, classification, and lifecycle contracts.
        Evidence: intake schemas.
  - [x] M2.1.b Implement bounded multipart upload with content sniffing, hash-
        while-streaming, temporary quarantine, and tenant isolation. Evidence:
        upload tests.
  - [x] M2.1.c Implement allowlisted URL/feed retrieval with SSRF/redirect/DNS/
        size/type/time protections and immutable retrieval receipt. Evidence:
        network intake tests.
  - [x] M2.1.d Implement LMS/repository connectors with scoped credentials,
        stable external IDs/versions, pagination/rate limits, and deletion/
        update semantics. Evidence: connector tests.
  - [x] M2.1.e Resolve DOI metadata/content through approved registries with
        authoritative identifier, edition/version, access/rights, and no false
        full-text assumption. Evidence: DOI fixtures.
  - [x] M2.1.f Implement manual bibliographic/source record with required
        provenance/evidence and explicit no-bytes/unchecked status. Evidence:
        manual intake tests.
  - [x] M2.1.g Normalize all paths to one source-version record with original
        locator, retrieval time, content hash, connector/tool versions, and
        audit. Evidence: cross-path parity tests.
        <!-- M2.1 completion evidence: `SourceVersionRecord` and the Metis
             source-intake runtime normalize upload, URL, feed, LMS,
             repository, DOI, and manual-record paths into one immutable,
             tenant-scoped provenance/checksum/audit contract. Focused API,
             streaming, SSRF, connector, DOI-fixture, manual-record, parity,
             OpenAPI, and generated-client checks provide runtime implementation
             evidence; M2.2 scan admission is separately evidenced and production
             deployment remains an operational gate. -->
- [x] M2.2 Quarantine and scan source uploads. Evidence: malicious document,
      archive, macro, and oversize fixtures.
  - [x] M2.2.a Store new bytes in nonpublic nonexecutable quarantine with opaque
        names, tenant scope, encryption, limits, and no downstream parser
        access. Evidence: quarantine tests.
  - [x] M2.2.b Scan malware/signatures, content type/polyglot, macros/scripts/
        active content, archive traversal/symlinks/nesting/bombs, and parser
        exploit indicators. Evidence: malicious fixture suite.
  - [x] M2.2.c Enforce compressed/uncompressed size, file/count/depth, page/
        object/media, CPU/time/memory, and password/encryption rules. Evidence:
        boundary tests.
  - [x] M2.2.d Record scanner/database/version/config/hash/results and classify
        clean/quarantined/rejected/manual/unavailable without false clean state.
        Evidence: result schema tests.
  - [x] M2.2.e Admit exact scanned hash only after all required scans; rescan on
        scanner/policy/freshness change and retain prior evidence. Evidence:
        admission/race tests.
        <!-- M2.2 completion evidence: encrypted AES-256-GCM tenant quarantine,
             bounded built-in structural inspection, a versioned fail-closed
             ClamAV command adapter, immutable self-hashed scan/admission
             receipts, exact-hash parser gating, superseding-rescan revocation,
             and malicious document/archive/macro/oversize fixtures are covered
             by the Metis runtime and generated evidence inventory. This is
             runtime implementation evidence, not a security certification;
             production key custody and live ClamAV database delivery remain deployment gates. -->
- [x] M2.3 Extract text, structure, tables, equations, media, and metadata with
      page/section anchors. Evidence: golden corpus.
  - [x] M2.3.a Define extraction document/node/anchor/confidence/error contracts
        with exact source hash and extractor/config versions. Evidence:
        extraction schema.
  - [x] M2.3.b Extract ordered text,
        headings/sections/lists/footnotes/citations/ reading order with
        page/region/character anchors. Evidence: structure goldens.
  - [x] M2.3.c Extract tables with cells/spans/headers/captions/units/footnotes
        and page regions; preserve image fallback. Evidence: table goldens.
  - [x] M2.3.d Extract equations as source/math representation plus visible
        text/ image, labels, variables, and page regions without inventing
        semantics. Evidence: equation goldens.
  - [x] M2.3.e Extract embedded images/audio/video/files with hashes, captions/
        alt text, time/page anchors, technical metadata, and source rights.
        Evidence: media goldens.
  - [x] M2.3.f Extract bibliographic/document metadata with
        source-versus-derived provenance, normalization, confidence, and
        conflicts. Evidence: metadata fixtures.
  - [x] M2.3.g Test multilingual/RTL/scanned/OCR/complex layout/malformed/
        password/partial cases against licensed golden corpus. Evidence:
        extraction quality report.
        <!-- M2.3 completion evidence: the source-bound 2.3 extraction graph,
             typed W3C-aligned character/page/region/time selectors, bounded
             Markdown/HTML/PDF/DOCX/image/archive/media readers, Tesseract TSV
             seam, explicit table/equation/media/metadata provenance, and the
             synthetic CC0 golden corpus are exercised by runtime, OpenAPI,
             generated-client, and evidence checks. This is implementation and
             deterministic corpus evidence, not an extraction-quality
             certification; production parser and OCR binaries/language packs remain deployment gates,
             alongside representative institution-owned corpus calibration. -->
- [x] M2.4 Preserve original bytes and extraction version. Evidence:
      re-extraction does not rewrite historical evidence.
  - [x] M2.4.a Store original bytes immutably by content hash with
        source-version manifest, encryption, access, retention, and legal-hold
        metadata. Evidence: storage integrity tests.
  - [x] M2.4.b Define extraction-run identity with exact source hash,
        parser/OCR/ model/config/runtime versions, status, outputs, errors, and
        timestamps. Evidence: extraction run schema.
  - [x] M2.4.c Persist extracted artifacts/anchors under the run identity and
        forbid in-place correction or replacement. Evidence: immutability tests.
  - [x] M2.4.d Re-extract as a new run, compare/diff outputs, migrate accepted
        anchors explicitly, and retain consumers of old evidence. Evidence:
        re-extraction tests.
  - [x] M2.4.e Verify stored source/extraction hashes on read/reuse and
        quarantine corruption without rewriting history. Evidence: corruption
        tests.
        <!-- M2.4 completion evidence: tenant-partitioned content-addressed
             originals use authenticated AES-256-GCM envelopes and immutable
             source-version manifests with retention, legal-hold, access, and
             disposition policy. Exact source/extractor/runtime/config/model-use
             identities, outputs, bindings, errors, retries, parents,
             supersession, comparisons, selection transitions, accepted anchor
             migrations, and corruption quarantine receipts are append-only and
             hash-verified on reuse. Deterministic/concurrent retry, historical
             re-extraction, partial/failed, refresh invalidation, tenant access,
             and tamper tests cover the local runtime. Production KMS/HSM custody and replicated object-lock storage remain
             deployment gates, alongside backup/restore exercises, approved
             retention schedules, and operational legal-hold procedures. -->
- [x] M2.5 Implement source authority, license, currency, jurisdiction, and
      audience metadata. Evidence: validation and review UI.
  - [x] M2.5.a Define publisher/author/institution/issuer authority identity,
        evidence, role, credentials, reputation limits, and verification state.
        Evidence: authority schema.
  - [x] M2.5.b Define source/license/version/owner, access/use/reproduce/adapt/
        teach/train/distribute rights, attribution, territory, term, and
        evidence. Evidence: license schema.
  - [x] M2.5.c Define published/updated/retrieved/reviewed/effective/expired
        dates, supersession/retraction, subject volatility, and currency review
        interval. Evidence: currency tests.
  - [x] M2.5.d Define jurisdiction/geographic scope/applicability/conflicts and
        intended learner age/grade/language/prerequisite/accessibility audience.
        Evidence: jurisdiction/audience schemas.
  - [x] M2.5.e Validate completeness/compatibility/freshness and present raw
        metadata/evidence/conflicts/reviewer decision separately. Evidence:
        validation/review UI tests.
        <!-- Implemented in the source-governance 2.5.0 contracts, deterministic
             validation helpers, append-only source-version-bound governance
             service, tenant and admin APIs, generated OpenAPI client, and the
             Metis admin Source Evidence Review workspace. Authority identity,
             credentials, evidence, explicitly bounded reputation, the seven
             rights actions, attribution/territory/term, currency lifecycle,
             jurisdiction conflicts, BCP-47-shaped audience language, learner
             age/grade/prerequisite/accessibility, raw declarations, evidence,
             conflicts, live findings, and authenticated decisions remain
             structurally separate. Python service/API/tamper/idempotency/
             tenant tests, admin component tests, responsive Playwright flows,
             and automatic WCAG analysis cover the local runtime; see
             evidence/v1-workbenches/metis-source-governance.json. Production
             registry integrations and legal license clearance remain
             deployment and governance gates, together with ratified
             institution-specific jurisdiction/audience policy, qualified
             reviewer operations, replicated durable storage, and backup/
             restore exercises. -->
- [x] M2.6 Implement claim extraction behind the fenced provider seam. Evidence:
      proposals require human confirmation.
  - [x] M2.6.a Define claim proposal with exact source/extraction revision,
        text/normalized proposition, type/scope/qualifiers, locators,
        confidence, provider/model/prompt/config version, and status. Evidence:
        schema tests.
  - [x] M2.6.b Send only authorized bounded source passages/instructions through
        the injected provider, fenced as untrusted data with no tools/authority.
        Evidence: injection/data-minimization tests.
  - [x] M2.6.c Validate provider schema/locators/quotes/size, reject unsupported
        source assertions, and persist refusal/malformed/unavailable honestly.
        Evidence: provider seam tests.
  - [x] M2.6.d Present proposals with source context, uncertainty, duplicates/
        conflicts, and accept/edit/reject/defer actions. Evidence: review tests.
        <!-- Implemented in the source-claims 2.6.1 contracts, exact-extraction-
             bound append-only service, injected OpenAI-compatible provider
             protocol, tenant and admin APIs, generated OpenAPI client, and the
             Metis admin Claim Confirmation workspace. Only current,
             governance-approved source passages with explicit access/use/
             reproduce/adapt permission are selected by exact node character
             range. Source and instructor inputs are rendered as separately
             labeled untrusted non-instruction data; the provider receives no
             tool surface or authority. Quote offsets, substrings, schema,
             relationships, numeric/lexical support, and a two-MiB output bound
             fail closed, while refused, malformed, unavailable, and completed
             runs remain immutable and visible without partial proposals.
             Explicit administrator accept/edit decisions atomically create
             hash-linked canonical revisions that preserve the exact proposal,
             provider invocation, extraction/run hashes, and authority decision;
             reject/defer, unauthorized, or stale decisions cannot create a
             revision, and later withdrawal never rewrites revision history.
             Python provider/service/API/tamper/idempotency/tenant tests, admin
             component tests, three-browser Playwright edit/stale/mobile flows,
             and automatic WCAG analysis cover the local runtime; see
             evidence/v1-workbenches/metis-source-claims.json. Production
             provider credentials and availability remain deployment gates,
             together with representative-corpus quality calibration,
             adversarial red-team exercises, qualified reviewer operations,
             legal source clearance, replicated durable storage, backup/
             restore exercises, and production monitoring. -->
  - [x] M2.6.e Create canonical claim revisions only from explicit authorized
        human decisions; preserve original proposal/model provenance. Evidence:
        authority boundary tests.
- [x] M2.7 Implement citation attachment with page, section, paragraph,
      timecode, dataset slice, or URI fragment locators. Evidence: locator
      validation fixtures.
  - [x] M2.7.a Define citation identity/version, claim/content target, exact
        source/extraction revision, locator kind/value, quote/context, role,
        author, and verification state. Evidence: citation schema.
  - [x] M2.7.b Implement page/region, heading/section, paragraph/character,
        media timecode/range, dataset row/column/filter/version, and URI
        fragment locators as typed variants. Evidence: locator fixtures.
  - [x] M2.7.c Validate locator syntax/bounds/source-kind/revision and resolve
        to exact content without mutable URL/page assumptions. Evidence:
        validation tests.
  - [x] M2.7.d Detect source/extraction changes that make locators exact,
        shifted, ambiguous, orphaned, or invalid and require re-review.
        Evidence: anchor survival tests.
  - [x] M2.7.e Render/export accessible citation context and preserve rights/
        excerpt limits without exposing protected sources. Evidence: citation
        UI/ export tests.
        <!-- Implemented in the source-citations 2.7.0 discriminated contracts,
             offline exact-extraction resolver, append-only citation service,
             tenant and admin APIs, generated OpenAPI client, and the Claim
             Confirmation citation provenance ledger. Citation versions bind
             claim/content targets to exact extraction outputs, source-version
             records, original-locator hashes, typed locators, resolved anchors,
             quote/context, roles, authenticated authors, review state, and
             rights snapshots. Page/normalized-region, section/path,
             paragraph/half-open character, media/half-open millisecond,
             dataset row/column/filter/version, and RFC 3986 fragment fixtures
             resolve only inside immutable extraction output without network
             dereference or mutable page assumptions. Selected extraction,
             source, claim-authority, and rights changes reclassify exact,
             shifted, ambiguous, orphaned, or invalid locators and require an
             append-only reviewed reattachment. Exports apply the stricter of
             snapshotted/current rights, cap eligible excerpts at 500
             characters, and redact protected context; every caller-facing
             attachment, reattachment, package, and admin workspace response
             projection omits stored quote, surrounding context, locator, and
             rights objects entirely. Strict Python
             schema/resolver/service/API/tenant/idempotency/tamper tests,
             OpenAPI drift checks, admin typecheck/lint/component tests, and
             three-browser Playwright attachment/redaction/mobile journeys with
             automatic WCAG analysis cover the local runtime; see
             evidence/v1-workbenches/metis-source-citations.json. Production
             deployment and legal rights clearance remain gates, together with
             representative-corpus anchor-survival measurement, qualified
             reviewer operations, replicated durable storage, backup/restore,
             monitoring, and re-review service levels. -->
- [x] M2.8 Implement high-risk severity floors for health, legal, safety, and
      other ratified domains. Evidence: authoritative-source requirements.
  - [x] M2.8.a Define versioned risk-domain registry, severity levels,
        claim/use/ audience applicability, and nonwaivable code floors.
        Evidence: risk schema.
  - [x] M2.8.b Define minimum source authority, recency, jurisdiction,
        corroboration, citation, reviewer qualification, disclaimer/escalation,
        and update monitoring per severity. Evidence: requirement matrix.
  - [x] M2.8.c Classify curriculum claims/lessons/items/tutor settings using
        typed evidence and route ambiguous/high severity to human review.
        Evidence: classification tests.
  - [x] M2.8.d Block planning/publication/tutor use when authoritative-source or
        qualified-review requirements are missing/stale/conflicting. Evidence:
        fail-closed tests.
  - [x] M2.8.e Test institution overlays can tighten but never loosen floors and
        that source withdrawal propagates. Evidence: monotonicity/lifecycle
        tests.
        <!-- Implemented in the source-risk-policy 2.8.0 contracts, deterministic
             self-hashed domain registry, immutable tenant-scoped assessment and
             review service, administrator decision workspace, generated OpenAPI
             client, and planning/generation/publication/tutoring read-back gates.
             Health, legal, safety, and financial policies each define general,
             elevated, high, and critical floors across exact target, use,
             audience, and consequence applicability. Current governed source
             authority and age, exact jurisdiction, independently governed
             corroboration, verified target-bound citations, qualified review,
             disclaimer, escalation, monitoring, withdrawal, and critical tutor
             prohibition are evaluated independently per requested use. Claims,
             lessons, assessment items, tutor settings, and curriculum scope bind
             exact revisions or hashes; ambiguity and high severity require a
             qualified evidence-hash-bound human decision. New institution overlay
             versions may only tighten both code floors and their own previous
             version. Current evidence is re-derived at each downstream boundary,
             so changed overlays, stale review hashes, source withdrawal, or
             target/citation drift block use without rewriting history. Python
             schema/helper/service/API/idempotency/tamper/tenant/lifecycle tests,
             OpenAPI drift checks, admin typecheck/lint/component tests, and
             responsive Playwright decision/review journeys with automatic WCAG
             analysis cover the local runtime; see
             evidence/v1-workbenches/metis-source-risk-policy.json. The recorded
             WHO, NIST, GovInfo, OSHA, CFPB, and UNESCO sources inform a
             conservative engineering baseline rather than jurisdictional,
             clinical, legal, safety, or financial ratification. Production
             ratification, qualified reviewer staffing, representative-corpus
             calibration, durable
             replicated storage, backup/restore, monitoring operations, and
             deployment validation remain explicit gates. -->
- [x] M2.9 Implement source update/retraction blast radius across lessons,
      items, feedback, tutor prompts, and published courses. Evidence:
      known-value propagation test.
  - [x] M2.9.a Maintain forward/reverse versioned dependency edges from source/
        extraction/claim/citation into all named consumers and release bundles.
        Evidence: dependency graph tests.
  - [x] M2.9.b Ingest verified update/supersession/retraction/rights change with
        effective time, reason, authority, scope, and source identity matching.
        Evidence: change intake tests.
  - [x] M2.9.c Traverse direct/transitive impacts with stable pagination,
        reason/path/current revision/status, unknown dependencies, and tenant/
        property authorization. Evidence: blast-radius fixtures.
  - [x] M2.9.d Mark affected draft/review/published/tutor/delivery eligibility
        stale or blocked according to risk policy without deleting history.
        Evidence: propagation tests.
  - [x] M2.9.e Route owners to re-source/revise/review/rebuild/unpublish and
        verify all downstream states/read-backs. Evidence: known-value
        end-to-end test.
        <!-- Implemented in the source-blast-radius 2.9.0 contracts and service,
             with immutable content-addressed graph snapshots, forward/reverse
             versioned source-to-consumer edges, explicit unknown dependencies,
             exact source-version identity checks, verified authority evidence,
             append-only hash-chained change events, deterministic shortest-path
             traversal, signed snapshot-bound pagination, tenant/property
             authorization, and immutable remediation receipts. Effective
             updates, supersessions, retractions, and rights changes mark the
             exact current lesson/item/feedback/tutor/publication/delivery
             revisions stale or blocked; planning, generation, promotion,
             publication, tutoring, and delivery re-check unresolved impacts
             without rewriting historical artifacts. The administrator lineage
             ledger exposes exact paths, owners, authority evidence, snapshot
             provenance, and re-source/revise/review/rebuild/unpublish routing.
             A known-value retraction fixture proves the complete source ->
             extraction -> claim -> citation -> lesson path, every named
             consumer, stable pagination and tamper rejection, access isolation,
             all downstream gates, unpublish safety, and unchanged historical
             bytes. OpenAPI/client drift checks, Python integration tests, admin
             typecheck/lint/component coverage, and responsive accessible
             Playwright journeys cover the local runtime; see
             evidence/v1-workbenches/metis-source-blast-radius.json. W3C PROV-O,
             DataCite metadata, and Crossref Crossmark inform the provenance and
             lifecycle model. Production publisher/webhook integrations, durable
             replicated storage, alert delivery, operator staffing, and
             deployment validation remain explicit gates. -->
- [x] M2.10 Implement curriculum brief capture with audience, prerequisites,
      outcomes, constraints, standards, duration, modality, locale, and
      assessment strategy. Evidence: required-field gate.
  - [x] M2.10.a Define versioned brief identity/scope/owner/course/program,
        intended learners/grade/age/context, enrollment scale, and source refs.
        Evidence: brief schema.
  - [x] M2.10.b Capture prerequisite knowledge/skills/evidence and target
        learning outcomes/objectives with measurable criteria. Evidence:
        prerequisite/outcome tests.
  - [x] M2.10.c Capture standards/framework/version/coverage,
        institution/region/
        safety/accessibility/rights/technology/budget/schedule constraints.
        Evidence: constraint tests.
  - [x] M2.10.d Capture total/calendar/seat/self-study duration, synchronous/
        asynchronous/in-person/online/blended modality, locale/language, device/
        bandwidth/offline requirements. Evidence: delivery tests.
  - [x] M2.10.e Capture formative/summative/diagnostic/authentic assessment
        strategy, weighting/frequency/security/accommodation/feedback
        constraints. Evidence: assessment strategy tests.
  - [x] M2.10.f Validate required/conditional fields, conflicts, units,
        compatibility, code/institution floors, and reviewer acceptance before
        planning. Evidence: required-field gate.
        <!-- Implemented in the curriculum-brief 2.10.0 contracts and service,
             with exact course/program/owner and source-version identities,
             append-only self-hashed revisions and reviews, deterministic
             validation evidence, a code floor, monotonic tightening institution
             floors, idempotent author/reviewer mutations, tenant authorization,
             and tamper/chain read-back checks. Draft revisions may retain
             policy-floor findings, while submission, administrator acceptance,
             and source-outline planning fail closed on required/conditional
             fields, conflicts, units, modality compatibility, current policy,
             exact current revision hashes, exact validation hashes, and source
             package binding. The instructor/admin dossier exposes four
             progressively disclosed stages, a live institution-aware readiness
             ledger, revision register, and exact reviewer hash read-back.
             Python service/API/OpenAPI tests, generated-client drift checks, web
             validation tests, production build verification, and responsive
             axe-backed Playwright journeys across Chromium, Firefox, and WebKit
             cover the local runtime; see
             evidence/v1-workbenches/metis-curriculum-brief.json. 1EdTech CASE
             1.1, WCAG 2.2, RFC 5646/BCP 47, and CEDS inform the standards,
             accessibility, locale, and education-domain boundaries. Production
             identity federation, durable replicated storage, institution policy
             ratification, reviewer staffing, backup/restore, monitoring, and
             deployment validation remain explicit operational gates. -->
- [x] M2.11 Implement objective and concept-map authoring. Evidence: cycle,
      orphan, unreachable objective, and prerequisite checks.
  - [x] M2.11.a Build typed objective create/edit/version/link UI/API with
        taxonomy, criteria, standards, evidence, ownership, and validation.
        Evidence: objective authoring tests.
  - [x] M2.11.b Define concept node identity/type/version/source/locale and
        prerequisite/part-of/example/contrast/causal/related relationship kinds.
        Evidence: concept graph schema.
  - [x] M2.11.c Build accessible graph plus synchronized tree/list/table
        operations for add/link/unlink/reorder/group/annotate/import. Evidence:
        semantic UI tests.
  - [x] M2.11.d Detect cycles where forbidden, self/duplicate/conflicting edges,
        orphan objectives/concepts, missing roots, and unreachable outcomes.
        Evidence: graph validation fixtures.
  - [x] M2.11.e Validate prerequisite ordering against modules/lessons/
        assessments and surface remediation without automatic destructive edits.
        Evidence: sequencing tests.
        <!-- Implemented in the curriculum objective-map 2.11.0 contracts,
             service, API, and instructor workbench. Objective and concept
             identities carry typed ownership, locale, taxonomy, criteria,
             standards, evidence, source, and embedded immutable revision
             chains inside append-only self-hashed map snapshots bound to one
             exact accepted curriculum-brief revision. Deterministic validation
             fails closed for validated maps and planning readiness while draft
             revisions may retain explicit cycle, self/duplicate/conflicting
             edge, orphan, missing-root, unreachable, parent-structure,
             duplicate-position, dangling-placement, and prerequisite-order
             findings with human-readable remediation and no automatic graph or
             sequence rewrite. The warm-paper workbench synchronizes one
             selection across a visual SVG graph, keyboard tree, semantic list,
             and native table, and exposes reversible add/edit/link/unlink/
             reorder/group/annotate/import operations plus exact validation and
             revision hashes. Python service/API/OpenAPI tests, generated-client
             drift checks, web contract tests, production build verification,
             and responsive axe-backed Playwright journeys across Chromium,
             Firefox, and WebKit cover the local runtime; see
             evidence/v1-workbenches/metis-curriculum-map.json. 1EdTech CASE
             1.1, W3C SKOS, and WAI-ARIA APG tree and keyboard patterns inform
             the standards and interaction boundaries; this is runtime evidence,
             not CASE/SKOS conformance or assistive-technology certification.
             Production identity federation, replicated durable storage and
             backup, institution policy ratification, representative graph-scale
             performance and assistive-technology testing, monitoring, and
             deployment validation remain explicit operational gates. -->
- [x] M2.12 Implement standards-alignment suggestions behind the provider seam.
      Evidence: human acceptance and source/version attribution.
  - [x] M2.12.a Define alignment proposal for exact objective/content/item and
        standard framework/version/code, strength/coverage, rationale, source
        locators, confidence, and provider provenance. Evidence: schema tests.
  - [x] M2.12.b Retrieve only authorized current standard text/metadata and send
        bounded inputs through the fenced provider with injection resistance.
        Evidence: provider input tests.
  - [x] M2.12.c Validate proposed codes/versions/locators against the canonical
        standards registry and deduplicate/conflict-check. Evidence: validation
        tests.
  - [x] M2.12.d Present source text/diff/confidence/limitations and require
        human accept/edit/reject with reviewer authority. Evidence: review
        journey.
  - [x] M2.12.e Persist accepted alignment separately from proposal and
        invalidate/review on objective or standard revision change. Evidence:
        lineage tests.
        <!-- Implemented in the curriculum standards-alignment 2.12.0
             contracts, canonical registry, fenced provider seam, append-only
             service, API, generated client, and instructor/admin review
             workbench. Exact objective/content/item snapshots and canonical
             records retain resource/revision/content/text hashes,
             framework/version/code/locator, strength, coverage, rationale,
             confidence, limitations, and provider/prompt/config provenance.
             Only current, explicitly authorized records can cross the
             zero-tool trust boundary; target, registry, and reviewer inputs
             are bounded, data-only fenced, and rejected rather than truncated,
             while every proposed identity is validated against the selected
             canonical set and checked for duplicates and conflicts. Named
             administrators must inspect exact source and target evidence and
             accept, edit, or reject with hash readback; accepted alignments are
             immutable records separate from model proposals and decisions,
             with fail-closed review-required lineage when objectives disappear
             or target/standard revision, content, lifecycle, or authorization
             changes. Python service/API/OpenAPI tests, generated-client drift
             checks, web contract tests, production build verification, and
             responsive axe-backed Playwright journeys across Chromium,
             Firefox, and WebKit cover the local runtime; see
             evidence/v1-workbenches/metis-standards-alignment.json. 1EdTech
             CASE 1.1, OWASP LLM01:2025, NIST AI RMF, and WAI-ARIA APG tabs and
             keyboard practices inform the contract, trust, authority, and
             interaction boundaries; this is runtime evidence, not external
             standards conformance, security certification, or deployment
             validation. Seeded records are repository-authored normalized
             metadata abstracts versioned by the exact Metis catalog; they do
             not reproduce full upstream standards or claim an upstream
             framework edition, which reviewers must verify at the linked
             authoritative source. Production provider credentials and
             allowlisting, institution-owned registry ingestion/versioning and
             rights review, federated identity and reviewer staffing,
             replicated durable storage and backup, monitoring, representative
             accessibility testing, and deployment validation remain explicit
             operational gates. -->
- [x] M2.13 Implement course-outline divergence. Evidence: N variants preserve
      generated structure and score provenance.
  - [x] M2.13.a Define outline variant/set contracts with stable module/lesson/
        objective structure IDs, source brief/revisions, generation plan, and
        provider provenance. Evidence: outline schemas.
  - [x] M2.13.b Generate N bounded variants with explicit diversity strategy,
        seeds/controls, budget/cost confirmation, and unique identities.
        Evidence: divergence tests.
  - [x] M2.13.c Validate every variant for hierarchy, objective/prerequisite/
        standards/coverage/workload constraints before comparison. Evidence:
        outline validation tests.
  - [x] M2.13.d Record provider/evaluator/human scores with rubric/version/
        uncertainty separately; manual variants show absent generated scores.
        Evidence: score provenance tests.
  - [x] M2.13.e Compare/select/reject/merge/fork variants through decision
        records while retaining all original structures. Evidence: decision
        journey.
        <!-- Implemented in the course-outline divergence 2.13.0 contracts,
             zero-tool provider seam, deterministic validator, append-only
             service, API, generated client, and instructor workbench. Every
             set binds one exact planning-ready curriculum-brief revision,
             accepted brief review, objective-map revision, validation hashes,
             source objective/prerequisite/standard identities, generation
             plan, and confirmed cost ceiling. Requests require two through six
             candidates with explicit diversity strategies, unique seeds,
             workload and structure bounds, input/output limits, and pricing
             provenance; provider results must match the exact plan and count,
             remain inside the confirmed cost envelope, and retain prompt,
             model, config, trust-label, token, outcome, and response hashes.
             Source, plan, and reviewer guidance cross a bounded data-only
             prompt fence with no tool access and fail closed on oversize,
             refusal, malformed output, count/identity drift, or storage
             tampering. Stable variant/module/lesson/objective structure IDs
             preserve each generated shape. Deterministic validation records
             hierarchy and sequence limits, lesson and objective prerequisite
             ordering, exact objective and standards references, required
             objective/standard/assessment coverage, and workload bounds before
             comparison, selection, merge, or fork. Provider, evaluator, and
             named human scores are separate immutable records with distinct
             lineage, rubric/version, dimensions, rationale, and uncertainty;
             manual and derived structures inherit no generated scores.
             Optimistic content, validation, and workspace hashes bind
             select/reject/merge/fork decisions while all originals and exact
             source contributions remain readable. The warm-paper blueprint
             workbench exposes the complete decision journey through a semantic
             captioned comparison table in its own labelled scroll region,
             roving keyboard tabs, narrow document reflow, and reduced motion.
             Python service/API/OpenAPI tests, generated-client drift checks,
             web contract tests, production build verification, and responsive
             axe-backed Playwright journeys across Chromium, Firefox, and
             WebKit cover the local runtime; see
             evidence/v1-workbenches/metis-course-outline.json. 1EdTech Common
             Cartridge, NIST AI RMF, OWASP LLM01:2025, and W3C table/reflow
             guidance inform the identity, measurement, trust, and interaction
             boundaries; this is runtime evidence, not Common Cartridge or
             WCAG conformance, security certification, or deployment
             validation. Production provider credentials and allowlisting,
             institution policy and rubric governance, federated identity and
             reviewer staffing, replicated durable storage and backup,
             monitoring, representative accessibility and scale testing, and
             deployment validation remain explicit operational gates. -->
- [x] M2.14 Implement workload and seat-time estimation. Evidence: deterministic
      calculation with modality-specific assumptions.
  - [x] M2.14.a Define versioned effort model for reading/listening/viewing,
        instruction, discussion, practice, assessment, project/lab/simulation,
        feedback, and navigation with units/assumptions. Evidence: model schema.
  - [x] M2.14.b Derive content/activity quantities from exact outline/course
        revision and apply modality, difficulty, learner-band, language,
        accessibility, and synchronous/self-paced factors. Evidence: calculation
        fixtures.
  - [x] M2.14.c Calculate learner effort, instructor/facilitator workload, seat
        time, independent time, calendar duration, range/uncertainty, and
        bottlenecks separately. Evidence: estimate tests.
  - [x] M2.14.d Expose per-component
        inputs/formulas/assumptions/overrides/source and never hide unknown work
        as zero. Evidence: explainability tests.
  - [x] M2.14.e Compare estimate to brief/institution constraints and require
        authorized reasoned overrides without changing raw calculations.
        Evidence: constraint tests.
<!-- M2.14 evidence: schema version 2.14.0 and effort-model version 2026.08.1
     define 18 modality/unit rates across nine component families, each with an
     explicit unit, lower/expected/upper rate, scaling rule, source references,
     and assumptions. Requests bind an immutable accepted-brief revision plus
     either exact outline identity/content hashes or a captured, self-hashed
     course revision whose text witnesses preserve derived word counts; media
     durations and other quantities not present in the source remain unknown,
     never inferred from URLs or converted to zero. Decimal interval arithmetic
     applies explicit difficulty, learner-band, language, accessibility, and
     delivery factors. Accessibility factors describe only documented
     accommodation, format, and support-service work rather than treating
     disability as learner slowness. Results separate learner effort,
     enrollment-aware instructor/facilitator work, seat time, independent time,
     calendar duration, uncertainty ranges, and bottlenecks; incomplete totals
     are labelled known subtotals with unknown component counts. The warm-paper
     engineering ledger exposes raw and adjusted component formulas, quantities,
     rates, factors, assumptions, sources, constraints, and immutable authorized
     reasoned overrides without mutating raw arithmetic. Tenant and role gates,
     idempotency, artifact tamper checks, course-revision capture, OpenAPI/client
     contracts, focused and full Python service tests, web unit tests, lint,
     typechecking, production build, and axe-backed responsive Playwright
     journeys across Chromium, Firefox, and WebKit cover the local runtime; see
     evidence/v1-workbenches/metis-workload-estimation.json. 34 CFR 600.2, the
     Rice Course Workload Estimator, NIST TN 1297, and W3C reflow guidance inform
     the credit-hour vocabulary, transparent assumptions, range reporting, and
     interaction design. This is inspectable planning evidence, not a
     credit-hour or clock-hour determination, statistical confidence interval,
     labor rule, accessibility/WCAG conformance claim, or deployment validation.
     Institution-approved rates and policy rules, production identity and
     storage, representative learner validation, governance, monitoring, and
     deployment testing remain explicit operational gates. -->
- [x] M2.15 Implement objective-to-content-to-assessment coverage matrices.
      Evidence: undercoverage and overassessment gates.
  - [x] M2.15.a Build exact revision links from objectives to modules/lessons/
        blocks/activities/practice/items/assessments/rubrics and evidence.
        Evidence: coverage graph tests.
  - [x] M2.15.b Define instructional/introduced/practiced/reinforced/assessed/
        mastered coverage roles, depth/cognitive level, weight, and required
        thresholds. Evidence: coverage vocabulary.
  - [x] M2.15.c Compute accessible objective-by-content-by-assessment matrices
        with direct/indirect, missing, stale, unsupported, and duplicated links.
        Evidence: matrix fixtures.
  - [x] M2.15.d Detect required objective undercoverage, content without
        objective, assessment without instruction, excessive weight/frequency,
        level mismatch, and overassessment. Evidence: gate tests.
  - [x] M2.15.e Present raw links/measures and remediation separately from
        policy verdict; recompute on revision/threshold changes. Evidence:
        UI/API tests.
<!-- M2.15 evidence: schema version 2.15.0 and coverage-policy version
     2026.08.1 bind immutable curriculum-map and outline revisions plus their
     hashes to self-hashed objective, module, lesson, block, activity, practice,
     item, assessment, rubric, evidence, and relationship snapshots. Each link
     preserves exact endpoint revisions/hashes, direct or indirect relation,
     one of six versioned instructional/introduced/practiced/reinforced/
     assessed/mastered roles, cognitive level, weight, frequency, rationale,
     and evidence revisions. Deterministic raw measurement retains every link
     and the complete objective-by-resource Cartesian matrix, including empty,
     stale, unsupported, and duplicated relationships; policy credit excludes
     stale/unsupported evidence and deduplicates repeated objective/resource/
     role links without hiding the raw records. Institution-sourced effective
     thresholds drive explicit source-traced blocking and warning findings for
     required-objective undercoverage, content without objectives, assessment
     without instruction, excessive weight or frequency, cognitive-level
     mismatch, overassessment, stale links, unsupported links, and duplicates.
     Source facts, raw measures, policy verdicts, and non-automatic remediation
     suggestions are separate immutable layers. Exact source or governed
     threshold changes create a linear, self-hashed recomputation successor;
     no-op, stale-workspace, divergent-successor, tenant, role, idempotency, and
     tamper checks fail closed. The warm-paper audit workbench splits content,
     practice, and assessment into semantic captioned matrices with scoped
     headers and labelled internal scroll regions, supplies a linear objective
     register and raw-link inspector, preserves raw/policy selection, reflows at
     390 px, and honors reduced motion. Ruff, Mypy, 644 Python service tests
     (10 environment-dependent skips), OpenAPI and generated-client drift
     checks, 113 web unit tests, lint, typecheck, production build, 10 evidence
     tests, and axe-backed Playwright journeys across Chromium, Firefox, and
     WebKit cover the local runtime; see
     evidence/v1-workbenches/metis-curriculum-coverage.json. 1EdTech CASE, CAST
     UDL assessment guidance, and W3C table/reflow guidance inform identity,
     planning, and interaction boundaries. This is inspectable runtime planning
     evidence, not CASE certification or conformance, proof of instructional or
     assessment validity or learner mastery, accreditation determination,
     accessibility/WCAG conformance, or deployment validation. Institution
     vocabulary and threshold governance, expert curriculum review,
     representative-user evaluation, production identity and durable storage,
     backup, monitoring, security, scale, and deployment evidence remain
     explicit operational gates. -->
- [x] M2.16 Implement curriculum decision records. Evidence: selected, rejected,
      and superseded outlines remain traceable.
  - [x] M2.16.a Define immutable decision identity, actor/authority, compared
        outline revisions, selected/rejected/deferred/merged disposition,
        rationale, evidence, conditions, and timestamp. Evidence: decision
        schema.
  - [x] M2.16.b Bind selection to exact outline/brief/objective/standards/
        workload/coverage versions and retain all alternatives. Evidence:
        selection tests.
  - [x] M2.16.c Record rejection/defer reasons and requested changes without
        deleting or modifying the outline. Evidence: negative decision tests.
  - [x] M2.16.d Record merge/fork as a new outline revision with explicit source
        variant contributions and decisions. Evidence: merge lineage tests.
  - [x] M2.16.e Supersede through a new decision linked to the prior decision
        and preserve current/historical projection. Evidence: traceability
        tests.
<!-- M2.16 evidence (2026-08-29): Metis now persists tenant-scoped, immutable,
     self-hashed `2.16.0` curriculum decision records with stable identity,
     named actor and role, explicit authority scope and basis, all compared
     outline revision witnesses, selected/rejected/deferred/merged/forked
     disposition, rationale, exact review evidence, conditions, negative reason
     codes and requested changes, server timestamp, and request and record
     digests. Selection fails closed unless the chosen subject has current exact
     workload and coverage workspaces whose outline, brief, objective-map,
     content, source, model/policy, calculation, and verdict hashes agree; the
     accepted brief revision supplies exact versioned standards while every
     alternative remains embedded in the record. Rejection and deferral write
     only the decision ledger and retain byte-equivalent outline records. Merge
     and fork delegate to the M2.13 outline revision engine, then bind its
     immutable structural decision, created revision, parent structures, and
     explicit source contributions. A new decision must name the exact current
     predecessor; current and historical status is derived from one linear
     successor chain, and missing, stale, cyclic, divergent, cross-tenant,
     unauthorized, idempotency-reused, or tampered evidence fails closed.

     The warm-paper decision workbench at `/create/decisions` exposes a
     captioned semantic comparison register, exact workload/coverage readiness,
     selected-row evidence inspector, signed authority and evidence form,
     conditional rejection/defer requests, merge/fork revision controls, and a
     linear current/historical authority timeline. The comparison is the only
     labelled two-dimensional scroll region, the rest reflows at 390 px,
     reduced-motion preferences are honored, and shared axe automation runs on
     every browser journey. Runtime OpenAPI and the generated TypeScript client
     include list, detail, and decision-write contracts. Automated service,
     web, contract, build, evidence, and cross-browser verification is recorded
     in `evidence/v1-workbenches/metis-curriculum-decisions.json`.

     W3C PROV-O entity/activity/agent, attribution, revision, and derivation
     concepts; NIST AI RMF human governance and documentation boundaries; and
     W3C table and reflow guidance informed the provenance and interaction
     design. This is inspectable local runtime evidence, not PROV-O or NIST
     framework conformance, instructional validity, accreditation or governance
     determination, WCAG conformance, or production deployment validation.
     Institution authority policy, expert curriculum review, representative-user
     evaluation, production identity and durable transactional storage, backup,
     monitoring, scale, security, and deployment evidence remain explicit
     operational gates. -->
- [x] M2.17 Build curriculum discovery across existing courses, open resources,
      standards, concept graphs, prerequisites, credentials, and institution
      catalogs with rights, authority, version, locale, and reuse diagnostics.
      Evidence: discovery never silently imports or republishes content.
  - [x] M2.17.a Define discovery index/result/source/version contracts and
        connectors for each named catalog type with provenance/freshness.
        Evidence: discovery schemas.
  - [x] M2.17.b Ingest only authorized metadata/excerpts/fingerprints with
        rights, authority, locale, standards/objectives/concepts/prerequisites/
        credential relations. Evidence: connector tests.
  - [x] M2.17.c Implement permission-aware search/filter/facet/rank/compare with
        exact source, version, match rationale, currency, and no protected
        answer/learner leakage. Evidence: query tests.
  - [x] M2.17.d Diagnose reuse compatibility for license, audience, locale,
        standards, prerequisites, level, accessibility, target, source quality,
        and update risk. Evidence: diagnostic fixtures.
  - [x] M2.17.e Implement preview/bookmark/request-import/link-as-reference as
        explicit actions; require review/rights/validation before canonical
        authoring use. Evidence: no-silent-import tests.
<!-- M2.17 evidence (2026-08-29): Metis now exposes an immutable, self-hashed
     `2.17.0` curriculum-discovery boundary over exactly seven connector
     profiles: existing courses, authorized open resources, CASE-shaped
     standards frameworks, governed concept graphs, prerequisite maps,
     credential catalogs, and institution registrar catalogs. Every snapshot
     retains connector profile and version, authority basis, catalog version,
     manifest URI and digest, retrieval and index time, tenant owner, exact
     external source/version URIs, fingerprint, rights evidence, locale,
     accessibility metadata, quality, visibility, and typed standard,
     objective, concept, prerequisite, and credential relations. Input
     contracts require explicit metadata and fingerprint authorization, allow
     excerpts only with preview authorization, structurally prohibit protected
     answers and learner data, reject contradictory or uncleared copy/adapt
     grants, require catalog-appropriate authority, and preserve every
     historical snapshot while projecting only the latest per connector owner.

     Search filters visibility before query matching, facets, ranking, or
     comparison so hidden tenant records do not affect counts or evidence.
     Results bind the exact index record to source version, retrieval currency,
     rank components and rationale, ten fixed reuse checks (license, audience,
     locale, standards, prerequisites, level, accessibility, target, source
     quality, and update risk), and deterministic result and diagnostic hashes.
     Invisible comparisons, stale item or diagnostic hashes, unauthorized
     public snapshots, idempotency divergence, lineage divergence, and disk
     tampering fail closed. Preview, bookmark, import request, and reference
     link are separate immutable actions. Import approval requires a different
     administrator, copy/adapt rights, a non-blocked current diagnosis, and
     rights and validation hashes matching the exact current evidence; even an
     approved review records `canonical_write_performed=false`, copies no
     content, and creates no canonical artifact.

     The frontend-skill-guided warm-paper finding aid at `/create/discovery`
     uses a restrained ranked register and synchronized exact-source inspector,
     exposes visible facets, authority, rights, version, fingerprint, currency,
     match and typed-relation evidence, reveals a semantic ten-row comparison
     only after selecting two to four sources, and makes the four action verbs
     explicit alongside their human/rights/validation gate states. Content
     reflows without page overflow at 390 px; only the labelled two-dimensional
     comparison scrolls internally, reduced-motion preferences remove entrance
     motion, and the shared axe fixture runs after every browser journey.
     Runtime OpenAPI and the generated TypeScript client include connector,
     snapshot, search, exact-item, action, action-ledger, and independent-review
     contracts. Focused Ruff/Mypy/service/API/contract/release-gate tests, web
     lint/type/unit checks, deterministic evidence tests, OpenAPI/client drift
     checks, and 12 axe-backed journeys across Chromium, Firefox, and WebKit
     cover the affected local runtime; see
     `evidence/v1-workbenches/metis-curriculum-discovery.json`.

     1EdTech CASE and LTI Resource Search metadata, Schema.org
     LearningResource, W3C ODRL 2.2, Credential Engine CTDL, and Creative
     Commons license guidance informed identity, relation, rights, and action
     boundaries. This is inspectable local runtime evidence, not standards or
     accessibility conformance, a legal or license determination, proof of
     instructional validity, accreditation, or production deployment
     validation. Institution connector credentials and source-specific terms,
     legal and expert curriculum review, representative-user evaluation,
     production identity and durable transactional storage, backup, monitoring,
     scale, security, and deployment evidence remain explicit operational
     gates. -->
- [x] M2.18 Build discipline-aware planning templates and verification
      requirements for proofs/worked solutions, lab or field evidence, data
      analysis, source interpretation, language performance, and executable
      computing artifacts. Evidence: specialized requirements remain typed and
      survive standards export where supported.
  - [x] M2.18.a Define versioned discipline template with applicability,
        specialized typed blocks, verification/gate/reviewer/accessibility/
        standards-export requirements, inheritance, and code floors. Evidence:
        template schema.
  - [x] M2.18.b Define proof/worked-solution premises/steps/justifications/
        notation/equivalence/checker/manual review and semantic math
        alternatives. Evidence: mathematics templates.
  - [x] M2.18.c Define lab/field question/method/materials/safety/ethics/
        observations/data/uncertainty/analysis/reproducibility evidence.
        Evidence: lab templates.
  - [x] M2.18.d Define dataset/source/version/variables/units/cleaning/method/
        code/results/visualization/uncertainty/reproducibility for data
        analysis. Evidence: data templates.
  - [x] M2.18.e Define primary/secondary source context/authority/perspective/
        claim/citation/corroboration/counterevidence for interpretation.
        Evidence: source interpretation templates.
  - [x] M2.18.f Define language/oral performance prompt/context/language/skills/
        recording/consent/rubric/assessor/accessibility evidence. Evidence:
        performance templates.
  - [x] M2.18.g Define
        code/runtime/dependencies/input/output/tests/seed/resource/
        network/security/accessibility/reproducibility for computing artifacts.
        Evidence: computing templates.
  - [x] M2.18.h Map each typed field to applicable QTI/course/standards export
        or explicit extension/loss/incompatibility and round-trip it. Evidence:
        export survival tests.
<!-- M2.18 evidence (2026-08-29): Metis now exposes a strict, immutable
     `2.18.0` discipline-planning registry with one self-hashed abstract code
     floor and six self-hashed concrete templates. Applicability pins subject,
     artifact kind, and depth tier. Every concrete template inherits the same
     reviewer-count, reviewer-role, typed-completeness, human-review,
     accessibility-alternative, and export-survival requirements; catalog
     construction rejects any weakened floor, incomplete field mapping, stale
     template hash, mismatched artifact kind, or inapplicable subject.

     The discriminated artifact union gives proof/worked solutions typed
     premises, steps, justifications, notation, equivalence claims, checker
     evidence, manual review, and presentation/content MathML plus speech,
     braille, and plain-text alternatives. Lab and field plans type question,
     method, materials and hazards, safety, ethics, observations, data,
     uncertainty, analysis, and reproducibility. Data plans type the exact
     dataset source/version, variables/types/units/roles, cleaning, method,
     pinned code, results, accessible visualization, uncertainty, and
     reproducibility. Interpretation plans keep primary and secondary source
     kinds separate while typing context, citation, authority, perspective,
     claim, corroboration, and counterevidence. Language plans type prompt,
     context, locale, skills, recording digest and transcript/captions,
     consent/retention, rubric, calibrated assessor, moderation, and non-audio
     alternatives. Computing plans type source, runtime/container,
     dependencies/integrity, input/output contracts, tests, deterministic seed,
     resource limits, fail-closed network policy, sandbox and scans, accessible
     errors/output, and reproducibility evidence.

     Every leaf in every specialized block has exactly one QTI, course, and
     standards disposition. Metis course JSON carries the typed plan natively.
     QTI 3 packages retain it through a declared JSON sidecar because QTI
     natively describes assessment interactions, while CASE 1.1 retains it in
     a declared extension because CASE natively describes competencies and
     rubrics. Preserved-extension exports deserialize to the identical plan
     hash across all three channels; native-only exports preserve course JSON
     and report every removed QTI/CASE extension leaf as explicit loss. No
     unsupported artifact evidence is mislabeled as a native standards field.

     The frontend-skill-guided `/create/disciplines` workbench uses a restrained
     six-template rail, typed scalar and structured evidence editor, exact
     template pin ledger, non-weakening gate inspector, extension-policy
     control, and per-field export-survival table. Structured JSON errors are
     stable across engines. The page reflows without document overflow at
     390 px; the labelled field table alone scrolls horizontally and is
     keyboard-focusable; reduced-motion preferences remove entrance motion;
     and the shared axe fixture runs after every browser journey. Runtime
     OpenAPI and the generated TypeScript client include the catalog,
     validation, export, six-way discriminator, and field-disposition
     contracts. Focused Ruff/Mypy/service/API/OpenAPI/release-gate tests, web
     lint/type/unit checks, deterministic evidence tests, OpenAPI/client drift
     checks, and 12 axe-backed journeys across Chromium, Firefox, and WebKit
     cover the affected local runtime; see
     `evidence/v1-workbenches/metis-discipline-templates.json`.

     Official 1EdTech QTI 3 and CASE 1.1, W3C MathML 4, and WCAG 2.2 informed
     the local mapping and accessibility decisions. This is inspectable local
     runtime evidence, not QTI, CASE, MathML, WCAG, instructional,
     accreditation, or production conformance. Expert discipline,
     accessibility, safety, ethics, assessment, legal, representative-user,
     production identity, transactional persistence, backup, monitoring,
     scale, security, and deployment review remain explicit operational
     gates. -->

### M3 — Lesson, activity, media, and rubric authoring

- [x] M3.1 Build course and module boards with guarded maturity transitions.
      Evidence: server-enforced move tests.
  - [x] M3.1.a Define board columns/grouping/order/filter view from canonical
        course/module revision and maturity state. Evidence: board view tests.
  - [x] M3.1.b Implement accessible create/open/reorder/group operations with
        keyboard/non-drag alternatives and optimistic concurrency. Evidence:
        board interaction tests.
  - [x] M3.1.c Resolve allowed maturity transitions, roles, prerequisites,
        blockers, review/approval, reason, and version effect server-side.
        Evidence: transition matrix tests.
  - [x] M3.1.d Reject crafted moves, stale revisions, missing gates,
        unauthorized roles, and cross-tenant IDs; preserve immutable transition
        history. Evidence: server-enforced negative tests.
<!-- M3.1 evidence (2026-08-29): Metis now projects course and module boards
     from the existing SQL Course/Module hierarchy. Alembic 003 adds canonical
     maturity, revision, group, and order metadata plus an append-only maturity
     transition ledger; a clean temporary Postgres rehearsal passed empty-to-
     head upgrade, 003-to-002 downgrade, and 002-to-003 re-upgrade. Alembic's
     synchronous URL now selects the declared psycopg driver explicitly.

     Every board card carries its exact entity revision and content witness.
     The board witness is calculated over the complete unfiltered canonical
     entity set, so grouping, maturity filtering, and search cannot weaken
     create or reorder concurrency checks. Course and module create, open,
     exact-set reorder, and group moves use expected board hashes and/or entity
     revisions. Earlier/Later buttons are the primary reorder control, making
     pointer dragging unnecessary; filtered views disable reorder rather than
     submitting an incomplete set.

     A server-owned eleven-edge matrix governs Idea, Planned, Authoring, In
     review, Approved, Published, and Archived. Clients submit only entity ID,
     expected revision, target, and a bounded reason. The authenticated server
     derives role eligibility, canonical content prerequisites, review state,
     author-approval conflict, parent/publication gates, blockers, review
     effect, and version effect. Row locks and optimistic witnesses reject
     stale, crafted, missing-gate, unauthorized, duplicate/incomplete reorder,
     and cross-tenant requests. Accepted moves append actor, reason, complete
     resolved evidence, previous-event hash, and event hash; history reads
     recompute and report chain validity. There is no update/delete history API.

     The frontend-skill-guided `/create/boards` workbench uses a restrained
     board-first register, accessible forms, explicit open/reorder/group
     controls, a gate-and-version-effect inspector, stale-write reload, and an
     immutable-history view. At 390 px the document remains contained while
     the labelled board region alone scrolls horizontally; reduced-motion
     preferences remove entrance animation. Focused Ruff/Mypy/service/API/
     OpenAPI/persistence/release-gate checks, generated-client drift checks,
     web lint/type/unit checks, deterministic evidence tests, and 12 axe-backed
     Playwright journeys across Chromium, Firefox, and WebKit cover the affected
     local runtime. See
     `evidence/v1-workbenches/metis-course-module-boards.json`.

     This is inspectable local runtime evidence, not proof of production
     migration, deployment, scale, availability, backup/recovery, monitoring,
     identity-provider or institutional role governance, representative-user
     usability, accessibility conformance, instructional validity, legal
     review, or accreditation. Those remain explicit operational gates. -->
- [x] M3.2 Build the lesson dossier with objectives, prerequisites, sources,
      content, activities, assessments, assets, gates, review, and history.
      Evidence: live-data browser journey.
  - [x] M3.2.a Define one typed lesson dossier projection joining exact lesson/
        course revision and all named relationships with freshness/redaction.
        Evidence: dossier contract.
  - [x] M3.2.b Present objectives/prerequisites/coverage and source/citation/
        authority/currency/rights with conflicts and missing requirements.
        Evidence: curriculum/source panels.
  - [x] M3.2.c Present ordered content/activities/assessments/rubrics/assets/
        accessibility variants and dependency state. Evidence: content panels.
  - [x] M3.2.d Present quality/safety/accessibility/correctness gates, comments/
        reviews/approvals, maturity, publication readiness, and remediation.
        Evidence: gate/review panels.
  - [x] M3.2.e Present revision/diff/decision/audit/published-build/use history
        with stable deep links and no answer-key exposure. Evidence: history
        tests.
  - [x] M3.2.f Verify live edit-review-preview journey, direct property auth,
        responsive accessibility, and large-dossier performance. Evidence:
        browser E2E.
<!-- M3.2 evidence (2026-08-29): Metis now joins the canonical SQL Lesson,
     Module, Course, assessment metadata, and an append-only dossier ledger into
     one typed `3.2.0` projection. Every projection and mutation carries exact
     lesson, module, course, dossier revision, tenant, observation-time, and
     dossier-hash witnesses. Legacy course/module/lesson mutations advance the
     same hierarchy revisions, so older endpoints cannot silently bypass
     dossier concurrency. Alembic 004 adds lesson maturity/revision plus
     hash-chained dossier revision and event tables; an isolated temporary
     Postgres rehearsal passed empty-to-head upgrade, 004-to-003 downgrade, and
     003-to-004 re-upgrade with two columns, two tables, and seventeen indexes
     verified at the boundary before the temporary database was removed.

     The strict dossier contract binds stable, unique objectives,
     prerequisites, governed source versions/citations, ordered content,
     activities, assessment links, rubrics, assets, and accessibility variants.
     It rejects broken references, non-contiguous order, and protected source
     excerpts at the persistence boundary. The server derives dependency state,
     coverage/source/rights/accessibility/assessment conflicts, missing
     requirements, relationship freshness, explicit redactions, and quality,
     safety, accessibility, and correctness gates. Assessment projections carry
     only identity, metadata, question count, objective/rubric links, and
     dependency state: questions, options, explanations, answer keys, and
     correct answers are absent. Service and browser fixtures include a sentinel
     secret answer and prove it never appears in dossier or preview JSON, the
     page, or browser mutation payloads.

     Authenticated reads and writes authorize the exact course property; row
     locks plus all expected revisions and the dossier hash reject stale,
     cross-tenant, crafted, and missing-gate requests. Author self-approval is
     forbidden, approval is administrator-only and requires a review request
     for the exact current revision, and edit/review/approval transitions append
     bounded actor/reason/diff evidence. Preview, published-build, and recorded-
     use events preserve stable deep links, exact artifact hashes, and validated
     revision/event hash chains without a history update/delete API.

     The frontend-skill-guided `/create/lessons/[lessonId]` workbench is a calm,
     dossier-first evidence register rather than a dashboard mosaic. It exposes
     exact scope and counts, canonical and structured editing, curriculum/source
     conflicts, ordered learning relationships, derived gates/remediation,
     immutable review, learner-safe preview, revision diffs, decisions, audits,
     builds, uses, redactions, and a labelled freshness register. Course Studio
     links newly created lessons directly to their stable dossiers. Large
     registers progressively disclose beyond forty rows; at 390 px the document
     remains contained while only the labelled freshness table scrolls, and
     reduced-motion preferences remove transitions.

     Focused Ruff/Mypy/service/API/OpenAPI tests (including sixty-six final
     service/API contract checks), a fresh migration rehearsal,
     generated-client drift checks, web lint/type/unit checks, deterministic
     evidence tests, and 12 axe-backed Playwright journeys across Chromium,
     Firefox, and WebKit cover the affected local runtime. See
     `evidence/v1-workbenches/metis-lesson-dossiers.json`.

     This is inspectable local runtime evidence, not proof of production
     deployment, production migration, scale, availability, backup/recovery,
     monitoring, identity-provider or institutional role governance,
     representative-user usability, accessibility conformance, instructional
     validity, legal review, or accreditation. Those remain explicit
     operational gates. -->
- [x] M3.3 Build a structured lesson block editor for prose, callout, example,
      equation, code, image, audio, video, interaction, practice, reflection,
      and assessment link. Evidence: block registry coverage.
  - [x] M3.3.a Define versioned block registry entry, shared identity/order/
        locale/source/objective/accessibility fields, per-kind schema, renderer,
        validator, and export mappings. Evidence: registry schema.
  - [x] M3.3.b Implement typed editors/previews for prose/callout/example and
        semantic equation/code blocks. Evidence: text/technical block tests.
  - [x] M3.3.c Implement governed image/audio/video attachment blocks with
        captions/transcripts/descriptions/rights/provenance. Evidence: media
        block tests.
  - [x] M3.3.d Implement sandboxed interaction, practice, reflection, and exact
        assessment/item link blocks without copying protected answers. Evidence:
        activity block tests.
  - [x] M3.3.e Implement insert/duplicate/reference/reorder/nest/convert/delete/
        restore with stable IDs, depth rules, dependency invalidation, and
        non-drag controls. Evidence: editing tests.
  - [x] M3.3.f Fail coverage for any registered block lacking editor, learner
        renderer, validation, accessibility, localization, import/export,
        preview, and test disposition. Evidence: block registry gate.
  <!-- M3.3 completion evidence (2026-08-29):

       - `metis.schemas.lesson_blocks` originated registry `3.3.0` and now
         exposes backward-compatible registry `3.5.0`, strict
         discriminated schemas for all twelve requested kinds, stable shared
         identity/order/parent/locale/source/objective/reference/origin and
         accessibility fields, per-kind editor/learner-renderer/validator
         dispositions, localization/import mappings, six export mappings,
         preview fixtures, and test dispositions. Server and browser coverage
         gates fail when any registered capability is absent.
       - Prose/callout/example/reflection editors, semantic sanitized MathML,
         and deliberately inert code previews are typed end to end. Image,
         audio, and video blocks pin exact asset/revision/rendition/hash plus
         rights evidence, provenance, captions/transcripts/descriptions, and
         accessibility alternatives. Unsafe or incomplete blocks render as
         inert unavailable placeholders rather than leaking ungoverned media.
       - Interactions use the allowlisted `isolated-v1` sandbox profile and
         declared capabilities. Practice and assessment links carry exact
         activity/assessment/item IDs only; learner renders and browser
         fixtures prove the protected-answer sentinel is absent.
       - The authenticated block-operation boundary applies named
         insert/duplicate/reference/reorder/nest/convert/edit/delete/restore
         commands against exact course/module/lesson/dossier/revision/block-hash
         witnesses. Server application preserves stable IDs, normalized sibling
         order, maximum depth three, cycle/reference rules, append-only revision
         and event history, tombstone restore, and dependency invalidation. Full
         dossier PUT cannot bypass the operation boundary.
       - The frontend-skill-informed manuscript canvas, structural rails, and
         focused inspector expose typed controls for all twelve kinds, explicit
         non-drag reorder/nesting actions, deletion history, exact learner
         preview, validation findings, and registry proof. The canvas discloses
         blocks progressively after sixty and remains keyboard-operable,
         reduced-motion aware, and contained at 390 CSS pixels.
       - Automated proof passes Ruff, Python 3.12 MyPy, 46 focused service/API/
         OpenAPI tests, OpenAPI export and generated-client drift checks,
         Metis web ESLint/typecheck/unit tests, twelve Chromium/Firefox/WebKit
         M3.3 Playwright journeys (including 180-block performance, 390-pixel
         containment, reduced motion, axe, and direct-property refusal), and
         twelve M3.2 dossier regression journeys. Deterministic traceability and
         current input checksums are published at
         `evidence/v1-workbenches/metis-lesson-blocks.json`.

       This is inspectable local contract and browser-runtime evidence, not
       proof of production deployment, production media/activity integration,
       production load or availability, backup/recovery, institutional policy
       configuration, representative-user usability, accessibility conformance,
       instructional validity, legal review, or accreditation. M3.5-M3.6 retain
       the governed gallery and hostile sandbox verification gates. -->
- [x] M3.4 Build semantic MathML and accessible code/example editing. Evidence:
      screen-reader and copy/export tests.
  - [x] M3.4.a Author validated MathML source with visual preview, semantic
        tree, labels/numbering, variable/units context, speech/braille text, and
        image fallback. Evidence: math editor tests.
  - [x] M3.4.b Preserve semantic MathML through save/diff/copy/paste/QTI/export/
        learner render without raster-only substitution. Evidence: round-trip
        tests.
  - [x] M3.4.c Author code with language/version, plain text, line semantics,
        annotations, input/output/example/test refs, and no formatting-only
        meaning. Evidence: code editor tests.
  - [x] M3.4.d Provide accessible syntax/error/result descriptions, keyboard
        operation, copy behavior, wrapping/zoom/high contrast, and screen-reader
        navigation. Evidence: accessibility tests.
  - [x] M3.4.e Sanitize markup/code, fence execution separately, and verify
        exports cannot inject scripts/styles or leak hidden answers. Evidence:
        security/export tests.
  <!-- M3.4 completion evidence (2026-08-29):

       - Technical schema `1.1.0` and the technical milestone's registry
         `3.4.0` now participate in backward-compatible registry `3.5.0` while
         retaining stored `1.0.0` compatibility and requiring complete
         nonvisual semantics for newly saved equation, code, and worked-example
         blocks. The strict MathML
         parser accepts a bounded MathML namespace/tag/attribute profile,
         rejects declarations, processing instructions, custom entities,
         foreign/active elements, unsafe attributes, structural text, more than
         2,000 nodes, depth beyond 32, and sources beyond 100 KB, then stores
         canonical namespace-qualified MathML and a typed semantic tree.
       - Equation authoring presents validated source, a React-reconstructed
         visual preview without HTML injection, keyboard-navigable semantic
         nodes, label and equation number, variable meanings and unit systems,
         speech/braille/plain alternatives, and an exact governed fallback
         image revision/rendition/digest/alt-text witness. Learner projections
         retain the MathML tree and alternatives and explicitly report
         `raster_only=false`.
       - Exact JSON-pointer diffs, append-only named edits, SHA-256 integrity
         clipboard envelopes, new-ID paste with origin lineage and renewed
         accessibility review, and learner projection preserve semantic source.
         The authenticated export boundary binds lesson/module/course/dossier,
         dossier hash, and block-document hash before serializing learner HTML,
         plain text, semantic PDF source, QTI, SCORM, or Common Cartridge. Every
         non-plain equation export embeds semantic MathML, and all eighteen
         kind-by-target cases are parsed back to verify their declared round
         trip rather than accepting raster substitution.
       - Code remains plain text with explicit language/version, exactly one
         role and accessible description per line, typed annotation ranges,
         input/output/example/test references, and independent syntax, error,
         and result descriptions. The editor exposes keyboard line navigation,
         wrapping, 80-200% zoom, and an axe-verified high-contrast mode. Worked
         examples expose ordered analysis steps, result descriptions, exact
         input/output/test references, and an ordered screen-reader preview.
       - Rendering never grants execution: code carries an immutable disabled
         fence with no sandbox, network, filesystem, credentials, or learner-
         data access, and the UI exposes no run control. XML/HTML serializers
         escape code and author text, protected answer keys are forbidden in
         clipboard/schema/API projections, stale or invalid blocks cannot
         export, and direct-property authorization is enforced before any
         technical payload is produced. Denied Clipboard API access falls back
         to explicit selection copy without an unhandled browser rejection.
       - Focused Ruff/format, Python 3.12 strict MyPy, 71 service/API/OpenAPI
         tests, OpenAPI export and generated-client drift/type checks, Metis web
         ESLint/typecheck, 141 web unit tests, and 24 axe-backed block-editor
         journeys across Chromium, Firefox, and WebKit pass. Those comprise 12
         M3.4 math/code/example/security journeys plus 12 retained M3.3 block
         journeys; 12 cross-browser M3.2 dossier journeys also pass. Generated
         traceability and current input checksums are published at
         `evidence/v1-workbenches/metis-lesson-technical.json`.

       This is inspectable local contract and browser-runtime evidence, not
       proof of production deployment, formal assistive-technology or
       representative-user validation, accessibility conformance, production
       LMS interoperability, semantic print fidelity, institutional policy
       configuration, instructional validity, legal review, or accreditation.
       Those remain explicit operational gates. -->
- [x] M3.5 Build media attachment through the governed Isis gallery. Evidence:
      source, rights, alt text, transcript, captions, and provenance gates.
  - [x] M3.5.a Query/select only curated audience-authorized gallery asset
        revisions by kind/locale/target/status with current capability state.
        Evidence: gallery selector tests.
  - [x] M3.5.b Attach exact immutable asset/output revision and approved
        derivative/rendition role without copying or rewriting Isis lineage.
        Evidence: attachment schema tests.
  - [x] M3.5.c Require source/rights/consent/provenance/quality/safety/target
        eligibility and propagate expiry/quarantine/recall. Evidence: gate
        tests.
  - [x] M3.5.d Require kind-appropriate alt text/long description/transcript/
        captions/audio description/sign-language or reviewed N/A reason.
        Evidence: accessibility gate tests.
  - [x] M3.5.e Preview through the learner renderer and verify media, metadata,
        accessibility tracks, permissions, and offline/bandwidth variants.
        Evidence: attachment E2E.
  <!-- M3.5 completion evidence (2026-08-29):

       - Isis now owns a curated lesson-gallery projection over immutable output
         records. Its BFF derives tenant, audience, and capabilities from the
         authenticated request; filters current revisions by media kind,
         locale, target, lifecycle status, and delivery capability; computes
         totals only after authorization; and discloses no generation workflow,
         model, prompt, raw evidence, or lineage internals.
       - Metis queries that projection only after direct lesson authorization.
         Attachment re-resolves the opaque selection against current Isis state
         inside the dossier row lock, pins exact asset/output revision,
         derivative/rendition role, rendition and content digests, and authority
         fingerprints, and appends the dossier asset and media block atomically.
         Generic insert, convert, edit, and whole-dossier replacement paths
         cannot fabricate or rewrite governed media or copy Isis lineage.
       - Selection and every learner render require current source, rights,
         consent, provenance, quality, safety, target, and rendition gates.
         Expiry, supersession, quarantine, recall, or lost eligibility causes
         exact re-resolution to fail and learner media plus its top-level stale
         delivery locator to be withheld. Service and authority tests cover
         concurrent state change, forged selections, digest mismatch, tenant or
         audience mismatch, and authorization-before-disclosure behavior.
       - Isis validates kind-appropriate alternative text, long descriptions,
         transcripts, captions, audio descriptions, and sign-language references
         or an explicit reviewed N/A reason with reviewer and review time. Metis
         revalidates that complete access matrix rather than trusting a partial
         projection, and stores authority references and fingerprints instead of
         copied source/rights/provenance evidence.
       - The frontend-skill-informed editorial contact sheet supports filtered
         keyboard selection, restrained governance evidence, exact attachment,
         and a read-only immutable inspector. The same learner renderer presents
         image/audio/video media, caption and accessibility tracks, rights and
         credit metadata, download permissions, and online/offline/
         low-bandwidth variants; recalled media becomes an unavailable notice.
       - Automated proof passes 60 focused Python service/API/OpenAPI/client
         tests, 38 Isis gallery tests, five focused BFF route/OpenAPI-runtime
         tests, Ruff, strict targeted MyPy, Isis and Metis-web typechecks,
         task-scoped ESLint, both OpenAPI generated-client drift checks, 145 web
         unit tests, and 33 axe-backed block-editor journeys across Chromium,
         Firefox, and WebKit. Nine of those browser journeys exercise query,
         keyboard selection, exact attachment, governance evidence, learner
         variants/tracks, recall withholding, tenant boundaries, and narrow
         layout. Deterministic traceability and current input checksums are in
         `evidence/v1-workbenches/metis-lesson-gallery.json`.

       This is inspectable local contract and browser-runtime evidence, not
       proof of production Isis curation population, deployment, representative
       catalog scale, CDN/offline delivery, institutional rights policy,
       assistive-technology or representative-user validation, accessibility
       conformance, legal review, or accreditation. Task-scoped BFF lint is
       clean; the app-wide BFF lint target remains red on 150 errors and 265
       warnings in unrelated pre-existing files. Those operational and baseline
       gates remain explicit. -->
- [x] M3.6 Build interactive-activity configuration with preview isolation.
      Evidence: untrusted embeds cannot access parent credentials or data.
  - [x] M3.6.a Define allowlisted activity/runtime version, typed parameters,
        initial/reset state, objectives, events, data needs, sizing, offline,
        accessibility alternative, and target. Evidence: config schema.
  - [x] M3.6.b Validate parameter ranges/dependencies/seed/resource/network/
        browser permissions/content security and prohibit arbitrary code/URLs.
        Evidence: validation tests.
  - [x] M3.6.c Render preview in origin/process sandbox with explicit message
        protocol, capability grants, time/memory/storage/network limits, and
        teardown. Evidence: sandbox tests.
  - [x] M3.6.d Use synthetic sample identity/context and prevent access to
        parent cookies/storage/credentials/DOM/network, real learner data, or
        answer keys. Evidence: escape tests.
  - [x] M3.6.e Capture deterministic events/results/errors for preview
        validation without recording as real attempts or grades. Evidence:
        preview tests.
  <!-- M3.6 completion evidence (2026-08-29):

       - Metis now owns a URL-free activity registry `3.6.0` with two pinned
         activity/runtime/implementation tuples. Each entry fixes typed
         integer, number, boolean, and enum definitions; exact initial/reset
         state; objectives, events, synthetic-only data needs, pinned package
         dependencies, capability grants, responsive sizing, offline posture,
         learner-web target, CSP profile, and resource ceilings. Interaction
         blocks require a complete keyboard-usable non-interactive alternative.
       - Pydantic discriminated values and server-owned validators reject wrong
         types, missing/extra IDs, duplicates, ranges, step misalignment,
         dependency violations, invalid seeds, ungranted browser capabilities,
         widened sizing or resource limits, weakened network/storage/
         credential/DOM/answer-key policy, arbitrary code, and runtime URLs.
         Block validation also blocks protected assessment-answer text copied
         into activity authoring fields.
       - Read-only preview issuance binds current lesson, module, course,
         dossier, dossier-hash, and block-document-hash witnesses. It projects a
         constant sample learner/tenant, objective aliases, and deterministic
         session/instance/contract hashes, scans the finished payload against
         canonical IDs and assessment secrets, and creates no attempt, grade,
         dossier revision, domain event, or database flush. Author-entered title
         and instruction prose remains in the parent; the iframe receives only
         server-owned runtime copy, removing that answer-key escape channel.
       - The frontend-skill-informed workbench edits only server-allowlisted
         fields and opens a static runtime on a distinct origin in an
         `allow-scripts`-only, credentialless iframe with an opaque origin. The
         sandbox route sends deny-by-default CSP and Permissions-Policy,
         credentialless embedder policy, cross-origin resource policy,
         no-referrer/no-store headers, and Origin-Agent-Cluster opt-in. Runtime
         fetch/XHR/WebSocket/EventSource/beacon/open access is disabled in
         addition to zero-network CSP and zero persistent-storage grants.
       - Host/runtime communication accepts only the explicit versioned
         initialize/reset/teardown and runtime-event protocol, exact session,
         source and opaque origin, contiguous sequence, allowed event type,
         bounded payload and event count. Timeout, invalid messages, runtime
         errors, and explicit teardown fail closed and remove the browsing
         context. Result payloads contain null score/grade/attempt fields and a
         preview-only recording scope; stable canonical payload serialization
         makes the validation ledger deterministic.
       - Automated proof passes focused Ruff/format, Python 3.12 strict targeted
         MyPy, 61 service/API/OpenAPI tests, OpenAPI export and generated-client
         drift checks, Metis web ESLint/typecheck, 149 web unit tests, nine
         deterministic evidence tests, and 36 axe-backed block-editor journeys
         across Chromium, Firefox, and WebKit. Three hostile activity journeys
         verify distinct opaque origin, exact sandbox headers, missing parent
         cookie/credentials/DOM/storage/network access, no answer-key escape,
         deterministic events, reset, no grade/attempt event, and teardown.
         The inspected full-page Chromium capture preserves the restrained
         editorial hierarchy and makes the isolation proof legible. Current
         traceability and input checksums are published at
         `evidence/v1-workbenches/metis-lesson-activities.json`.

       This is inspectable local contract and browser-runtime evidence, not
       proof of production origin routing or deployment. Opaque cross-origin
       sandboxing and Origin-Agent-Cluster establish browser isolation requests,
       while exact renderer-process placement and hard OS memory enforcement
       remain browser/deployment controlled and are not certified here. It also
       is not representative assistive-technology or learner validation,
       accessibility conformance, institutional policy approval, instructional
       validity, legal review, or accreditation. The repository's default
       Python 3.11 MyPy mode currently stops in installed NumPy stubs that use
       Python 3.12 syntax; the changed service surface is clean in the actual
       Python 3.12 environment. A production Next build was not run under the
       small-host hard memory gate; targeted typecheck, unit tests, and three-
       engine dev-runtime compilation/browser verification pass. Those
       operational and baseline gates remain explicit. -->
- [x] M3.7 Build rubric authoring with weight validation and exemplar evidence.
      Evidence: exact weighted-score fixtures.
  - [x] M3.7.a Build typed rubric/criterion/level/descriptors/points/weights/
        ordering/objective/item/evidence authoring with immutable revisions.
        Evidence: authoring tests.
  - [x] M3.7.b Validate unique criteria/levels, complete descriptors, ordered/
        nonoverlapping score ranges, weights/totals, required evidence, locale,
        and scoring method. Evidence: validation tests.
  - [x] M3.7.c Attach authorized exemplar artifact/revision, criterion/level
        mapping, annotation, rights/consent, redaction, and reviewer approval.
        Evidence: exemplar tests.
  - [x] M3.7.d Preview learner/assessor versions with protected exemplar/key
        fields separated and accessible table/nonvisual semantics. Evidence:
        role preview tests.
  - [x] M3.7.e Calculate exact scores for boundary/missing/N/A/rounding/
        moderation cases and pin delivered items to rubric revision. Evidence:
        weighted-score fixtures.
  <!-- M3.7 completion evidence (2026-08-30):

       - Metis now owns strict typed rubric, criterion, level, evidence,
         objective, assessment-item, scoring-policy, exemplar, review, preview,
         item-binding, moderation, and score contracts. Server-issued rubric
         revision IDs, sequence numbers, timestamps, digests, dossier revisions,
         and domain events make rubric history append-only; exact lesson,
         module, course, dossier, prior-rubric, and content-hash witnesses reject
         stale authoring.
       - Authoring validation requires unique contiguous criteria and levels,
         complete locale-matched descriptors, monotonically increasing points,
         contiguous ordered nonoverlapping inclusive ranges, unique evidence
         requirements, valid objective/item/assessment links, the fixed analytic
         weighted-points policy, and exactly 10,000 basis points globally and
         for every delivered item. Generic dossier replacement cannot mutate
         the typed rubric-revision ledger.
       - Exemplars resolve an exact current dossier artifact revision and digest
         and an exact criterion/level pair. Rights, consent evidence, redaction
         evidence, annotation, and independent approval fail closed. A new or
         changed approved exemplar must be submitted by the authenticated admin
         named as reviewer, and that reviewer cannot be the course author.
       - Distinct learner and assessor response models provide an ordered table
         caption, explicit row/column semantics, and nonvisual summary. Learner
         schemas structurally omit assessor notes, score keys, review identity,
         and assessor-only exemplars; the service constructs each role projection
         independently and regression tests scan the serialized learner result
         for protected values.
       - Exact Decimal scoring resolves inclusive boundary levels, verifies all
         required evidence, scores missing criteria as zero, reweights N/A
         criteria, applies independently justified moderation, rounds HALF_UP at
         the configured precision, and emits exact string-valued contributions,
         totals, a calculation digest, and the immutable delivered assessment/
         item/rubric revision binding. Known-value fixtures cover boundary,
         missing, N/A, rounding, moderation, and stale or mismatched delivery.
       - The frontend-skill-informed rubric workbench uses the existing warm
         manuscript studio language as a restrained assessor ledger: criterion
         ordering and exact weights, level ranges, evidence requirements,
         governed exemplar review, role-switched semantic tables, immutable
         witness receipts, and non-persisting score proofs remain legible at
         390px and under reduced motion. The dossier projects real safe item
         metadata for selection without answer keys or explanations.
       - Automated proof passes focused Ruff/format, Python 3.12 targeted MyPy,
         79 service/dossier/block/OpenAPI tests, OpenAPI export and generated-
         client drift checks, Metis web ESLint/typecheck, 154 web unit tests,
         deterministic evidence tests, 15 axe-backed rubric journeys, twelve
         dossier regression journeys, and 36 block-editor regression journeys
         across Chromium, Firefox, and WebKit. Current traceability and input
         checksums are published at
         `evidence/v1-workbenches/metis-lesson-rubrics.json`.

       This is inspectable local contract and browser-runtime evidence, not
       proof of production deployment, production identity or persistence
       integration, institutional rubric or moderation policy, representative
       learner/assessor usability, assistive-technology validation,
       accessibility conformance, instructional validity, legal review, or
       accreditation. The repository's default Python 3.11 MyPy mode currently
       stops in installed NumPy stubs that use Python 3.12 syntax; the changed
       service surface is clean in the actual Python 3.12 environment. A
       production Next build was not run under the small-host hard memory gate;
       targeted typecheck, unit tests, and three-engine dev-runtime compilation/
       browser verification pass. Those operational and baseline gates remain
       explicit. -->
- [x] M3.8 Build authoring-time readability and language-complexity analysis.
      Evidence: server-computed known-value tests.
  - [x] M3.8.a Define supported language/locale/age/grade/content-kind
        applicability, metrics/features, units, versions, thresholds, and
        limitations. Evidence: analysis schema.
  - [x] M3.8.b Compute server-side
        token/sentence/word/syllable/lexical/syntactic/ cohesion/domain-term
        features on exact block/lesson revision. Evidence: known-value fixtures.
  - [x] M3.8.c Return raw measures/regions/assumptions/uncertainty/N/A
        separately from institution policy bands and suggestions. Evidence:
        result tests.
  - [x] M3.8.d Avoid penalizing proper nouns, technical terms, quotations, code,
        math, multilingual/learner-level intentional text without applicability
        context. Evidence: false-positive tests.
  - [x] M3.8.e Present advisory evidence with source/version and author accept/
        dismiss/revise decision; never auto-rewrite. Evidence: review UI tests.
  <!-- M3.8 completion evidence (2026-08-30):

       - Metis now owns a strict v1 readability profile for English, the exact
         `en`, `en-US`, and `en-GB` locales, ages 11–120, four grade bands, six
         analyzable prose-bearing block kinds, six automatically N/A block
         kinds, nineteen versioned raw metrics across token, sentence, word,
         syllable, lexical, syntactic, cohesion, and domain feature families,
         four grade-sensitive policy references, 500-block and two-million-
         prose-character request bounds, and six disclosed limitations.
         Unsupported languages, English locales,
         audiences, content kinds, and locale mismatches return explicit N/A
         evidence rather than fabricated values.
       - The deterministic server engine analyzes exact typed block fields and
         binds every result to lesson, module, course, dossier, dossier digest,
         and block-document digest witnesses. A known-value fixture proves all
         nineteen unique outputs for `cats nap. dogs run.`, including four
         tokens/words/syllables, two sentences, mean length 2.00, reading ease
         120.21, and grade proxy -3.01. The content digest is stable when only
         observation time changes.
       - Raw lesson/block measures, exact field paths and character offsets,
         excerpts, regions, exclusions, assumptions, uncertainty, and N/A
         reasons are structurally separate from the institution policy result.
         Policy carries its own ID/version, observed/reference comparisons and
         advisory prompts, reports `aggregate_score=not_computed`, and never
         represents a learner diagnosis, quality score, or accessibility score.
       - Declared technical/domain terms, quotations, likely proper nouns and
         acronyms remain visible as excluded raw regions but leave policy-grade
         arithmetic. Parameterized fixtures prove explicit N/A behavior for
         code, MathML equations, locale mismatch, unsupported English locale,
         unsupported language and audience, and author-declared intentional or
         multilingual complexity. Every N/A case yields no policy prompt.
       - Analysis is a read-only named operation. Accept, dismiss, and revise
         are authenticated human decisions: the service recomputes the exact
         analysis, rejects stale digests and unknown suggestions, appends an
         immutable review record to the dossier, and confirms
         `content_mutated=false`. Generic dossier replacement cannot bypass the
         review ledger. Suggestion contracts contain evidence and author prompts
         while fixing proposed replacement to null and automatic rewrite to
         false.
       - The frontend-skill-informed language proof sheet uses a restrained warm
         manuscript layout with exact witnesses, applicability controls,
         protected vocabulary, raw measures and anchored regions, explicit N/A,
         a separate policy table with formulas/limits, and a dark immutable
         human-decision ledger. The inspected full-page Chromium capture keeps
         the evidence hierarchy legible. The UI contains no horizontal overflow
         at 390px, honors reduced motion, and fixes the contrast and definition-
         list semantics found by automated accessibility review.
       - Automated proof passes focused Ruff/format, Python 3.12 strict targeted
         MyPy, 130 service/block/interaction/rubric/technical/API/OpenAPI tests,
         live OpenAPI and generated-client drift checks, Metis web ESLint,
         typecheck, 157 unit tests, nine deterministic evidence tests, and 78
         axe-backed browser journeys across Chromium, Firefox, and WebKit: 15
         readability, twelve dossier, 36 block-editor, and 15 rubric cases.
         Current traceability and input checksums are published at
         `evidence/v1-workbenches/metis-lesson-readability.json`.

       This is inspectable local contract and browser-runtime evidence, not
       proof of production deployment or persistence integration,
       representative learner/author usability, assistive-technology
       validation, formal accessibility conformance, institutional threshold
       approval, comprehension, instructional validity, legal review, or
       accreditation. The v1 engine is deliberately English-only and uses
       disclosed punctuation, orthographic syllable, marker-word clause, and
       adjacent-word-overlap heuristics rather than a full linguistic parser.
       The repository's default Python 3.11 MyPy mode currently stops in
       installed NumPy stubs that use Python 3.12 syntax; the changed service
       surface is clean in the actual Python 3.12 environment. A production
       Next build was not run under the small-host hard memory gate; targeted
       typecheck, unit tests, dev-runtime compilation, and three-engine browser
       verification pass. Those operational and baseline gates remain
       explicit. -->
- [x] M3.9 Build terminology and glossary lint. Evidence: locale-aware,
      course-scoped results.
  - [x] M3.9.a Define versioned course/locale glossary entries, preferred/
        allowed/deprecated/forbidden terms, definitions, variants, context,
        capitalization, links, and ownership. Evidence: glossary schema.
  - [x] M3.9.b Tokenize/normalize by locale/script while excluding code, math,
        citations, quoted source, names, and configured contexts. Evidence:
        locale fixtures.
  - [x] M3.9.c Detect undefined/inconsistent/deprecated/forbidden/ambiguous
        terms, acronym first use, spelling/casing, and glossary-link drift with
        exact ranges. Evidence: lint tests.
  - [x] M3.9.d Scope results to exact course/glossary/lesson revisions and
        distinguish inherited/institution/domain terms and conflicts. Evidence:
        scope tests.
  - [x] M3.9.e Present replace/add-definition/link/allow-context/dismiss actions
        with human decision and no destructive bulk edit. Evidence: lint UI
        tests.
        <!-- M3.9 implementation evidence (2026-08-30):
        - Added immutable `metis.terminology-lint` v1 contracts for exact
          institution/domain/course glossary snapshots and entry revisions.
          The schema binds locale, scope identity, inheritance, publication,
          preferred/allowed/deprecated/forbidden status, definition, spelling/
          alias/acronym variants, contextual dispositions, capitalization,
          definition/authority/usage/translation links, replacement lineage,
          and accountable owners. The lesson dossier projection is version
          3.5.0 and carries an append-only terminology-review ledger.
        - The deterministic server engine resolves exact-locale entries by
          course > domain > institution precedence without hiding inherited
          resolutions or status/definition/capitalization/ambiguous-surface
          conflicts. Exact lesson/module/course/dossier/block witnesses and
          every glossary revision/hash are joined into one glossary-set digest;
          stale, foreign-course, missing-block, and oversized scopes fail with
          stable errors.
        - Unicode NFKC plus case-fold tokenization preserves original field
          offsets and identifies Latin, Han, Hiragana, and Katakana evidence.
          Parameterized en-US, es-ES, fr-FR, and ja-JP fixtures cover accented,
          apostrophe, and full-width forms. Code, math, citations, quotations,
          configured names/contexts, unsupported locales/content, and empty
          analyzable text become exact exclusions or explicit N/A rather than
          silent findings. Limits are disclosed and enforced at 500 blocks,
          2,000,000 analyzed characters, 100 glossaries, and 100,000 entries.
        - All nine required categories—undefined, inconsistent, deprecated,
          forbidden, ambiguous, acronym-first-use, spelling, casing, and
          glossary-link drift—are covered by server-known fixtures with exact
          block, JSON field path, start/end offsets, excerpt, entry/glossary/
          conflict provenance, severity, and grounded actions. The analysis
          digest excludes observation time; every finding declares
          `automatic_edit=false` and the analysis declares
          `content_mutated=false`.
        - Added authenticated analysis and review commands to the dossier API,
          live/checked-in OpenAPI, and generated TypeScript client. Review
          submission recomputes the exact analysis and rejects stale witnesses,
          unknown findings, mismatched actions, or out-of-scope link targets.
          Exactly one replace/add-definition/link/allow-context/dismiss proposal
          is appended with reviewer and full witness evidence; generic dossier
          updates cannot bypass the ledger, and both `content_mutated` and
          `glossary_mutated` remain false.
        - Following the required frontend skill, the author surface uses an
          editorial terminology-ledger thesis: restrained paper-like lesson
          evidence, a dark provenance rail, one teal action accent, visible
          layer conflicts, and a separate preserved-decision ledger instead of
          generic dashboard cards. It exposes locale/scope controls, definition
          candidates, name exclusions, block treatments, exact ranges,
          exclusions/N/A, method limits, all five human actions, immutable
          history, stale-witness reload, direct role restriction, keyboard focus,
          responsive 390px layout, and reduced-motion behavior. Automated axe
          review also drove dark-rail contrast and opacity-free motion fixes.
        - Automated proof passes focused Ruff/format, Python 3.12 strict MyPy
          for all six affected service files, 147 terminology/readability/
          dossier/block/interaction/rubric/technical/API/OpenAPI tests, live
          OpenAPI and generated-client drift checks, Metis web ESLint,
          typecheck, 161 unit tests, nine deterministic evidence tests, and 96
          affected axe-backed browser journeys across Chromium, Firefox, and
          WebKit: 18 terminology, 15 readability, twelve dossier, 36 block
          editor, and 15 rubric cases. Current traceability and input checksums
          are published at
          `evidence/v1-workbenches/metis-lesson-terminology.json`.

        This is inspectable local contract and browser-runtime evidence, not
        proof of production deployment or persistence integration,
        institutional glossary approval, representative author usability,
        assistive-technology validation, formal accessibility conformance,
        semantic or linguistic correctness, legal review, or accreditation.
        The v1 engine supports disclosed en/es/fr/ja locale families but uses
        literal normalization/token patterns, deterministic quotation/citation/
        name controls, and no morphological parser or semantic disambiguation;
        Japanese script runs are not word segmentation. Undefined-term evidence
        is conservatively limited to configured definition candidates and
        unregistered uppercase acronyms. A production Next build was not run
        under the 15 GiB host's hard memory gate; targeted typecheck, unit tests,
        dev-runtime compilation, and three-engine browser verification pass.
        Those operational and baseline gates remain explicit. -->
- [x] M3.10 Build inclusivity and bias assist as review evidence, not an
      unchallengeable truth score. Evidence: model/version/uncertainty visible.
  - [x] M3.10.a Define versioned review dimensions/rubrics, applicability,
        evidence units, evaluator/provider/model/config, thresholds/manual
        bands, limitations, and protected-data policy. Evidence: assist schema.
  - [x] M3.10.b Analyze exact content/item/media revision for representation,
        stereotypes, exclusionary language/context, cultural/linguistic bias,
        accessibility, and assessment construct-irrelevant barriers. Evidence:
        calibrated fixtures.
  - [x] M3.10.c Return attributed findings with text/region/item anchor,
        category, rationale, score/confidence/uncertainty, counterevidence, and
        N/A/error. Evidence: result tests.
  - [x] M3.10.d Display evaluator/model/version/corpus limits and avoid one
        aggregate “bias score” or automatic truth/author blame. Evidence: UI
        tests.
  - [x] M3.10.e Require authorized human
        accept/reject/reframe/request-specialist review decisions, preserving
        proposal and disagreement. Evidence: authority tests.
      <!-- M3.10 implementation evidence (2026-08-30):
        - `lesson_inclusivity.py` defines the immutable `1.0.0` assist
          contract: six separately reported review dimensions, typed evidence
          units, applicability, three human-review bands, exact evaluator,
          provider, model, configuration, corpus, and calibration identities
          plus hashes, and explicit limitations. Its protected-data policy
          permits authored content and public course metadata only, forbids
          learner identity/enrollment/grade/attempt/accommodation/demographic
          and inferred-trait inputs, keeps provider transmission and excerpt
          persistence off, prohibits trait inference, and requires an
          authorized human decision.
        - `lesson_inclusivity_helpers.py` provides a bounded server-local
          deterministic English evaluator over the exact lesson/module/course/
          dossier/block-document witnesses and selected block, content-item,
          governed-media-revision, and assessment dossier-revision subjects.
          Calibrated literal and structural fixtures cover representation,
          stereotyping, exclusionary language and sensory assumptions,
          cultural/linguistic context, missing media accessibility support, and
          construct-irrelevant assessment barriers. Quoted, historical, and
          configured-exclusion contexts remain explicit rather than silently
          changing the evidence.
        - Results preserve original text offsets or typed item/media/assessment
          anchors, exact revision/hash references, category, rationale,
          evaluator rule/runtime attribution, evidence-strength score,
          confidence, uncertainty, suggested human-review band, and linked
          counterevidence. Every unit is independently `analyzed`, `not_applicable`,
          or `error`; every dimension remains separate; the contract computes
          no aggregate bias score and makes no truth, author-blame, automatic
          edit, or learner-impact claim. Stable subject-set and analysis digests
          make the same exact inputs reproducible.
        - The dossier schema is now `3.6.0`. Analyze and review endpoints bind
          exact witnesses, reject stale or cross-tenant requests, and append
          immutable inclusivity review/event/revision evidence. Direct generic
          mutation of the review ledger is rejected. Authorized reviewer roles
          may accept, reject, reframe, or request specialist review; each record
          preserves the evaluator proposal, human rationale, disagreement or
          reframing, specialist request, reviewer identity/role, and confirms
          that neither the finding nor authored content was mutated.
        - The Metis `Challenge folio` workbench follows the mandatory
          `frontend-skill`: a warm editorial evidence surface with one muted
          violet accent and a dark provenance margin, not a dashboard scorecard.
          It exposes exact scope and witnesses, six independent dimensions,
          anchored findings, counterevidence, N/A/error states, all model/
          provider/config/corpus/calibration versions and limitations, all four
          human decisions, preserved disagreement, immutable history, stale
          reload, role refusal, 390px reflow, and reduced-motion behavior.
          Automated axe review also drove a provenance-rail contrast correction.
        - Automated proof passes focused Ruff/format, Python 3.12 strict MyPy
          for six affected service files, 171 inclusivity/readability/
          terminology/dossier/block/interaction/rubric/technical/API/OpenAPI
          tests, live OpenAPI and generated-client drift checks, Metis web
          ESLint, typecheck, 164 unit tests, nine deterministic evidence tests,
          and 114 affected axe-backed browser journeys across Chromium,
          Firefox, and WebKit: 18 inclusivity, 18 terminology, 15 readability,
          twelve dossier, 36 block-editor, and 15 rubric cases. Current
          traceability and input checksums are published at
          `evidence/v1-workbenches/metis-lesson-inclusivity.json`.

        This is inspectable local contract and browser-runtime evidence, not
        proof of production deployment or persistence integration, cultural or
        linguistic validity, demographic representativeness, lived impact,
        institutional policy approval, legal review, representative specialist/
        author usability, assistive-technology validation, or formal
        accessibility conformance. The v1 evaluator supports disclosed en,
        en-US, and en-GB locales and deliberately uses a small versioned literal/
        structural corpus; it cannot infer intent, speaker position, dialect,
        pragmatics, cultural meaning, the assessed construct, or whether an
        alternative is equivalent. Assessment analysis is bound to the exact
        dossier revision because the legacy assessment link has no separate
        upstream version field; media without a governed asset revision is an
        explicit error. A production Next build was not run under the 15 GiB
        host's hard memory gate; targeted typecheck, unit tests, dev-runtime
        compilation, and three-engine browser verification pass. Those
        operational and baseline gates remain explicit. -->
- [x] M3.11 Build lesson duration and cognitive-load estimates. Evidence:
      assumptions and confidence shown.
  - [x] M3.11.a Define per-block/activity/media/assessment time and intrinsic/
        extraneous/germane or ratified load model with versions/units/
        assumptions/applicability. Evidence: estimate schema.
  - [x] M3.11.b Compute active/passive/synchronous/independent duration and load
        by segment from exact content quantities, modality, difficulty,
        prerequisites, language, device, and supports. Evidence: fixtures.
  - [x] M3.11.c Aggregate sequence peaks, sustained load, switching, redundancy,
        pacing, breaks, and total ranges with uncertainty. Evidence: sequence
        tests.
  - [x] M3.11.d Compare against brief/institution/learner-band constraints and
        surface assumptions/data gaps/remediation without treating estimates as
        fact. Evidence: explainability tests.
  - [x] M3.11.e Record author override with reason and preserve calculated
        values separately; recompute on revision/model change. Evidence:
        lifecycle tests.
      <!-- M3.11 implementation evidence (2026-08-30):
        - `lesson_duration_load.py` defines the immutable `1.0.0` estimate
          contract and the `metis.lesson-duration-load` / `1.0.0` model plus
          `metis.lesson-duration-load.en-v1` / `1.0.0` configuration. Eleven
          versioned rates cover block, activity, media, and assessment subjects
          in word, line, equation, image, media-second, activity, and
          assessment-item units. Every rate carries applicability, bounded
          seconds per unit, active share, baseline intrinsic/extraneous/germane
          planning-proxy ranges, assumptions, and source references. The model
          profile exposes its arithmetic, hashes, supported locales, learner-
          band uninterrupted thresholds, and limitations; duration/load
          observation and automatic fact flags are structurally false.
        - `lesson_duration_load_helpers.py` walks exact depth-first authored
          blocks, then dossier-only activities and assessments, and binds every
          segment to a subject revision/hash plus exact quantity source. Block
          quantities come from exact words, lines, equations, images, or typed
          activities; governed media require an exact asset revision and
          declared duration; assessments require a declared exact version, item
          count, and bounded seconds per item. Missing or mismatched evidence is
          an explicit `data_gap`, `not_applicable`, or `error`, never a silent
          zero. Difficulty, prerequisite readiness, instruction language,
          device, supports, modality, delivery mode, synchronous assignments,
          and breaks produce attributed factors. Active plus passive and
          synchronous plus independent ranges remain arithmetically balanced.
          Scope is bounded to 10,000 segments and two million characters per
          text subject, and stable digests exclude observation time.
        - Sequence aggregation reports lower/expected/upper total and channel
          ranges, complete/known-subtotal/unavailable status, confidence,
          missing segment IDs, separate intrinsic/extraneous/germane peaks,
          sustained high-load and uninterrupted runs, modality switches,
          adjacent redundancy, pacing changes, and planned breaks with
          uncertainty and remediation. Brief and institution constraints carry
          policy code/version/evidence, while the learner-band uninterrupted
          constraint is bound to the model profile. Interval comparisons return
          within/below/above/straddles/inconclusive/not-available and explicitly
          state that they are not observed learner results; missing policy
          sources do not become passes.
        - The dossier schema is now `3.7.0`. Estimate requests are stateless and
          bind exact lesson/module/course/dossier/block-document, subject-set,
          context, model, and configuration witnesses. The named override
          operation locks and recomputes the current estimate, rejects stale or
          unknown segments and cross-tenant access, snapshots the raw calculated
          duration/load vector, and appends a reasoned replacement plus evidence
          reference, actor, and exact hashes. Calculated and effective segments,
          summaries, and constraints remain separate. An override applies only
          to the exact subject/context/model tuple; later exact overrides
          supersede without erasing history, and content/context/model revisions
          surface stale-subject/stale-context/stale-model dispositions. Direct
          generic mutation of the override ledger is rejected, and receipts
          confirm `content_mutated=false` and
          `calculated_values_mutated=false`.
        - The Metis `Pacing sheet` workbench follows the mandatory
          `frontend-skill`: a warm editorial rehearsal surface with restrained
          rust/navy accents, a calculated staff beside an author-adjusted staff,
          explainable segment sheets, and a dark provenance rail rather than a
          generic score dashboard. Authors can declare every context axis,
          supports, synchronous segments, breaks, exact media and assessment
          quantities, and policy ceilings; inspect all four duration channels,
          all three separately labeled planning proxies, sequence signals,
          constraint comparisons, assumptions, uncertainty, gaps, remediation,
          versions, and hashes; then append a balanced adjustment while the raw
          estimate stays visible. It includes stale reload, role refusal, 390px
          reflow, reduced-motion handling, confined sequence overflow, and an
          explicit “estimate, never observation” boundary. Automated axe review
          drove darker accent and secondary-text contrast before acceptance.
        - Automated proof passes focused Ruff format/lint, Python 3.12 strict
          MyPy for six affected source files, 185 estimator/inclusivity/
          readability/terminology/dossier/block/interaction/rubric/technical/
          API/OpenAPI tests, live OpenAPI and generated-client drift checks,
          Metis web ESLint, typecheck, 167 unit tests, nine deterministic
          evidence tests, and 132 affected axe-backed browser journeys across
          Chromium, Firefox, and WebKit: 18 duration/load, 18 inclusivity, 18
          terminology, 15 readability, twelve dossier, 36 block-editor, and 15
          rubric cases. Current traceability and input checksums are published
          at `evidence/v1-workbenches/metis-lesson-duration-load.json`.

        This is inspectable local contract and browser-runtime evidence, not
        proof of production deployment or persistence integration, empirical
        timing or cognitive-load validity, observed learner state, outcome or
        ability prediction, institutional policy approval, representative
        author or learner usability, assistive-technology validation, formal
        accessibility conformance, or accreditation. The eleven v1 rates and
        context multipliers are coarse disclosed deterministic planning
        assumptions over en/en-US/en-GB content, not a validated psychometric or
        learning-science instrument. No learner records, demographics,
        telemetry, attempts, grades, accommodations, or measured behavior are
        inspected. Media duration must be supplied against a governed asset
        revision. Assessment version and timing are author-declared because the
        legacy dossier assessment link has no separate upstream version field.
        A production Next build was not run under the 15 GiB host's hard memory
        gate; targeted typecheck, unit tests, dev-runtime compilation, and
        three-engine browser verification pass. Those operational and baseline
        gates remain explicit. -->
- [x] M3.12 Build learner preview using the real delivery renderer and
      permission-safe sample identity. Evidence: author/learner parity test.
  - [x] M3.12.a Invoke the same versioned renderer/runtime/target contracts used
        by learner delivery against an exact unpublished course/lesson build.
        Evidence: renderer parity tests.
  - [x] M3.12.b Issue a synthetic sample identity with no real enrollment/
        learner/grade/integrity authority and a bounded preview session.
        Evidence: identity isolation tests.
  - [x] M3.12.c Project selected synthetic locale/preferences/PNP/prerequisite/
        progress states without copying real learner records. Evidence: context
        tests.
  - [x] M3.12.d Prevent preview interactions from creating real attempts,
        grades, credentials, analytics, notifications, integrity cases, or LTI/
        LMS writes. Evidence: side-effect tests.
  - [x] M3.12.e Compare author preview DOM/media/interaction/accessibility/
        scoring-safe behavior to learner delivery for the same build. Evidence:
        parity journey.
      <!-- M3.12 implementation evidence (2026-08-30):
        - Metis now has a separate immutable `1.0.0` lesson-delivery contract.
          One `LessonDeliveryDocument` binds the exact lesson/module/course/
          dossier/block-document revisions and hashes, publication posture,
          learner-web target contract, block and activity registry versions,
          interaction protocol, renderer ID/version/hash, selected context,
          redacted content, governed learner media, filtered runtime entries,
          semantic block projections, and its own deterministic digest. Author
          preview and published delivery are distinct authenticated read routes,
          but both call the same pure document builder. Published access permits
          a course author/administrator, an authenticated free-preview learner,
          or an active enrollment and returns only an opaque subject digest; it
          records no access or progress mutation. Preview can bind an unpublished
          build through complete optimistic witnesses and never needs a publish
          state or fake enrollment.
        - The author route issues a random, bounded 5–30 minute, non-resumable
          sample session whose state is limited to browser parent memory. Its
          generated `Sample learner` identity has structurally false enrollment,
          grade, and integrity authority, no bearer token, no canonical learner
          or profile lookup, and no protected-data copy. Locale, explanation
          depth, media preference, pace, PNP supports, prerequisite state,
          progress state/percent/completed block IDs, and a rationale are typed,
          hashed, uniqueness/consistency checked, and constrained to the exact
          current block set. The service uses `for_update=False`, does not load
          Enrollment or Progress on preview, does not append a dossier event or
          revision, and does not call add/flush. Exact answer values, assessment
          explanations, and canonical person IDs are scanned out before the
          contract can leave the service.
        - Preview write isolation names and exactly denies attempts, grades,
          credentials, analytics, notifications, integrity, LTI, and LMS sinks;
          mutation transport is disabled, credentials are not issued,
          persistence hooks are absent, scoring is local/nonpersistent, and the
          event budget is bounded to parent memory. The shared React renderer has
          a single `document` prop and no preview/delivery mode branch or network
          client. Completion, practice, reflection, governed activity, and
          assessment-detail actions use component-local state only. Governed
          media fail closed on withheld or unsafe locators. Interaction blocks
          fail closed without the exact filtered runtime-registry entry and
          expose the same activity/runtime, learner-web target, and opaque-origin
          sandbox profile in both surfaces. Assessment answers never enter the
          document.
        - Following the mandatory `frontend-skill`, the new delivery rehearsal
          is an editorial learner canvas rather than a dashboard: the real lesson
          paper dominates, an intentionally compact sample-context rail sits to
          one side, and exact build/session/write/parity evidence sits on the
          other. The author page owns those rails and session countdown outside
          the renderer. Course play invokes the same renderer for canonical UUID
          lessons and retains its prior content/Yemaya fallback when typed
          delivery is absent. The renderer covers all twelve block kinds,
          semantic math and code alternatives, governed media/accessibility
          text, exact activity runtime evidence, keyboard-operable local inputs,
          withheld states, selected synthetic completion, responsive 390px
          reflow, and reduced motion. Existing fake-ID course fixtures keep their
          legacy path, while real UUID lessons take typed delivery.
        - Parity is executable evidence rather than a UI claim. The contract
          requires equal preview/learner document hashes and the same renderer
          contract hash. Service tests compare complete author and published
          documents; React server-render tests compare exact static markup; and
          the Playwright journey compares exact `innerHTML` plus document digest
          between author rehearsal and course play for the same build. Separate
          browser journeys verify all twelve kinds and media/accessibility
          alternatives, exact context payloads, zero API requests after local
          interactions, 409 witness reload, role refusal before dossier access,
          and 390px/reduced-motion containment. Every journey uses the shared
          automatic WCAG 2.1 AA axe fixture.
        - Automated proof passes focused Ruff format/lint, Python 3.12 strict
          targeted MyPy for the four affected service sources, 190 lesson block/
          activity/technical/dossier/duration/readability/terminology/
          inclusivity/rubric/API/OpenAPI tests, live OpenAPI and generated-client
          drift checks, Metis web ESLint and typecheck, 173 web unit tests (six
          focused context/renderer cases), nine deterministic evidence tests,
          and 18 delivery-preview browser journeys across Chromium, Firefox,
          and WebKit. Current traceability and input checksums are published at
          `evidence/v1-workbenches/metis-lesson-delivery-preview.json`.

        This is inspectable local contract, renderer, and automated browser
        evidence, not proof of production deployment or persistence integration,
        every external activity or media runtime, representative author or
        learner usability, assistive-technology behavior, formal accessibility
        conformance, or every future target/build combination. The covered
        locale set is deliberately en/en-US/en-GB; device/theme/network/offline
        matrices remain M3.13 scope. The existing production course-progress
        chrome is outside the shared document and is not asserted as preview
        parity. The installed MyPy configuration defaults to Python 3.11 while
        installed NumPy stubs use Python 3.12 syntax; the changed service surface
        is clean under the actual Python 3.12 environment. A production Next
        build was not run under the 15 GiB host hard-memory gate; targeted
        typecheck, unit tests, development-runtime compilation, visual review,
        and three-engine browser verification pass. -->
- [x] M3.13 Build device, theme, locale, accommodation, offline, and bandwidth
      preview modes. Evidence: preview matrix.
  - [x] M3.13.a Define supported viewport/input/device capabilities, themes/
        contrast/motion, locales/direction, PNP supports, connectivity/cache,
        and network profiles as versioned preview presets. Evidence: preset
        schema.
  - [x] M3.13.b Apply presets to the real learner renderer without claiming
        hardware/assistive-technology emulation beyond measured capabilities.
        Evidence: preview accuracy tests.
  - [x] M3.13.c Verify content/layout/input/media/interaction/accessibility/
        localization behavior and explicit unsupported/degraded state for every
        valid matrix cell. Evidence: preview matrix.
  - [x] M3.13.d Simulate offline/intermittent/slow/high-latency/limited-data
        behavior with cache provenance, fallback, retries, and no hidden
        success. Evidence: network tests.
  - [x] M3.13.e Save/share a bounded preview configuration and findings without
        learner identity or false conformance certification. Evidence: report
        tests.
      <!-- M3.13 implementation evidence (2026-08-30):
        - The immutable `metis.lesson-delivery-preview-presets@1.0.0` registry
          exposes exactly eight finite cells spanning desktop/tablet/phone CSS
          viewports; pointer, touch, keyboard, and combined requested input
          semantics; light/dark/high-contrast/reduced-motion scoped appearance;
          en-US/en-GB plus an explicit en-US RTL direction-only fallback;
          registered PNP supports; fast/high-latency/limited-data/intermittent/
          offline transport; and write-through, explicit-network, warm-session,
          and empty-session cache modes. The registry and every bound
          environment carry deterministic SHA-256 witnesses and are available
          through an instructor/admin route and generated OpenAPI client.
        - Registered sessions still mount `LessonDeliveryRenderer` over the
          exact immutable M3.12 document. `LessonDeliveryRuntimeProvider` adds
          only CSS-container dimensions, renderer-scoped tokens/direction/
          requested semantics, and controlled media transport. Contracts fix
          physical-hardware emulation, assistive-technology emulation, and
          formal accessibility certification to false; saved measurements
          separately record actual browser container, pointer/hover, theme,
          contrast, motion, online, locale, direction, and DPR observations.
        - Every cell returns all seven content/layout/input/media/interaction/
          accessibility/localization outcomes with explicit supported,
          degraded, unsupported, or not-applicable status. Playwright iterates
          all eight cells in Chromium, Firefox, and WebKit, checks exact
          container dimensions/direction/theme/transport, containment, and a
          WCAG 2.1 A/AA axe analysis inside the real learner document for every
          cell. Existing exact author/published DOM parity remains covered when
          no matrix cell is selected.
        - Governed media starts source-less, then uses credential-free,
          no-store controlled fetch with latency/throughput pacing, deterministic
          intermittent drops, exact content-type/byte-length/SHA-256 checks,
          document-bound session-memory cache provenance, explicit limited-data
          opt-in, visible retry, and offline warm-hit/cold-miss fallback. Browser
          request ledgers prove held, failed, and offline states make no hidden
          request. The tests caught and removed an initial-render native request
          and made retry counters safe under React development effect replay.
        - Migration 005 and `LessonDeliveryPreviewReportRecord` define immutable
          author/admin-scoped, idempotent, exact-build/environment reports with
          30-day expiry. POST/GET routes reauthorize course scope, revalidate all
          witnesses and governed media observations, reject idempotency drift,
          verify stored report hashes, and reject expired reports. The response,
          share receipt/link, and JSON download contain a bounded safe context,
          configuration, actual measurements, and server-derived findings while
          structurally excluding learner/author identity and all hardware/AT/
          conformance claims.
        - Verification passed: Ruff and Python 3.12 MyPy on every changed service
          surface; offline Alembic upgrade SQL through revision 005; 83 focused
          service/API/OpenAPI/export tests; current OpenAPI regeneration; full
          Metis web lint/typecheck; 177 web unit tests; and 27 focused Playwright
          cases across Chromium/Firefox/WebKit with both automatic serious/
          critical axe gating and the per-cell WCAG A/AA checks. Deterministic
          traceability is stored at
          `evidence/v1-workbenches/metis-lesson-delivery-preview-matrix.json`.
        - Scope remains explicit: the checks do not prove production deployment,
          a live database migration, representative author/learner usability,
          physical hardware, assistive technology, formal accessibility
          conformance, or arbitrary custom matrix cells. A production Next build
          was not run under the 15 GiB host approval gate; targeted typecheck,
          unit coverage, and three-engine development-runtime compilation pass. -->
- [x] M3.14 Build autosave, revision history, semantic diff, restore, and
      concurrent conflict resolution. Evidence: Playwright conflict journey.
  - [x] M3.14.a Implement local draft state and debounced idempotent server
        autosave with expected revision, dirty/saving/saved/offline/error/
        conflict indicators, retry, and unload recovery. Evidence: autosave
        tests.
  - [x] M3.14.b Persist immutable authoring revisions/checkpoints with author/
        reason/time/base, changed entities, and audit; avoid revision explosion
        from transient UI state. Evidence: history tests.
  - [x] M3.14.c Compute semantic block/field/relationship/objective/source/
        accessibility diffs using stable IDs and protected-field redaction.
        Evidence: diff fixtures.
  - [x] M3.14.d Restore by creating a new revision from an authorized historical
        version with impact/stale approval preview. Evidence: restore tests.
  - [x] M3.14.e Detect concurrent edits at entity/field/list level, present
        mine/theirs/base, support safe merge/manual resolution, and forbid last-
        write-wins data loss. Evidence: conflict tests.
  - [x] M3.14.f Run two-editor/offline/reconnect/reorder/delete/restore/
        permission-revocation accessible Playwright journey. Evidence: conflict
        E2E.
  <!-- M3.14 completion evidence (2026-08-30):
       - The authoring write boundary now separates private mutable autosave
         heads from immutable canonical checkpoints. Each editor-owned
         `LessonAuthoringDraftRecord` carries an immutable exact base witness
         and full base document, a monotonically compared draft version, the
         current document digest, and request-bound idempotency evidence. A
         700 ms client debounce writes only through compare-and-swap; retries
         cannot reuse a key with different content, a stale request cannot
         overwrite the draft head, and another actor cannot read it.
       - The change-ledger client keeps a lesson+actor-scoped, versioned local
         recovery envelope with a seven-day expiry and no credentials. It
         persists synchronously on every accepted edit and `beforeunload`,
         reconciles the server head without dropping a newer local intent,
         retries on reconnect, and exposes current/dirty/saving/saved/offline/
         error/conflict/access-revoked states. A revoked 403 stops automatic
         retries while retaining an explicit JSON export. Protected source
         excerpts remain excluded by the typed dossier boundary.
       - Migration 006 adds the private draft table and enriches every future
         immutable dossier revision with a full canonical+dossier document and
         digest, revision kind, base revision number/hash, changed stable
         entities, restore provenance, and request idempotency evidence. The
         existing actor, role, reason, timestamp, chained revision hash, and
         chained audit event remain server-owned. Named content operations now
         capture the same full snapshot; autosave tests prove that transient
         keystrokes create zero immutable revisions.
       - `lesson_authoring_helpers.py` computes deterministic domain-semantic
         block, scalar field, relationship, objective, source, and
         accessibility changes. Collections bind stable domain IDs rather than
         indexes; relationship additions merge as sets with stable order; and
         delete-versus-edit, reordered positions, same-field edits, and other
         overlaps retain base/mine/theirs. Source excerpts and answer/secret
         field families are structurally redacted from diff and conflict
         projections.
       - Checkpoint creation performs a server-side three-way merge from the
         immutable base, private head, and newly authorized canonical state.
         Non-overlapping field and relationship changes merge safely. Every
         unresolved entity/field/list overlap returns a typed conflict outcome
         with no projection or revision, and mine/theirs/manual resolutions
         must bind the current deterministic conflict IDs before validation and
         commit. Named review/rubric/governed-media evidence still cannot be
         bypassed through the generic authoring document.
       - Authorized comparison and restore endpoints read hash-verified full
         historical snapshots. Restore preview rechecks all optimistic
         witnesses, computes the exact semantic direction and changed entities,
         lists approval events that will become stale, and issues a stable
         preview digest. Apply recomputes that preview and creates a new chained
         `restore` revision pointing to the historical source; it never mutates
         old history. Pre-M3.14 rows without a full canonical snapshot fail
         explicitly instead of fabricating restorable values.
       - The UI follows the frontend-skill's calm editorial change-ledger
         thesis: the draft is dominant, the immutable timeline is a narrow
         rail, and conflicts expand in place into base/mine/theirs evidence.
         Conflict mount transfers keyboard focus to “No silent winner”; all
         choices are labelled fieldset controls, protected values announce
         redaction, and the layout reflows without page overflow at 390 px.
       - Verification passed: Ruff on the full Metis service, Python 3.12 MyPy
         on every changed service surface, offline Alembic SQL through the sole
         006 head, deterministic OpenAPI export plus generated Metis TypeScript
         drift checks, 209 lesson/OpenAPI/provider Python tests, full Metis web
         lint and typecheck, all 181 web unit tests, and six focused Playwright
         cases across Chromium/Firefox/WebKit. The browser suite uses the
         automatic serious/critical axe gate and proves two isolated editor
         contexts, conflict focus and no last-write-wins, offline unload and
         reconnect recovery, block reorder+delete, restore, permission
         revocation with stopped retries, local export, reduced motion, and
         390 px reflow. Deterministic traceability is stored at
         `evidence/v1-workbenches/metis-lesson-authoring-revisions.json`.
       - Scope remains explicit: tests do not prove a production deployment, a
         live migration, representative two-person usability, physical
         assistive-technology behavior, or formal accessibility conformance.
         The two browser editors use isolated contexts against a deterministic
         API ledger. A production Next build was not run under the 15 GiB host
         approval gate; targeted development-runtime compilation, typecheck,
         unit, and three-engine browser checks pass. -->
- [x] M3.15 Build explicit AI assist for rewrite, example, hint, feedback, and
      localization. Evidence: per-block accept and AI attribution.
  - [x] M3.15.a Define assist request/proposal contracts for each operation with
        exact block/course/source/objective/item/locale revisions, instructions,
        provider provenance, cost, and status. Evidence: assist schemas.
  - [x] M3.15.b Send only authorized bounded content/source context through the
        fenced provider with answer-key/integrity/learner-data exclusions and
        injection resistance. Evidence: context boundary tests.
  - [x] M3.15.c Validate operation-specific output structure, citations,
        protected fields, locale, accessibility, safety, and malformed/refusal/
        unavailable/budget states. Evidence: provider tests.
  - [x] M3.15.d Present per-block diff/source/uncertainty/attribution and
        require explicit accept/edit/reject; never replace content
        automatically. Evidence: review UI tests.
  - [x] M3.15.e Accept as a new attributed revision, preserve proposal and human
        edits, and recompute affected correctness/accessibility/gates. Evidence:
        lineage tests.
        <!-- M3.15 evidence (2026-08-30):
        - `lesson_assist.py` and the server-authored preparation receipt bind the
          exact lesson/module/course/dossier/block/registry/locale witness plus
          hashed source versions, objective revisions, and relevant item
          records. Provider/model/config/prompt/pricing identity, tokens, USD
          minor-unit cost, outcome, and zero-tool status remain immutable.
        - The OpenAI-compatible boundary uses five prompt-trust zones, rejects
          truncation, preflights the educator-confirmed budget, and excludes
          answer keys, integrity fields, learner data, protected source text,
          credentials, and tools structurally. Trusted/current/rights-cleared
          references and operation-specific output, citation, locale,
          accessibility-review, and integrity-marker checks fail closed.
        - The block inspector adds a restrained proposal margin rather than a
          chat surface. It shows the field diff, source citations, uncertainty,
          rationale, provider/model/cost, and quarantined provider states. The
          canonical editor remains unchanged until separate accept, accept-edit,
          or reject controls record an educator reason.
        - Accepted output creates only an `assist_accept` immutable revision
          carrying proposal/provider/model/operation/human-edit/effective-block
          attribution. Proposal and educator-edited JSON remain separate rows;
          block validation, correctness/accessibility findings, readiness, and
          release gates are recomputed. Reject creates no canonical revision and
          stale proposals cannot apply.
        - Automated proof covers all five operations, refusal/malformed/
          unavailable/budget/stale states, injection and protected-data
          fixtures, idempotency, lineage, 390 px reflow, reduced motion,
          keyboard focus, and automatic axe analysis. Deterministic
          traceability is stored at
          `evidence/v1-workbenches/metis-lesson-assist.json`.
        - Scope remains explicit: provider and browser transports are mocked;
          tests do not prove a live model, production deployment, production
          migration, representative educator usability, physical assistive-
          technology behavior, or formal accessibility conformance. Migration
          007 was verified as offline PostgreSQL SQL. A Next production build
          was not run under the 15 GiB host approval gate; targeted development
          compilation, typecheck, unit, service, contract, and three-engine
          browser checks are the applicable proof. -->
- [x] M3.16 Build lesson-template inheritance with code/institution floors.
      Evidence: tenant templates cannot loosen safety or accessibility.
  - [x] M3.16.a Define versioned template identity/scope/applicability, parent,
        typed block skeleton/defaults/constraints/required gates, owner,
        lifecycle, and code-floor refs. Evidence: template schema.
  - [x] M3.16.b Resolve exact parent chain with cycle/depth/version handling and
        explicit per-field/block merge/replace/append/remove/lock semantics.
        Evidence: inheritance tests.
  - [x] M3.16.c Apply code, regional, institution, program, and course floors
        monotonically for safety, rights, sources, accessibility, assessment
        integrity, privacy, and standards. Evidence: floor property tests.
  - [x] M3.16.d Preview inherited/defaulted/overridden/locked/conflicting values
        and effective requirements before instantiation/update. Evidence:
        effective template tests.
  - [x] M3.16.e Version/publish/deprecate templates and explicitly pin/migrate
        lesson consumers without altering historical revisions. Evidence:
        lifecycle tests.
        <!-- M3.16 evidence (2026-08-30):
        - `lesson_template.py` closes the 1.0.0 identity, exact version ref,
          code/regional/institution/program/course scope, applicability,
          ownership, lifecycle, parent, typed value/default, skeleton,
          constraint, required-gate, code-floor, preview, and consumer-lineage
          contracts. Every template carries all seven floor domains exactly
          once, and generated OpenAPI/TypeScript expose only exact typed bodies.
        - The resolver walks a digest-verified root-to-leaf exact chain with
          distinct missing-template, missing-version, mismatch, cycle, maximum-
          depth, scope-inversion, and code-floor-pin failures. Field and block
          merge/replace/append/remove/lock operations preserve contribution
          provenance; attempts to alter locked values remain visible as
          rejected conflicts rather than silently winning.
        - The immutable code floor seeds safety, rights, sources,
          accessibility, assessment integrity, privacy, and standards. Floors
          may only raise their level and add required evidence, gates, and
          prohibitions. Property coverage exercises every one of the seven
          domains against all four weakening dimensions (28 fail-closed cases),
          plus monotonic tightening and a complete five-scope chain.
        - The frontend-skill-guided editor adds a quiet full-width policy ledger
          before block insertion. Educators preview the exact parent chain,
          inherited/defaulted/overridden/locked/removed/conflicting values,
          block skeleton, constraints, gates, all effective floors, and
          provenance before the explicit pin action is enabled. Conflict
          verdicts take focus, pin controls disable fail-closed, motion is
          removable, and the existing editorial manuscript remains dominant.
        - Migration 008 stores immutable definitions, hash-chained publish/
          deprecate events, and append-only instantiate/migrate pins. Exact
          template, preview, dossier revision/hash, actor, reason, idempotency,
          and previous-pin lineage are captured; a storage check requires
          historical revisions to remain preserved, and stale/no-op/changed-
          lineage operations fail closed.
        - Verification passed: targeted Ruff and strict Python 3.12 MyPy, sole
          Alembic 008 head plus offline PostgreSQL SQL, deterministic OpenAPI
          export and generated-client drift checks, 88 resolver/service/API/
          OpenAPI/exporter tests, full Metis web lint/typecheck, all 188 web
          unit tests, eight evidence self-checks, and all 54 lesson-editor
          Playwright cases across Chromium/Firefox/WebKit. Browser proof covers
          exact five-layer preview, pin then migration without history rewrite,
          floor/lock conflicts, lifecycle transitions, focus, reduced motion,
          mobile reflow, and automatic serious/critical axe analysis.
          Deterministic traceability is stored at
          `evidence/v1-workbenches/metis-lesson-templates.json`.
        - Scope remains explicit: service database and browser APIs are mocked;
          tests do not prove a production deployment, a live migration,
          representative educator usability, physical assistive-technology
          behavior, or formal accessibility conformance. A Next production
          build was not run under the 15 GiB host approval gate; targeted
          development compilation, typecheck, unit, service, contract,
          migration, and three-engine browser checks are the applicable proof.
          -->
- [x] M3.17 Build diagram, chart, graph, presentation, audio/TTS, video, and
      Manim-style executable-animation authoring pipelines with source/data
      binding, preview, versioning, cancellation, cost, and per-artifact
      accessibility alternatives. Evidence: no generated teaching media can be
      accepted without its source, code/data, renderer, and critic evidence.
  - [x] M3.17.a Define a common teaching-media project/stage/artifact contract
        with
        objective/content/source/data/code/spec/renderer/model/tool/version,
        cost, progress, output, gates, and lineage. Evidence: media schemas.
  - [x] M3.17.b Implement diagram/illustration pipeline with semantic elements/
        labels/relationships, source grounding, alt/long description, and
        editable vector/spec where supported. Evidence: diagram tests.
  - [x] M3.17.c Implement data chart/graph pipeline with immutable dataset
        slice, transform code, units/scales/labels, statistical checks, data
        table/ narrative alternative, and reproducible render. Evidence: chart
        tests.
  - [x] M3.17.d Implement presentation pipeline with slide/notes/source/media/
        reading-order/contrast/layout contracts and accessible export. Evidence:
        presentation tests.
  - [x] M3.17.e Implement audio/TTS pipeline with script/source, approved voice/
        consent, pronunciation, timing, loudness, transcript, and downloadable
        accessible text. Evidence: audio tests.
  - [x] M3.17.f Implement video pipeline with script/storyboard/scenes/media/
        narration/captions/transcript/audio description/renditions and sync.
        Evidence: video tests.
  - [x] M3.17.g Implement Manim-style animation with versioned source/code/data/
        environment/seed/assets, sandboxed render, frame/time validation,
        captions/descriptions, and nonanimated equivalent. Evidence: animation
        tests.
  - [x] M3.17.h Provide preview/progress/cancel/retry/partial/error/cost/
        regeneration and immutable revision semantics per stage. Evidence:
        lifecycle tests.
  - [x] M3.17.i Require source/correctness/code-data-renderer/provenance/rights/
        safety/accessibility/technical/critic evidence before educator
        acceptance. Evidence: acceptance gate.
        <!-- M3.17 evidence (2026-08-30):
        - `teaching_media.py` closes a single 1.0.0 project, immutable revision,
          ordered stage, content-addressed artifact/evidence, exact lesson/
          objective/source/content witness, versioned renderer/model/tool,
          budget/progress/cost, worker-receipt, lineage, decision, and ten-domain
          acceptance contract. The authenticated API and generated TypeScript
          client expose only these typed request and response bodies.
        - Six exact deterministic renderer environments bind the governed
          control plane to the existing Metis semantic-diagram, verified data-
          figure, presentation-export, TTS, video-compositor, and isolated
          Manim implementations. Diagram semantics and editable alternatives;
          chart dataset/code/scales/statistics/table/narrative; slide reading
          order/contrast/notes/export; voice consent/pronunciation/loudness/
          transcript; video storyboard/renditions/captions/audio description/
          sync; and animation code/data/environment/seed/sandbox/frame/static-
          equivalent requirements all fail closed in their typed specs.
        - Migration 009 stores full append-only project snapshots, artifacts,
          and educator decisions with exact idempotency, hash-chain, indexed
          scope, budget, and composite project-revision foreign keys. Mutations
          lock the current project chain, recheck the live lesson dossier, and
          preserve every attempt. Content/renderer lineage, worker receipts,
          output digests, storage bodies, gates, project state, and decision
          digests are independently reproducible.
        - The frontend-skill-guided lesson editor adds one restrained editorial
          production strip: a project rail, ordered stage timeline, accessible
          artifact preview, exact source/renderer/worker receipt margin, and
          ten-domain evidence ledger. It exposes progress, retained cost,
          partial/error/cancel/retry/regenerate states and immutable revision
          inspection; acceptance disables and focuses the exact missing gate.
        - Verification passed: targeted Ruff and strict Python 3.12 MyPy, sole
          Alembic 009 head plus 1,147-line offline PostgreSQL SQL, deterministic
          OpenAPI export and generated-client drift checks, the focused and
          adjacent service/lesson-dossier/Manim/OpenAPI suites, the complete
          Metis web lint/typecheck and 198-unit-test suite, the Metis multimedia
          test and lint targets, 12 evidence self-checks, all 23 lesson-editor
          Chromium regressions, and all five teaching-media flows across
          Chromium/Firefox/WebKit. Browser proof includes 390 px reflow,
          reduced motion, exact worker receipts, acceptance/missing-critic
          focus, partial/failure/cancellation/retry/regeneration, sandboxed
          animation authoring, and automatic serious/critical axe analysis.
          Deterministic traceability is stored at
          `evidence/v1-workbenches/metis-teaching-media.json`.
        - Scope remains explicit: service database, browser APIs, and external
          render-worker transports are mocked; ten Manim environment-dependent
          cases skip when their optional renderer/toolchain is absent. Tests do
          not prove a production deployment, live migration, production media
          provider, representative educator usability, physical assistive-
          technology behavior, or formal accessibility conformance. A Next
          production build was not run under the 15 GiB host approval gate. A
          dependency-expanded multimedia build also remains blocked outside
          this scope by existing Iris/Metis type errors; its own tests and lint
          target pass, while the changed web and service surfaces pass targeted
          compilation and strict type analysis.
          -->
- [x] M3.18 Build the author→critic→media loop as an inspectable proposal
      workflow: lesson-to-scene plan, code/spec generation, sandbox render,
      visual/layout/correctness critique, revision, and educator acceptance.
      Evidence: the loop stops on budget, policy, renderer, or verification
      failure and never self-publishes.
  - [x] M3.18.a Define loop/iteration/agent/tool/plan/spec/render/critique/
        revision/proposal/decision contracts with exact versions and budgets.
        Evidence: workflow schemas.
  - [x] M3.18.b Convert exact lesson/objectives/sources/accessibility/target
        into an educator-reviewable scene/artifact plan before generation.
        Evidence: planning tests.
  - [x] M3.18.c Generate typed code/spec only through allowlisted tools/models
        with source/data binding, dependency locks, and fenced untrusted
        content. Evidence: generation tests.
  - [x] M3.18.d Render in a network/filesystem/resource/time-limited sandbox,
        retain logs/outputs/checksums, and stop on renderer/security failure.
        Evidence: sandbox tests.
  - [x] M3.18.e Run separate visual/layout/accessibility/correctness/source/
        technical critics with attributed findings, uncertainty, disagreement,
        and no approval authority. Evidence: critic tests.
  - [x] M3.18.f Produce bounded revisions linked to findings with maximum
        iterations/token/tool/compute/cost/time and explicit stop/timeout/
        abstention/partial state. Evidence: loop-control tests.
  - [x] M3.18.g Require educator inspect/accept/edit/reject for the exact final
        proposal and route it through normal gates; prohibit self-publication or
        self-waiver. Evidence: authority boundary tests.
        <!-- M3.18 implementation evidence (2026-08-31):
        - Added immutable v1 loop, project/lesson witness, target, six-axis
          budget/usage, policy-registry, scene-plan, typed-generation,
          sandbox-receipt, artifact, finding, six-critic, revision-patch,
          iteration, stop, unpublished-proposal, and educator-decision
          contracts. Agents pin provider/model/prompt/config versions and have
          no approval authority; tools are allowlisted with exact versions,
          configuration hashes, and dependency locks. Every plan, generation,
          receipt, finding, critique, revision, proposal, decision, and stored
          loop revision is independently content-addressed.
        - The loop is an append-only PostgreSQL and authenticated API control
          plane. Creation binds the exact current lesson dossier and teaching-
          media project revision; workers may submit only a pre-generation
          plan or bounded iteration receipt; educators alone approve/edit/
          reject the plan and accept/edit/reject the exact final proposal.
          Row locks, current-project rechecks, idempotency, authorization,
          monotonic cumulative usage, immutable history, and read-time digest
          verification fail closed on stale, cross-scope, replay, or tampered
          state.
        - Generation cannot start until the exact objective/source/
          accessibility/target-bound scene plan is approved. Typed source must
          use the admitted author/model/tool and exact dependency lock with
          untrusted input fenced. Sandbox receipts close network access, limit
          writes to the job directory, bind CPU/memory/timeout, and retain
          stdout/stderr, artifacts, worker versions, and checksums. Policy,
          renderer, verification, deadline, timeout, partial, abstention, and
          all iteration/token/tool/compute/cost/wall-time limits produce an
          explicit terminal receipt instead of autonomous retry.
        - Visual, layout, accessibility, correctness, source, and technical
          critics remain separately attributed and versioned. Findings retain
          exact artifact/source evidence, severity, confidence, and uncertainty;
          disagreement groups remain visible, and later iterations must link
          every prior non-passing finding. Convergence creates only an
          `unpublished_proposal` with self-publish, self-waiver, and normal
          acceptance-decision flags fixed false. Educator acceptance advances
          the exact proposal only to the existing normal teaching-media gates.
        - The frontend-skill-guided lesson editor adds a restrained proposal
          ledger beside normal media production: exact-witness creation,
          content-addressed revision rail, six budget meters, plan review,
          allowlisted policy margin, sandbox/log/source inspection, six critic
          columns, disagreement and stop receipts, and exact educator actions.
          It reflows at narrow widths, honors reduced motion, and keeps status,
          decision, and failure state available without color alone.
        - Verification passed: Ruff over the complete service source/tests,
          strict configured-Python MyPy for the new schema/helpers and strict
          Python 3.12 MyPy for all five changed service modules, 20 focused loop
          tests, 122 adjacent media/agent/render/OpenAPI tests with ten optional
          Manim environment skips, deterministic OpenAPI and generated-client
          drift checks, Metis web lint/typecheck and all 202 unit tests, all 30
          lesson-editor Chromium regressions, and all seven affected flows
          across Chromium/Firefox/WebKit (21/21). Every browser test also ran
          automatic serious/critical axe analysis. Migration 010 is the sole
          head and passed an isolated PostgreSQL 001→010 upgrade plus
          010→009→010 downgrade/upgrade cycle. Eleven evidence self-checks pass
          at `evidence/v1-workbenches/metis-media-loop.json`.
        - Scope remains explicit: database-service, browser-API, and external
          render-worker behavior uses deterministic fixtures; the migration
          exercise was isolated local PostgreSQL, not production. Repository-
          wide Alembic autogenerate still reports pre-existing metadata drift
          in older tables, while the new loop table reports no drift. Tests do
          not prove a production deployment/provider, representative educator
          usability, physical assistive-technology behavior, or formal
          accessibility conformance. A Next production build was not run under
          the less-than-32-GiB host approval gate; changed web and service
          surfaces pass their targeted compile, type, lint, unit, contract,
          migration, accessibility, and cross-browser gates.
          -->
- [x] M3.19 Build prerecorded lecture-package authoring for script, timing,
      presenter/avatar, pronunciation, voice, slides, diagrams, demonstrations,
      chapters, captions, transcript, audio description, localization,
      renditions, and learner preview. Evidence: media, consent, correctness,
      accessibility, and target-playback gates.
  - [x] M3.19.a Build structured
        script/segment/chapter/source/citation/objective editing with timing and
        dependency-aware revisions. Evidence: script authoring tests.
        <!-- evidence: metis-lecture-package.json .structuredScript; immutable
        source/objective/citation-bound segments and chapters, closed ordered
        dependency/timing validation, optimistic concurrency, append-only SQL
        revisions, and educator author/revise/history browser coverage. -->
  - [x] M3.19.b Build presenter/avatar/voice/language/pronunciation/performance
        selection with identity, consent, rights, disclosure, and expiry state.
        Evidence: presenter tests.
        <!-- evidence: .presenterGovernance; exact presenter/avatar/voice/locale/
        performance/pronunciation controls and content-addressed identity,
        consent, rights, disclosure, and expiry; structural gate tests prove
        expiry overrides nominally passing receipts. -->
  - [x] M3.19.c Build slides/diagrams/charts/code/demonstration cue authoring
        and exact media/source/data/code links synchronized to segments.
        Evidence: visual authoring tests.
        <!-- evidence: .synchronizedVisuals; all five cue kinds and all four
        exact link kinds carry locator/version/SHA-256 material, remain bounded
        to segment timing, and are inspected in authoring plus shared playback. -->
  - [x] M3.19.d Build captions/transcript/audio description/sign-language/
        translation review with speaker/time alignment and source-revision
        invalidation. Evidence: accessibility authoring tests.
        <!-- evidence: .accessibilityLocalization; all five human-reviewed
        track kinds bind source revision, speaker, source segment, and timing;
        stale/missing tracks fail closed and es-MX translation is exercised in
        the browser preview. -->
  - [x] M3.19.e Build video/audio/slide/text rendition configuration and
        multitrack learner preview across device/bandwidth/offline/locale modes.
        Evidence: rendition preview tests.
        <!-- evidence: .targetRenditions; all four rendition kinds bind exact
        locale/device/bandwidth/offline/locator/hash/track targets, every track
        locale requires offline low-bandwidth phone audio and screen-reader
        text, and the shared renderer exposes exact/fallback target state. -->
  - [x] M3.19.f Present media/consent/rights/source/correctness/accessibility/
        sync/technical/target gates and prevent package acceptance on stale/
        unavailable blockers. Evidence: gate tests.
        <!-- evidence: .acceptanceGates; nine closed gate domains, reproducible
        receipts/gate digest, structural consent/accessibility/target overrides,
        stale/expired/unavailable blocker tests, and disabled assembly until all
        nine current receipts pass in Playwright. -->
  - [x] M3.19.g Assemble/version/validate deterministic playback package and
        read it through the real learner renderer. Evidence: package E2E.
        <!-- evidence: .deterministicPlayback; exact authoring revision and gate
        bind the playback manifest, storage reads reverify body/index/lineage/
        manifest hashes, authenticated learner reads recheck current expiry,
        and educator preview plus course player use LecturePackageRenderer. -->
  <!-- completion evidence:
      - Deterministic generator and 11 self-checks:
        evidence/v1-workbenches/metis-lecture-package.json via
        pnpm run verify:inventory:v1-metis-lecture-package. It content-addresses
        migration/model/schema/helper/service/API/OpenAPI/generated-client/web/
        browser/TODO inputs and maps M3.19.a-g to executable evidence.
      - Backend: migration 011 creates append-only lecture_package_revisions;
        exact Pydantic contracts and lifecycle helpers cover structured ordered
        scripts, five cue kinds, five accessibility/localization tracks, four
        rendition kinds, presenter governance, nine acceptance gates, immutable
        revisions, deterministic assembly, and storage verification. Seven
        authenticated API operations expose catalog/create/read/revise/gate/
        assemble/learner-playback flows with exact witness, source, objective,
        idempotency, owner, and optimistic-concurrency enforcement.
      - Frontend: the restrained editorial lecture workbench is integrated into
        the lesson teaching-media surface; educators edit presenter/avatar/
        voice/performance/pronunciation/consent expiry, narration/timing,
        sign-language notes and reviewed translation, inspect visual/source
        links, immutable history and nine gate states, then assemble only when
        current. One responsive, reduced-motion-compatible renderer serves both
        educator target preview and the real course learner route across locale,
        device, bandwidth, offline, chapter, timeline, and track controls.
      - Verification passed: Ruff over complete Metis service source/tests;
        strict configured-Python MyPy on the new schema/helpers and strict
        Python 3.12 MyPy on all five changed service modules; 13 focused tests;
        118 adjacent media/avatar/prerecorded/OpenAPI tests with ten optional
        Manim environment skips; deterministic OpenAPI and generated Metis
        TypeScript drift checks; web lint with zero warnings, typecheck, and all
        207 unit tests in 56 files; all 32 lesson-editor Chromium regressions;
        and all three affected flows across Chromium/Firefox/WebKit (9/9).
        Browser automation includes automatic serious/critical axe analysis.
        Migration 011 is the sole head and passed isolated local PostgreSQL
        001→011 plus 011→010→011 downgrade/upgrade verification.
      - Scope remains explicit: database-service calls, browser API calls, and
        prerecorded asset locators use deterministic fixtures; the migration
        exercise was isolated local PostgreSQL, not production. Tests do not
        prove production deployment/media delivery, physical assistive-
        technology behavior, representative educator/learner usability, or
        formal accessibility conformance. A Next production build was not run
        under the less-than-32-GiB host approval gate; the affected service and
        web surfaces pass targeted compile, type, lint, unit, contract,
        migration, accessibility, regression, and cross-browser gates.
      -->
- [x] M3.20 Build interactive simulation/widget authoring with typed parameters,
      initial state, learning objective, expected observations, hints,
      assessment hooks, reset, deterministic seed, sandbox, telemetry, and
      non-interactive equivalent. Evidence: security and accessible-equivalence
      tests.
  - [x] M3.20.a Define registered simulation/widget/runtime version, objective,
        typed parameters/units/ranges/dependencies, initial state, deterministic
        seed, expected states/observations, and lifecycle. Evidence: schema
        tests.
        <!-- evidence: metis-simulation-authoring.json .registeredContract;
        registry 3.20 retains two exact legacy versions and publishes two
        current runtimes with closed objectives, typed units/ranges/dependency
        rules, seeds, state, observations, and seven-state lifecycle contracts. -->
  - [x] M3.20.b Build parameter/state/scenario/control/layout/instructions
        authoring with validation, presets, reset, and exact versioning.
        Evidence: authoring tests.
        <!-- evidence: .authoring; exact preset/runtime/scenario/control/layout
        identities, typed parameter and initial-state fields, deterministic seed,
        reset state, semantic instructions, telemetry controls, and authored
        equivalent operations are validated and persisted in immutable lesson-
        dossier revisions; Playwright exercises the complete edit write. -->
  - [x] M3.20.c Define hints/scaffolds and assessment hooks for events/state/
        observations/responses/scoring evidence without embedding answer keys in
        public config. Evidence: learning interaction tests.
        <!-- evidence: .learningInteraction; tiered scaffolds and finite hint,
        response, completion, and observation events bind public assessment-hook
        IDs/response schemas/scoring-evidence fields while answer material stays
        server-side and the public security contract explicitly denies keys. -->
  - [x] M3.20.d Execute preview/delivery in a versioned sandbox with resource/
        network/storage/permission/message limits, cancellation, and teardown.
        Evidence: security tests.
        <!-- evidence: .sandboxSecurity; author preview and learner delivery use
        one versioned renderer in an opaque cross-origin credentialless iframe
        with allow-scripts only, deny-by-default CSP/network/credentials/storage/
        parent-DOM policy, bounded runtime/memory/events/message sizes, host
        cancellation, teardown, and DOM removal verified in real browsers. -->
  - [x] M3.20.e Define privacy-safe typed telemetry events, sampling, learner/
        attempt context, offline buffering, consent/retention, and no arbitrary
        payloads. Evidence: telemetry tests.
        <!-- evidence: .telemetry; nine allowlisted event kinds have closed typed
        fields, pseudonymous learner and ephemeral attempt contexts, explicit
        consent and sampling, 14-day retention, and a drop-oldest offline buffer
        bounded to 64 events/32768 bytes/24 hours; schemas and units reject
        arbitrary payload containers and unconsented event collection. -->
  - [x] M3.20.f Author a noninteractive semantic equivalent with same objective/
        information/operation/assessment path and test outcome equivalence with
        assistive technology. Evidence: accessible-equivalence tests.
        <!-- evidence: .accessibleEquivalence; block validation requires exact
        objective, scenario information, operation-step IDs, response schema,
        assessment hooks, and observations to match the simulation. The shared
        learner renderer exposes ordered semantic text, labelled response,
        keyboard access, outcome witnesses, and screen-reader/text-only modes. -->
  <!-- completion evidence:
      - Deterministic generator and nine self-checks:
        evidence/v1-workbenches/metis-simulation-authoring.json via
        pnpm run verify:inventory:v1-metis-simulation-authoring. It content-
        addresses schemas/helpers/OpenAPI/generated-client/editor/renderer/unit/
        browser/TODO inputs and maps M3.20.a-f to executable evidence.
      - Backend: 100 focused service/API/OpenAPI tests pass across interaction,
        block, delivery-preview, dossier-service, authenticated API-contract, and
        generated-contract suites. Changed files pass Ruff and mypy; OpenAPI and
        TypeScript generated-client drift checks pass. Exact interaction config
        is stored through the existing append-only lesson-dossier revision path,
        so this extension requires no parallel table or migration.
      - Frontend: all 56 Vitest files/208 tests, ESLint, TypeScript, and changed-
        file Prettier checks pass. The restrained lesson inspector edits every
        registered authoring dimension, and the shared simulation component
        delivers the same security, telemetry, lifecycle, and semantic-
        equivalent contract in author preview and learner rehearsal.
      - Browser: all 33 lesson-editor and all nine learner-delivery Chromium
        tests pass serially, including repository automatic axe checks; the
        simulation sandbox, exact authoring write, and learner contract also pass
        a nine-test Chromium/Firefox/WebKit matrix. Tests cover typed scenarios/
        state/telemetry/equivalence, opaque sandbox headers and permissions,
        scaffolds/responses/observations/reset/cancel/teardown, no protected
        material, local-only equivalent response, and accessible outcome IDs.
      - Scope remains explicit: browser API transport and dossier persistence use
        deterministic fixtures while the actual cross-origin sandbox executes in
        real browser engines. This does not prove production deployment,
        representative learner usability, instructional validity, physical
        assistive-technology behavior, or formal accessibility conformance. A
        Next production build was not run under the less-than-32-GiB host
        approval gate; affected compile, type, lint, unit, contract, security,
        accessibility, regression, and cross-browser gates pass.
      -->
- [x] M3.21 Build media regeneration and surgical replacement by segment,
      figure, narration range, caption, translation, or rendition while
      retaining immutable prior artifacts and dependency invalidation. Evidence:
      unchanged segments keep stable identities and approvals are recomputed.
  - [x] M3.21.a Define replacement request with exact project/package/artifact
        revision, typed segment/figure/audio range/caption/translation/rendition
        target, requested change, reason, and expected version. Evidence:
        schema.
        <!-- evidence: metis-media-replacement.json .exactRequest; the closed
        target union binds one component to exact project, package, package
        revision/hash, specification hash, artifact ID/revision/hash, requested
        change, reason, and a bounded start/end witness for narration ranges. -->
  - [x] M3.21.b Resolve minimal affected source/spec/code/data/render stages and
        downstream dependency/invalidation graph before execution. Evidence:
        impact preview tests.
        <!-- evidence: .impactPreview; a read-only content-addressed preview
        resolves exact dependency edges, retained components, affected stages,
        gate domains, approval receipts, and prior playback before any revision
        is written. All six targets assert exact stage/gate sets and dependent
        counts in service tests, and Playwright inspects the educator preview. -->
  - [x] M3.21.c Regenerate/edit only the authorized target with compatible
        boundaries/timing/layout/style/voice/locale/format and new artifact
        identity. Evidence: surgical replacement tests.
        <!-- evidence: .surgicalReplacement; the service rejects zero, multiple,
        renamed, stale, cross-component, or boundary-drifting changes. Exactly
        one target changes, six compatibility locks remain true, and the new
        artifact receives a content-addressed identity at revision n+1. -->
  - [x] M3.21.d Retain unchanged component identities/hashes/revisions and all
        prior artifacts; never splice over immutable package history. Evidence:
        stability tests.
        <!-- evidence: .immutableRetention; unchanged manifest entries and gate
        receipts remain byte-identical, replacement/acceptance/gate/assembly
        actions append hash-chained package revisions, and the complete prior
        artifact plus playback witnesses remain inspectable in history. -->
  - [x] M3.21.e Recompute affected source/correctness/consent/rights/
        accessibility/technical/critic gates and mark dependent approvals/builds
        stale precisely. Evidence: invalidation tests.
        <!-- evidence: .preciseRegating; only target-dependent receipt domains
        become stale, unaffected consent/rights/source/correctness/media gates
        remain current when their inputs are unchanged, affected receipt IDs are
        audited, and gate execution is blocked until explicit acceptance. Tests
        prove unaffected receipts survive byte-for-byte while affected receipts
        are recomputed against the replacement specification. -->
  - [x] M3.21.f Assemble a new package revision, compare old/new components,
        require educator/reviewer acceptance, and verify learner playback.
        Evidence: replacement E2E.
        <!-- evidence: .acceptanceAndPlayback; educator/admin acceptance is an
        attributed immutable revision, the workbench compares before/after
        artifact revisions and preserved history, learner lookup serves the last
        accepted package throughout review, and accepted/regated media assembles
        a distinct learner package containing the replacement caption. -->
  <!-- completion evidence:
      - Deterministic generator and nine self-checks:
        evidence/v1-workbenches/metis-media-replacement.json via
        pnpm run verify:inventory:v1-metis-media-replacement. It content-addresses
        schemas/helpers/services/API/OpenAPI/generated-client/workbench/unit/
        browser/TODO inputs and maps M3.21.a-f to executable evidence.
      - Backend: 112 affected teaching-media, media-loop, prerecorded-lecture,
        lecture-authoring, API-contract, and OpenAPI tests pass. Changed files
        pass Ruff and four focused mypy source checks; live/checked-in OpenAPI
        and generated TypeScript client drift checks pass. Package revisions
        reuse the append-only hash-chained JSON body with no parallel payload
        table; forward migration 012 expands the ledger's database state/action
        constraints for `replacement_review`, `replace`, and
        `accept_replacement`.
      - Frontend: all 56 Vitest files/209 tests, ESLint with zero warnings,
        TypeScript, and changed-file Prettier checks pass. The restrained
        workbench exposes exact target/artifact selection, read-only blast radius,
        immutable before/after comparison, explicit acceptance, precise regating,
        and preserved revision inspection without exposing protected material.
      - Browser: all 34 lesson-editor and all nine learner-delivery Chromium
        tests pass serially with repository automatic axe checks. Authoring,
        assembly, and replacement also pass a nine-test Chromium/Firefox/WebKit
        matrix. The replacement E2E proves exact witnesses, four caption-
        dependent stale gates, disabled pre-acceptance regating, retained r3
        playback during review, accepted recomputation, new r7 learner playback,
        and the replacement caption without answer/secret leakage.
      - Scope remains explicit: browser API transport and service persistence use
        deterministic fixtures/repositories. This does not prove production
        deployment, representative educator or learner usability, physical
        assistive-technology behavior, or formal accessibility conformance. A
        Next production build was not run under the less-than-32-GiB host
        approval gate; affected compile, type, lint, unit, contract, security,
        accessibility, regression, and cross-browser gates pass.
      -->

### M4 — Assessment and item-bank authoring

- [x] M4.1 Build item-bank search by kind, objective, standard, difficulty,
      discrimination, exposure, locale, status, author, gate, and usage.
      Evidence: permission-aware query tests.
  - [x] M4.1.a Define searchable exact item-revision projection and allowlisted
        filters/sorts/facets for every named dimension. Evidence: search schema.
  - [x] M4.1.b Index answer-safe metadata with tenant/course/role/exposure field
        controls and explicit stale/unknown psychometric state. Evidence: index
        tests.
  - [x] M4.1.c Implement deterministic cursor pagination, query limits, stable
        ranking, and permission-safe counts/suggestions. Evidence: query tests.
  - [x] M4.1.d Present result/version/use/gate/rights/locale/accessibility state
        and authorized preview without key/rationale leakage. Evidence: UI
        tests.
  - [x] M4.1.e Test direct foreign IDs, hidden filters, facets/counts/timing,
        protected fields, and two tenants. Evidence: authorization suite.
      <!-- M4.1 completion evidence (2026-08-31):
      - Reproducible provenance: `evidence/v1-workbenches/
        metis-item-bank-search.json`, generated and drift-checked by
        `pnpm run verify:inventory:v1-metis-item-bank-search`, maps M4.1.a-e to
        executable source, schema, contract, authorization, and browser evidence.
      - Projection and persistence: migration `013` adds an append-only exact
        `(item_id, revision)` search projection with content-addressed lineage,
        a private source-change witness, explicit tenant/course/author/use state,
        and no answer, key, rationale, option, explanation, or hint columns.
        Unknown/stale/current discrimination and exposure value semantics are
        enforced in both Pydantic and PostgreSQL constraints. Legacy assessment
        question mutations, lifecycle changes, and retirement append projection
        revisions instead of rewriting history.
      - Query and authorization: authenticated instructor/admin search supports
        every named M4.1 dimension through closed request models, six stable
        total-order sorts, thirteen permission-safe facets, bounded suggestions,
        a 50-row page cap, a 120-character query cap, and a 5,000-document scope
        cap. HMAC cursors bind offset, normalized query, role, tenant, and the
        exact role-visible snapshot. Latest revision is resolved before role
        visibility so a newly hidden revision cannot fall back to an older visible
        one. Foreign-tenant changes do not perturb local snapshots or counts.
      - Direct lookup and leakage boundary: exact preview returns one generic 404
        contract for foreign, admin-only, and missing revisions and traverses the
        same complete bounded tenant snapshot for each case. Search, facets,
        counts, suggestions, preview, OpenAPI, generated clients, and the browser
        response guard exclude protected fields; exact exposure remains
        administrator-controlled.
      - Workbench: following `frontend-skill`, the instructor surface uses a
        restrained warm-paper editorial register rather than a generic card grid.
        It exposes all named search dimensions, permission policy, safe facets and
        suggestions, exact version/use/gate/rights/locale/accessibility state,
        signed-page continuation, learner denial, responsive reflow, and an
        answer-safe exact-revision preview.
      - Automated verification: 61 focused item-bank/OpenAPI service tests and 18
        assessment-service regression tests pass together (79/79); Ruff and
        focused Python 3.12 mypy pass; checked-in OpenAPI and generated TypeScript
        drift checks pass; all 57 Metis web unit files (214 tests), full web lint,
        web typecheck, and changed-file Prettier checks pass. Twelve item-bank
        Playwright flows pass serially across Chromium, Firefox, and WebKit with
        the repository automatic serious/critical Axe scan, including all filters,
        usage facet application, exact safe preview, signed pagination, learner
        denial before transport, and 390px no-overflow behavior.
      - Live PostgreSQL: a clean upgrade through `013`, a real `012 -> 013` legacy
        backfill containing a private answer/rationale/hint, both semantic
        constraint rejection probes, and an answer-only update/appended revision-2
        search smoke pass. The projected prompt is normalized correctly, protected
        projection-column count is zero, and only safe latest revision 2 is
        serialized.
      - Scope remains explicit: browser API transport uses deterministic fixtures.
        This does not prove production deployment, representative educator
        usability, physical assistive-technology behavior, or formal accessibility
        conformance. A Next production build was not run under the less-than-32-GiB
        host approval gate; affected compile, type, lint, unit, contract, database,
        security, accessibility, responsive, and cross-browser gates pass.
      -->
- [x] M4.2 Build zero-form item capture plus structured completion. Evidence:
      drafts retain provenance and validation state.
  - [x] M4.2.a Define minimal capture for prompt/idea/source/objective/item-kind
        hint/author/context with offline/local draft identity. Evidence: capture
        schema.
  - [x] M4.2.b Create a canonical draft revision with author/source/import/AI
        provenance and explicit incomplete/unvalidated status. Evidence:
        creation tests.
  - [x] M4.2.c Infer no protected answer or item semantics automatically; route
        the draft into kind-specific structured fields and requirements.
        Evidence: boundary tests.
  - [x] M4.2.d Show completion checklist for
        stem/response/key/scoring/rationale/
        objective/accessibility/source/review/gates. Evidence: completion tests.
  - [x] M4.2.e Preserve original capture and every structured edit/validation
        result through revision history. Evidence: provenance tests.
      <!-- M4.2 completion evidence (2026-08-31):
      - Reproducible provenance: `evidence/v1-workbenches/
        metis-item-draft-capture.json`, generated and drift-checked by
        `pnpm run verify:inventory:v1-metis-item-draft-capture`, maps M4.2.a-e
        to executable schema, persistence, API, authorization, offline, browser,
        and live-database evidence. All eight generator self-checks pass and
        every input is content-addressed.
      - Minimal capture and offline identity: the closed `4.2.0` request requires
        only `local_draft_id`; prompt, idea, source references, objective IDs,
        explicit canonical item-kind hint, and context remain independently
        optional. The instructor workbench writes an actor-bound
        `local-item-draft:<UUID>` recovery record before transport, preserves
        exact literal text, retains unsynchronized edits, and replays the same
        identity and capture after reconnect. Local identity reuse with different
        content fails with an explicit 409 rather than overwriting the original.
      - Canonical provenance and storage: migration `014` creates one immutable
        original-capture/provenance root and a separate append-only structured
        revision ledger. Author identity is derived from authentication;
        source/import/AI provenance is typed and explicit; capture and provenance
        have independent SHA-256 witnesses. Revision 1 is explicitly
        `incomplete` and `unvalidated`, every revision has request and content
        witnesses, and PostgreSQL constraints enforce contiguous lineage,
        idempotency, event, status, and validation state.
      - Protected boundary and routing: capture and structured OpenAPI schemas,
        ORM columns, generated clients, service responses, local storage, and UI
        transport contain no answer, key, option, explanation, hint, or rationale
        payload property. Initial structure copies only the literal prompt,
        objectives, and sources. Wording such as “multiple choice” or “correct
        answer” does not select a kind or populate response/scoring/protected
        fields; only an explicit hint routes to the canonical selected-response,
        written-response, executable-response, interactive-response, or
        performance-evidence family, with `inference_used=false`.
      - Structured completion and revision history: the workbench renders the
        exact ten-field stem/response/key/scoring/rationale/objective/
        accessibility/source/review/gates checklist. Unknown kinds block response,
        scoring, key, and rationale; explicit kinds expose answer-safe response
        and scoring fields while key/rationale stay blocked for their protected
        kind editor (or key becomes not applicable where the registry says so).
        Structured edits reset validation to unvalidated; explicit validation
        appends its exact checklist and findings. Reads reverify original,
        provenance, revision hashes, and the complete contiguous chain.
      - Database immutability: the live clean migration chain was paused at `013`
        (both draft tables absent), upgraded to `014`, and exposed both expected
        tables plus two mutation-rejection triggers. Real async PostgreSQL capture,
        structured edit, and validation commits produced three linked revisions;
        direct capture UPDATE and revision DELETE probes were rejected by the
        database trigger. The checked-in migration downgrade/upgrade cycle and
        head `014` pass.
      - Automated verification: all 16 focused M4.2 backend tests and the complete
        80-test affected M4.1/M4.2/OpenAPI service set pass; checked-in OpenAPI and
        generated Metis TypeScript client drift checks pass. Ruff passes 12
        affected Python/migration/test files and strict mypy passes five source
        files. All 58 Metis web unit files (220 tests), full ESLint with zero
        warnings, and TypeScript pass. Twelve capture/completion Playwright flows
        pass serially across Chromium, Firefox, and WebKit with the repository
        automatic serious/critical Axe scan, covering zero-form creation,
        structured revision and validation, answer-like prose non-inference,
        offline exact-identity replay, learner denial before transport, revision
        history, and 390px no-overflow behavior.
        The adjacent twelve-flow item-bank search matrix also passes across all
        three engines after capture navigation was added.
      - Workbench design: following `frontend-skill`, the new surface extends the
        restrained warm-paper assessment studio with an editorial capture ledger,
        immutable witness, structured writing column, sticky completion register,
        protected-boundary notice, and revision folio rather than a generic card
        dashboard. Search and course-studio navigation link the capture flow.
      - Scope remains explicit: browser API transport uses deterministic fixtures.
        This does not prove production deployment, representative educator
        usability, physical assistive-technology behavior, or formal accessibility
        conformance. A Next production build was not run under the less-than-32-GiB
        host approval gate; affected compile, type, lint, unit, contract, database,
        security, accessibility, offline, responsive, and cross-browser gates pass.
      -->
- [x] M4.3 Build item-variant generation behind the fenced provider seam.
      Evidence: `not_configured`, malformed, refusal, and budget states.
  - [x] M4.3.a Define variant request/set/proposal with exact source item,
        permitted transformations, invariants, count/diversity, provider
        provenance, estimate, and budget. Evidence: schemas.
  - [x] M4.3.b Fence source/stem/objectives/rubric/allowed key context and
        exclude learner/integrity/unrelated bank data from provider input.
        Evidence: context tests.
  - [x] M4.3.c Validate generated
        kind/schema/key/rationale/objective/difficulty/
        locale/accessibility/source and similarity constraints. Evidence:
        proposal validation tests.
  - [x] M4.3.d Preserve `not_configured`, refusal, malformed, timeout, partial,
        moderation, and budget outcomes without fallback content. Evidence: seam
        tests.
  - [x] M4.3.e Require per-variant human accept/edit/reject and create new
        attributed item revisions; no automatic bank admission. Evidence: review
        tests.
      <!-- M4.3 completion evidence (2026-08-31):
      - Reproducible provenance: `evidence/v1-workbenches/
        metis-item-variant-generation.json`, generated and drift-checked by
        `pnpm run verify:inventory:v1-metis-item-variant-generation`, maps
        M4.3.a-e to executable contracts, provider fencing, validation,
        persistence, authorization, browser, and live-database evidence. All
        eight generator self-checks pass and every input is content-addressed.
      - Exact request, estimate, and provenance: closed `4.3.0` schemas bind one
        content-addressed source draft revision to an explicit transformation
        set, hard invariants, one-to-six count, exact diversity specifications,
        difficulty/locale targets, server-modeled estimate, educator-confirmed
        maximum budget, and immutable provider/model/config/prompt/request/
        response/token/cost/trust-zone witnesses. Preparation records its exact
        count and total estimate; count changes refetch preparation, while stale
        estimate or pricing witnesses fail with an explicit 409 before model
        invocation. The provider is disabled unless explicitly configured.
      - Provider fence: the only provider-visible product context is the exact
        source kind/stem/response/scoring/objectives/difficulty/locale/
        accessibility/source surface plus optional server-authorized key context,
        confirmed controls, and educator guidance. Each surface is rendered as a
        labeled untrusted non-instruction zone; tools and browsing are absent.
        Learner data, integrity telemetry, unrelated bank content, credentials,
        and tools are enumerated exclusions. Deterministic HTTP transport tests
        prove learner-secret and unrelated-bank sentinels never enter the request.
      - Item validation: Pydantic rejects extra or malformed provider properties;
        the service independently revalidates kind, ordered objectives, protected
        key/rationale authorization, scoring, source references, difficulty,
        locale, required accessibility text, transformation/diversity/count, and
        moderation. Source and pairwise Jaccard similarity are recomputed locally;
        kind, key, objective, locale, count, source-copy, and similarity drift
        probes fail closed.
      - Explicit outcomes without fallback: `not_configured`, `unavailable`,
        refusal, malformed envelope/payload, timeout, partial output, moderation
        block, and preflight/actual budget-exceeded outcomes are typed and stored
        with zero silent candidates for terminal failures. Partial content is a
        bounded subset, never padded. The browser executes and labels all eight
        terminal states while retaining invocation cost, tool state, and receipt.
      - Human review and storage: migration `015` separates immutable generation
        sets, unaccepted proposals, and one append-only decision per proposal.
        Accept, human edit, and reject are distinct closed requests with a required
        human reason. Accept/edit create a content-addressed revision-1 record
        attributed to source draft/revision, proposal, provider/model, and actor;
        reject creates none. Every accepted revision is explicitly
        `not_admitted`; no item-bank projection write exists in the migration or
        service. PostgreSQL checks bind action/edit/revision semantics.
      - Live PostgreSQL: a clean chain paused at `014`, upgraded to `015`,
        downgraded, and upgraded again. Real async capture/edit, exact preparation,
        generation, proposal, and accept commits passed; the item-bank projection
        count remained unchanged. Direct set UPDATE, proposal DELETE, and decision
        UPDATE probes were rejected by all three immutability triggers. The
        disposable test database was removed afterward.
      - Automated verification: all 12 focused M4.3 tests including live
        PostgreSQL pass, and the complete 92-test affected M4.1/M4.2/M4.3/OpenAPI
        service set passes. Checked-in OpenAPI and generated Metis TypeScript
        client drift checks pass. Ruff passes all 16 changed Python/migration/test
        files and strict mypy passes five new source modules. All 59 Metis web unit
        files (222 tests), full ESLint with zero warnings, and TypeScript pass.
        Nine item-variant Playwright flows pass serially across Chromium, Firefox,
        and WebKit with the repository automatic serious/critical Axe scan,
        covering exact count/source/budget binding, accept/edit/reject, all
        provider outcomes, learner denial before transport, and 390px no-overflow.
        The adjacent twelve-flow item-draft matrix also passes across all three
        engines after variant navigation was added.
      - Workbench design: following `frontend-skill`, the educator-only surface
        uses an editorial assessment atelier: warm-paper planning controls beside
        a dark exact-source folio, a compact source witness, explicit exclusions
        and estimate, and a numbered proposal ledger rather than generic cards.
        Every proposal keeps its own rationale and decision controls, and accepted
        results visibly remain “not admitted.”
      - Scope remains explicit: provider HTTP and browser API transport use
        deterministic doubles; no production model credential was invoked. This
        does not prove production deployment, representative educator usability,
        physical assistive-technology behavior, or formal accessibility
        conformance. A Next production build was not run under the less-than-32-
        GiB host approval gate; affected compile, type, lint, unit, contract,
        database, security, accessibility, responsive, and cross-browser gates
        pass.
      -->
- [x] M4.4 Build selected-response editor with keyed answer and distractor
      rationales. Evidence: answer uniqueness and protected-field tests.
  - [x] M4.4.a Author stem/instructions/options with stable option IDs, rich
        accessible content, locale variants, ordering/randomization, and source
        refs. Evidence: editor tests.
  - [x] M4.4.b Configure single/multiple/ordered/matching response rules and
        exact key/partial-credit scoring with uniqueness/cardinality validation.
        Evidence: answer tests.
  - [x] M4.4.c Author correct rationale and each distractor’s misconception/
        rationale separately from learner feedback. Evidence: rationale tests.
  - [x] M4.4.d Preview author/key/learner modes with randomized order preserving
        stable key identity and accessibility. Evidence: preview tests.
  - [x] M4.4.e Enforce key/rationale property authorization across
        API/UI/search/ export/log/tutor and direct crafted payloads. Evidence:
        protected-field tests.
      <!-- M4.4 completion evidence (2026-08-31):
      - Canonical contracts and scoring: schema `4.4.0` closes every request,
        localized rich-content surface, stable `option:<UUID>` and
        `target:<UUID>` identity, source reference, randomization/fixed-position
        rule, and protected key/rationale object. Conditional validators bind
        single, multiple, ordered, and one-to-one matching keys to unique known
        IDs and valid cardinality. Exact scoring plus bounded multiple
        per-correct-less-incorrect, ordered per-position, and matching per-pair
        partial credit is computed server-side from stable IDs. Tests execute all
        four kinds, incorrect cardinality/foreign identity, locale completeness,
        duplicate identity, and closed crafted-property rejection.
      - Feedback and rationale boundary: learner feedback remains an option-local
        localized surface. Correct rationale and each distractor misconception/
        localized rationale live in separate protected properties; choice items
        require one review for every and only non-key option. The TypeScript
        builder and browser assertions prove neither representation absorbs the
        other. Ordered and matching items reject distractor-review payloads.
      - Role and downstream projections: author, key, and learner previews are
        distinct discriminated response models. Only key mode can contain the
        response rule, correct rationale, or distractor reviews; learner mode
        omits feedback before submission and structurally has no protected
        fields. Randomization derives only from revision witness, attempt seed,
        and stable identity, while unique first/last positions stay fixed. A
        separate closed projection for learner, search, safe export, log, and
        tutor surfaces enumerates excluded properties and cannot serialize key or
        rationale. Protected export is a separate explicitly labeled endpoint.
      - Authorization and immutable persistence: all preparation, full-history,
        append, preview, scoring, and protected-export HTTP paths require the
        instructor/admin dependency before service construction. Parameterized
        API tests send learner requests to all seven paths, including malformed
        crafted answer-key bodies, and prove 403 occurs before service access.
        Migration `016` stores full authoring revisions behind exact draft and
        optimistic editor witnesses, content/request/revision hashes, contiguous
        lineage, role constraints, idempotency, and a database UPDATE/DELETE
        rejection trigger. It never writes the item-bank search projection.
      - Live PostgreSQL: a clean chain upgraded through `015`, paused and upgraded
        to `016`, downgraded to `015`, and re-upgraded to head. Real async
        capture/edit/prepare/append/preview/project/score commits passed. Learner
        and key modes retained identical stable IDs; five safe projections were
        key-free; item-bank projection count was unchanged; direct authoring-row
        UPDATE and DELETE probes were rejected. The disposable database was
        removed afterward.
      - Automated verification: all 27 focused M4.4 tests including live
        PostgreSQL pass, and the complete 119-test affected M4.1–M4.4/OpenAPI
        service set passes. Checked-in OpenAPI and generated Metis TypeScript
        client drift checks pass. Ruff passes the 12 focused Python/migration/test
        files and strict mypy passes the four new source modules. All 60 Metis web
        unit files (228 tests), full ESLint with zero warnings, and TypeScript
        pass. Nine selected-response Playwright flows pass serially across
        Chromium, Firefox, and WebKit with the repository automatic serious/
        critical Axe scan, covering complete authoring, feedback/rationale
        separation, bilingual completeness, stable-key role previews, learner
        denial before transport, deterministic randomization, and 390px
        no-overflow. The adjacent twelve-flow capture matrix also passes across
        all three engines after the workbench link was added.
      - Workbench design: following `frontend-skill`, the educator-only page uses
        a restrained editorial composition: warm-paper numbered authoring ledger,
        compact exact-source/locale bands, visually fenced protected reviews,
        and one sticky dark role-preview rail rather than generic card grids.
        Matching pairs can be remapped independently, ordered keys can be moved,
        fixed positions are explicit, and every preview exposes the short stable
        identity used across randomization.
      - Generated evidence:
        `evidence/v1-workbenches/metis-selected-response-authoring.json` is
        content-addressed over implementation, tests, generated contracts, UI,
        TODO, and evidence producer/test inputs; its drift and semantic tests
        pass. Scope remains explicit: browser transport uses deterministic
        doubles. This does not prove production deployment, representative
        educator usability, physical assistive-technology behavior, or formal
        accessibility conformance. A Next production build was not run under the
        less-than-32-GiB host approval gate; affected compile, type, lint, unit,
        contract, database, security, accessibility, responsive, and
        cross-browser gates pass.
      -->
- [x] M4.5 Build constructed-response and essay editor with scoring guide,
      exemplar, acceptable variants, and moderation requirements. Evidence:
      contract tests.
  - [x] M4.5.a Author prompt/context/response format, length/language/media/
        citation constraints, instructions, sources, and accessibility.
        Evidence: prompt tests.
  - [x] M4.5.b Define acceptable answer variants/elements/tolerances and
        unacceptable/missing evidence without reducing essays to hidden exact
        strings. Evidence: response model tests.
  - [x] M4.5.c Attach rubric/scoring guide revision, criterion mappings,
        partial/ holistic method, and human/model-assisted scoring bounds.
        Evidence: scoring tests.
  - [x] M4.5.d Attach authorized annotated exemplars at performance levels with
        rights/redaction and protected learner identity. Evidence: exemplar
        tests.
  - [x] M4.5.e Define first/second marking, blind review, discrepancy
        thresholds, moderation/reconciliation, and audit. Evidence: moderation
        tests.
      <!-- completion evidence (2026-08-31):
      - Contract and semantic boundary: schema `4.5.0` admits only constructed
        response and essay kinds. Localized rich prompt, context, instructions,
        editor labels, format guidance, and media-alternative guidance are
        complete across every authored locale. Response formats, character/word
        bounds, languages, document MIME/byte limits, learner media permission,
        citation policy/minimum/styles, governed source references, keyboard-only
        support, source-description verification, and assistive-technology review
        are closed and cross-validated.
      - No hidden essay key: the protected response model is explicitly based on
        stable evidence elements, acceptable semantic variants and tolerance
        guidance, plus both unacceptable and missing-evidence boundaries. The
        schema has no exact-answer property, carries
        `hidden_exact_strings_prohibited: true`, rejects crafted extra answer
        fields, unknown element references, and non-semantic essay tolerances.
      - Scoring: exact rubric ID/revision/SHA-256 witnesses and criterion-to-element
        mappings support analytic partial or holistic judgment. Analytic points
        must sum to the maximum; holistic mappings cannot smuggle point keys.
        Human final judgment is mandatory. Model help is disabled or advisory,
        cannot finalize, is capped at 3,000 basis points, and requires explicit
        confidence and evaluation-profile bounds.
      - Exemplars and identity: at least two distinct performance levels require
        localized response content, criterion annotations, explicit rights basis,
        holder, license/permission reference, authorizer and timestamp. Verified
        direct/indirect redaction and literal absence of protected learner
        identity fail closed in both schema and browser payload tests.
      - Marking and moderation: first marking is required; configured second
        marking requires at least two blind markers and a positive discrepancy
        threshold. The executable moderation planner returns awaiting, complete,
        or reconciliation-required states, computes basis-point discrepancy,
        routes to lead/third/consensus reconciliation, and enumerates append-only
        first/second mark, model recommendation, human override, reconciliation,
        and final-decision audit events.
      - Protected surfaces and authorization: author, marker, and learner previews
        are discriminated. Learner plus search, safe export, log, and tutor
        projections structurally omit answer models, scoring, criteria, exemplars,
        moderation, model assistance, and marker identity. Protected export is a
        separate labeled route. All seven preparation/history/append/preview/
        projection/export/moderation HTTP paths require instructor/admin authority
        before service construction; parameterized crafted learner requests prove
        403 occurs before protected service access.
      - Persistence and live PostgreSQL: migration `017`, chained from `016`,
        stores complete authoring revisions with exact source/editor witnesses,
        content/request/revision hashes, contiguous lineage, role constraints,
        idempotency, and an UPDATE/DELETE rejection trigger. A disposable database
        passed clean 001→016→017, 017→016, and re-upgrade-to-head paths. Real async
        capture/edit/prepare/append/author-marker-learner preview/five safe
        projections/moderation commits passed; item-bank search count was
        unchanged; direct UPDATE and DELETE were rejected. The test-only database
        was removed afterward.
      - Automated verification: all 26 focused M4.5 tests including live
        PostgreSQL pass; the consolidated affected M4.1–M4.5/OpenAPI service set
        passes 145 tests. Checked-in OpenAPI and generated Metis TypeScript client
        drift checks pass. Ruff passes 14 focused Python/migration/test files and
        strict mypy passes the four new source modules. All 61 Metis web unit files
        (232 tests), full ESLint with zero warnings, and TypeScript pass. Nine
        written-response Playwright flows pass serially across Chromium, Firefox,
        and WebKit with the repository automatic serious/critical Axe scan,
        covering complete authoring, semantic-no-exact-key payloads, scoring/model
        bounds, rights/redaction, blind moderation, bilingual completeness,
        author/marker/learner field separation, learner denial before transport,
        and 390px no-overflow. The adjacent twelve-flow capture matrix also passes
        across all engines after adding the routed workbench link.
      - Workbench design: following `frontend-skill`, the educator-only surface
        extends the restrained assessment editorial system: exact-source strip,
        dark locale ledger, numbered paper authoring sequence, protected semantic/
        model/exemplar insets, and one sticky dark role-preview rail. The layout
        avoids generic dashboard cards and reuses the established responsive and
        accessible selected-response visual language for a coherent suite.
      - Generated evidence:
        `evidence/v1-workbenches/metis-written-response-authoring.json` is
        content-addressed over implementation, tests, generated contracts, UI,
        TODO, and its producer/test inputs; its drift and semantic tests pass.
        Scope remains explicit: browser API transport uses deterministic doubles.
        This does not prove production deployment, representative educator
        usability, physical assistive-technology behavior, or formal accessibility
        conformance. A Next production build was not run under the less-than-32-GiB
        host approval gate; affected compile, type, lint, unit, contract, database,
        security, accessibility, responsive, and cross-browser gates pass.
      -->
- [x] M4.6 Build math and code item editors with executable/renderer sandboxes.
      Evidence: resource, network, and escape tests.
  - [x] M4.6.a Author semantic MathML/symbolic/numeric/unit/graph/proof response
        models with equivalence/tolerance/assumptions and accessible rendering.
        Evidence: math item tests.
  - [x] M4.6.b Author code language/runtime/dependencies/starter files/API,
        stdin/arguments/output, visible/hidden tests, scoring, and limits.
        Evidence: code item tests.
  - [x] M4.6.c Pin renderer/compiler/interpreter/CAS/test harness/container/
        dependency versions and deterministic seeds. Evidence: runtime
        contracts.
  - [x] M4.6.d Execute author preview in isolated CPU/memory/time/process/file/
        network/syscall-limited sandbox with teardown. Evidence: sandbox tests.
  - [x] M4.6.e Protect hidden tests/answers/runtime secrets and test infinite
        loops, fork/memory bombs, filesystem/network/escape, and malicious
        output. Evidence: adversarial suite.
  <!-- M4.6 completion evidence (2026-08-31):

       - Contracts and access boundaries: Metis now owns a strict `4.6.0`
         discriminated contract for all six requested math response forms and
         executable code. Math records semantic protected answers with verified
         SHA-256 witnesses, versioned equivalence rules, assumptions, explicit
         tolerances, graph/proof constraints, localized accessible alternatives,
         exact renderer/graph/CAS pins, and an opaque no-script renderer policy.
         Code records exact language/runtime, optional compiler, preinstalled
         dependency integrity, starter files, invocation/API/I/O, visible and
         hidden tests, partial scoring, resource policy, and runtime-secret names.
         Compiler is explicitly not applicable for the one admitted interpreted
         Python runtime. Instructor/admin authorization precedes all eight routes;
         learner/search/export/log/tutor payload types structurally omit canonical
         answers, hidden tests, and runtime secrets, while a separate protected
         export retains them for authorized authoring and evaluation.
       - Execution isolation: author preview admits only CPython 3.12.14 in
         `python@sha256:d09d15e60962ca365d1cd544a48773bac9d33f2fb1b00f2aa0deec78ade7dc31`,
         with a byte-verified checked-in launcher and default-deny seccomp policy.
         Docker is invoked with pulls disabled, no network, read-only root and
         submission mounts, an isolated bounded tmpfs, unprivileged UID/GID,
         every capability dropped, no-new-privileges, process/memory/CPU/wall/
         output/open-file bounds, deterministic hash seed, no secret forwarding,
         one disposable container per visible case, forced removal, and verified
         absence after teardown. Authored image/profile/tool substitution is
         rejected before process launch.
       - Adversarial and persistence evidence: ten live Docker cases pass for
         normal exact-image execution, no secret inheritance, network and host
         filesystem denial, namespace/syscall denial, writable-file and process
         bounds, infinite loop, memory bomb, output bomb, malicious ANSI/control/
         HTML output neutralization, and teardown. Migration `018`, chained from
         `017`, passed clean 001→018, 018→017, and re-upgrade paths on disposable
         PostgreSQL; full protected state persisted, automatic item-bank search
         writes stayed at zero, and direct UPDATE/DELETE were rejected. Test-only
         databases were removed after verification.
       - Automated verification: all 44 focused M4.6 service/API/migration/live
         PostgreSQL/live Docker tests pass; the consolidated affected M4.1–M4.6
         and OpenAPI service set passes 186 tests. Checked-in OpenAPI and generated
         Metis TypeScript client drift checks pass. Focused Ruff covers 17 Python/
         migration/test/runtime files and strict mypy covers five source modules.
         All 62 Metis web unit files (244 tests), full ESLint with zero warnings,
         and TypeScript pass. Nine math/code Playwright flows pass serially across
         Chromium, Firefox, and WebKit with automatic serious/critical Axe analysis
         on the parent workbench, covering exact runtime/test/scoring payloads,
         protected execution requests, proof semantics, opaque MathML CSP/sandbox/
         role/name contracts, instructor-only transport, bilingual completeness,
         saved-revision rehydration, and 390px no-overflow. The iframe intentionally
         rejects script injection, so Axe cannot enter it; its security and
         accessibility contract is asserted directly. The adjacent twelve-flow
         capture matrix also passes after adding the routed workbench link.
       - Workbench design: following `frontend-skill`, the educator-only surface
         extends the restrained assessment editorial system with an exact-source
         strip, dark locale ledger, numbered paper authoring sequence, protected
         hidden-suite inset, pinned-runtime and sandbox controls, semantic learner/
         author preview, and one sticky execution-receipt rail rather than a dense
         generic dashboard.
       - Generated evidence:
         `evidence/v1-workbenches/metis-math-code-authoring.json` is
         content-addressed over implementation, checked-in runtime policy, tests,
         generated contracts, UI, TODO, and its producer/test inputs; drift and
         semantic tests pass. Browser API transport uses deterministic doubles.
         This does not prove production deployment, representative educator
         usability, physical assistive-technology behavior, formal accessibility
         conformance, or production evaluation-engine calibration. A Next
         production build was not run under the less-than-32-GiB host approval
         gate; affected compile, type, lint, unit, contract, database, security,
         accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M4.7 Build media and interactive item editor with accessible alternatives.
      Evidence: QTI export/import and accommodation preview.
  - [x] M4.7.a Author governed image/audio/video/3D/stimulus media revisions,
        interactions, response regions/timing/state, scoring hooks, and sources.
        Evidence: media item tests.
  - [x] M4.7.b Author captions/transcripts/descriptions/sign alternatives/
        keyboard paths/noninteractive equivalents and support requirements.
        Evidence: accessibility tests.
  - [x] M4.7.c Configure sandbox runtime, typed
        events/state/reset/seed/resource/ network limits, and secure response
        capture. Evidence: interactive tests.
  - [x] M4.7.d Preview permitted accommodation/locale/device/input modes with
        the real delivery renderer. Evidence: accommodation preview matrix.
  - [x] M4.7.e Map/import/export through the QTI profile or return exact
        extension/ loss/incompatibility with round-trip tests. Evidence: QTI
        suite.
  <!-- M4.7 completion evidence (2026-08-31):

       - Governed authoring contract: Metis now owns a strict `4.7.0` model for
         media, simulation, and interaction items. Image, audio, video, 3D, and
         stimulus bindings carry immutable asset and rendition revisions and
         hashes plus delivery locator, authority, source, rights, and provenance.
         Normalized response regions, timing/attempt bounds, typed state and event
         schemas, exact evaluator pins, expected values, and partial-credit hooks
         are cross-validated; scoring totals exactly 10,000 basis points and the
         capture allowlist equals the authored response-event set.
       - Accessibility and safe disclosure: every locale must resolve alternative
         text, long description, transcript, captions, audio description, and
         sign-language support as either a governed reference or an explicit
         reviewer/time-stamped not-applicable decision. Keyboard paths,
         noninteractive equivalents, support requirements, reduced-motion
         equivalents, and screen-reader review are required. Learner/search/
         export/log/tutor projections structurally omit scoring hooks, expected
         values, signer references, reviewer identities/times, and internal
         not-applicable reasons; protected export is a separate instructor/admin
         route. Blank added locales cannot be saved until every surface and
         alternative is completed.
       - Runtime and response capture: the sealed contract pins a delivery engine,
         exact bundle digest, deterministic seed, reset policy, HTTPS network
         allowlist, opaque origin, and CPU/wall/memory/process/writable/event/
         payload limits while denying same-origin authority, popups, top
         navigation, and persistent storage. Preview capture admits only exact
         typed events and bounded payloads tied to the current revision, then
         returns an HMAC-SHA256 receipt without learner identity, freeform
         telemetry, or key exposure. A configured server secret (with the existing
         server JWT secret as the deployment fallback) provides stable signing;
         the service fails closed if no signer is supplied.
       - Delivery and QTI evidence: the educator workbench previews locale,
         desktop/tablet/mobile, keyboard/pointer/touch/switch, full/reduced motion,
         standard/high contrast, default/captions/transcript/audio-description/
         sign-language modes, and online/low/offline bandwidth through the shared
         `MediaInteractiveDelivery` learner component and records one evidence
         decision per dimension. QTI 3.0 Metis extension export includes a
         checksum-bound full typed sidecar and immediately reimports it to an exact
         content hash; QTI 3.0 core returns the exact loss paths; QTI 2.2 returns an
         explicit incompatibility and no misleading artifact.
       - Persistence and automated verification: migration `019`, chained from
         `018`, passed clean 001→019, 019→018, and re-upgrade paths on disposable
         PostgreSQL. Full content persisted, automatic item-bank projection writes
         remained at zero, and direct UPDATE/DELETE were rejected. All 31 focused
         M4.7 service/API/migration/live-PostgreSQL tests pass; the consolidated
         affected M4.1–M4.7 and OpenAPI set passes 210 tests. Checked-in OpenAPI and
         generated Metis TypeScript client drift checks pass. Focused Ruff covers
         15 Python/migration/test files and strict mypy covers six source modules.
         All 63 Metis web unit files (248 tests), full ESLint with zero warnings,
         and TypeScript pass. Nine media/interactive Playwright flows pass serially
         across Chromium, Firefox, and WebKit with automatic serious/critical Axe
         analysis, covering complete payloads, safe real-renderer accommodation
         switching, signed capture, QTI exact/loss/incompatibility, learner denial,
         translation completeness, and 390px no-overflow. The adjacent twelve-flow
         capture matrix also passes after adding the routed workbench link.
       - Workbench design: following `frontend-skill`, the educator-only surface
         extends the restrained assessment editorial system with an exact-source
         strip, numbered authoring ledger, dense but linear governed-media table,
         explicit alternative decisions, one sticky dark delivery rail, and a
         contour-map stimulus stage. The first automated accessibility run exposed
         inherited low-contrast rail evidence; its component colors were corrected
         before the three-engine gate passed.
       - Generated evidence:
         `evidence/v1-workbenches/metis-media-interactive-authoring.json` is
         content-addressed over implementation, tests, generated contracts, UI,
         TODO, and producer/test inputs; its drift and semantic tests pass. Browser
         API transport uses deterministic doubles, and QTI round-trip is in-process
         rather than external-LMS certification. This does not prove production
         deployment, representative educator usability, physical assistive-
         technology behavior, formal accessibility conformance, or third-party QTI
         certification. A Next production build was not run under the
         less-than-32-GiB host approval gate; affected compile, type, lint, unit,
         contract, database, accessibility, responsive, and cross-browser gates
         pass. The three disposable verification databases were removed after use.
       -->
- [x] M4.8 Build stimulus-set authoring. Evidence: shared-stimulus edits mark
      every dependent item stale.
  - [x] M4.8.a Build immutable stimulus create/edit/version/localize/source/
        accessibility/rights workflow separately from items. Evidence: authoring
        tests.
  - [x] M4.8.b Build set identity, ordered stimulus revisions, shared
        directions/ context, item membership/order, and presentation rules.
        Evidence: set schema tests.
  - [x] M4.8.c Attach items by exact revision/role/locator and prevent cross-
        tenant, unauthorized, circular, or answer-exposing links. Evidence: link
        tests.
  - [x] M4.8.d Preview item/set combinations and detect conflicting directions,
        locale, accessibility, exposure, layout, and target constraints.
        Evidence: preview tests.
  - [x] M4.8.e On stimulus/set revision, compute dependent item/assessment/build
        blast radius and mark review/gates stale without rewriting them.
        Evidence: invalidation tests.
  <!-- M4.8 completion evidence (2026-08-31):

       - Immutable stimulus workflow: Metis now owns a strict `4.8.0` contract
         and append-only ledger for stimulus revisions independent of item
         revisions. Each stimulus records exact locale surfaces, source and
         rights provenance, governed asset revisions, accessibility supports,
         reviewed alternatives, author lineage, content hash, and idempotency
         key. Creating a revision requires an exact optimistic-concurrency pin;
         partial translations and locale surfaces without matching accessibility
         alternatives fail closed.
       - Set identity and composition: a separate immutable set ledger preserves
         stable set identity, exact ordered stimulus revisions, localized shared
         directions and context, exact ordered item or nested-set members, and
         presentation constraints for layout, viewport, devices, inputs, locale,
         and accessibility. The service rejects duplicate or noncontiguous order,
         no-change revisions, stale editors, missing revisions, and transitive
         set cycles while retaining complete revision history.
       - Exact authorized graph: item members pin tenant, family, item ID,
         revision, SHA-256, learner projection, semantic role, and locator. Exact
         resolution covers selected-response, written-response, math/code, and
         media/interactive ledgers. Instructor-only API gates run before service
         dispatch; cross-tenant, missing, unauthorized, circular, and
         answer-material-bearing references are rejected.
       - Preview and invalidation: exact-revision preview composes shared set
         context with learner-safe item pins and reports closed conflict codes for
         directions, locale, accessibility, answer exposure, layout, and target
         constraints. Revising a shared stimulus appends staleness rows for its
         current set, item, assessment, and build dependents; revising a set marks
         its prior item and downstream dependencies stale. Review/gate identities
         and old revision hashes are retained, and no dependent record is
         rewritten.
       - Persistence and automated verification: migration `020`, chained from
         `019`, passed clean 001→020, 020→019, and re-upgrade paths on disposable
         PostgreSQL. Three database triggers reject UPDATE/DELETE across the two
         revision ledgers and staleness ledger. The live PostgreSQL integration
         observes four exact stale dependency kinds after a shared-stimulus edit
         and proves the prior set remains unchanged. All 21 focused M4.8
         service/API/migration/live-PostgreSQL tests pass; the consolidated
         affected M4.1–M4.8 and OpenAPI set passes 232 tests, with ten optional
         sandbox-runtime cases skipped by their existing environment gate.
         Checked-in OpenAPI and generated Metis TypeScript client drift checks
         pass. Focused Ruff covers 16 Python/migration/test files and strict mypy
         covers five source modules.
       - Authoring studio: following `frontend-skill`, the new responsive source
         room separates stimulus and set work, supports bilingual authoring with
         accessibility/source/rights metadata, pins exact learner-safe items and
         downstream revisions, previews all six conflict families, and exposes
         the append-only review blast radius. It is discoverable from both item
         bank entry points. All 64 Metis web unit files (253 tests), ESLint with
         zero warnings, and TypeScript pass. Twelve stimulus/set Playwright flows
         pass serially across Chromium, Firefox, and WebKit with automatic
         serious/critical Axe analysis, including bilingual completeness, exact
         safe pins, conflict reporting, four-kind staleness, no rewrite, learner
         denial, and 390px reflow. The adjacent twelve-flow item-capture matrix
         also passes across all three engines.
       - Evidence boundary: the deterministic
         `metis-stimulus-set-authoring.json` inventory hashes every implementation,
         test, contract, UI, and traceability input and asserts M4.8.a–M4.8.e.
         Browser API transport uses deterministic doubles. This does not prove
         production deployment, representative educator usability, physical
         assistive-technology behavior, or formal accessibility conformance. A
         Next production build was not run under the less-than-32-GiB host
         approval gate; affected compile, type, lint, unit, contract, database,
         accessibility, responsive, and cross-browser gates pass. The disposable
         verification database was removed after use.
       -->
- [x] M4.9 Build item-level objective, standard, concept, and prerequisite
      linking. Evidence: invalid and cross-tenant refs fail.
  - [x] M4.9.a Define typed link roles/strength/coverage/cognitive
        level/rationale/ evidence with exact item and target revisions.
        Evidence: link schemas.
  - [x] M4.9.b Search/select only authorized course/tenant objective, standard,
        concept, and prerequisite registry entries. Evidence: selector tests.
  - [x] M4.9.c Validate target existence/lifecycle/version, discipline/kind/
        grade/locale compatibility, cycles, and conflicting links. Evidence:
        validation tests.
  - [x] M4.9.d Present links in item, coverage matrix, concept graph, search,
        and standards export with consistent meaning. Evidence: projection
        tests.
  - [x] M4.9.e Reject crafted foreign/stale/deprecated/unknown refs and preserve
        link revision/audit. Evidence: negative tests.
  <!-- M4.9 completion evidence (2026-08-31):

       - Exact typed links: Metis now owns a strict `4.9.0` contract for a
         learner-safe exact item revision, exact registry target snapshots, and
         typed objective, standard, concept, and prerequisite links. Every link
         records role, strength, basis-point coverage, cognitive classification,
         rationale, and evidence while preserving item and target revision IDs,
         content hashes, registry revision, tenant/course scope, and author
         lineage. Structural validation rejects duplicate targets, incompatible
         roles, invalid coverage, and any answer-bearing item projection.
       - Authorized selection: the target registry is resolved from current
         planning-ready curriculum-map revisions and the canonical standards
         registry. Instructor-only search filters query, target kind, tenant,
         course, lifecycle, and authorization before returning selectable
         entries. Objectives, standards, concepts, and prerequisites are all
         exposed without accepting client-authored registry identities.
       - Closed validation: exact resolution covers selected-response,
         written-response, math/code, and media/interactive item ledgers. The
         service re-resolves both item and target snapshots, then rejects missing,
         unauthorized, foreign-tenant/course, stale, deprecated, unknown, or
         tampered references. Discipline, grade, locale, item-family, target
         conflict, registry-cycle, and composed prerequisite-cycle checks all
         fail closed with stable error codes.
       - Consistent projections and history: one canonical semantic checksum and
         exact link payload are projected to the item, coverage matrix, concept
         graph, search, and standards export surfaces, preventing consumers from
         assigning different meaning to the same revision. Optimistic revision
         pins, idempotency drift checks, no-change rejection, content and lineage
         integrity checks, and immutable revision audit preserve every accepted
         decision, while rejected attempts leave prior revisions untouched.
       - Persistence and automated verification: migration `021`, chained from
         `020`, passed clean 001→021, 021→020, and re-upgrade paths on
         disposable PostgreSQL. Two database triggers reject UPDATE/DELETE on
         the revision and audit ledgers. The live integration preserves two exact
         revisions and two audit events and proves both ledgers immutable. All 32
         focused M4.9 service/API/migration/live-PostgreSQL tests pass; the
         consolidated affected M4.1–M4.9 and OpenAPI set passes 265 tests, with
         ten optional sandbox-runtime cases skipped by their existing environment
         gate. Checked-in OpenAPI and generated Metis TypeScript client drift
         checks pass. Focused Ruff covers 16 Python/migration/test files and
         strict mypy covers five source modules.
       - Authoring workbench: following `frontend-skill`, the responsive
         instructor-only alignment desk combines an authorized registry browser,
         exact item binding, compact typed link controls, semantic projection
         comparison, and immutable audit history. It is discoverable from both
         item-bank entry points. All 65 Metis web unit files (259 tests), ESLint
         with zero warnings, and TypeScript pass. Twelve M4.9 Playwright flows
         pass serially across Chromium, Firefox, and WebKit with automatic
         serious/critical Axe analysis, covering all target kinds, exact safe
         saves, projection equality, revision/audit preservation, stale-target
         draft retention, learner denial, and 390px reflow. The adjacent
         twelve-flow item-capture matrix also passes across all three engines.
       - Evidence boundary: the deterministic
         `metis-item-learning-links.json` inventory hashes every implementation,
         test, contract, UI, traceability, and producer input and asserts
         M4.9.a–M4.9.e. Browser API transport uses deterministic doubles. This
         does not prove production deployment, representative educator usability,
         physical assistive-technology behavior, or formal accessibility
         conformance. A Next production build was not run under the less-than-
         32-GiB host approval gate; affected compile, type, lint, unit, contract,
         database, accessibility, responsive, and cross-browser gates pass. The
         disposable verification database was removed after use.
       -->
- [x] M4.10 Build difficulty, cognitive level, time, and confidence estimates
      with source provenance. Evidence: estimates are never labeled calibrated
      before empirical data.
  - [x] M4.10.a Define separate author, rule/model-predicted, expert-reviewed,
        pilot, and empirically calibrated estimate sources with versions.
        Evidence: estimate schema.
  - [x] M4.10.b Estimate difficulty, taxonomy+level, response time/range, and
        per-estimate confidence/uncertainty from exact item/context revision.
        Evidence: known-value tests.
  - [x] M4.10.c Record
        method/model/config/corpus/features/assumptions/applicability and human
        changes; keep different source estimates visible. Evidence: provenance
        tests.
  - [x] M4.10.d Reserve “calibrated” for ratified empirical sample/method/
        uncertainty evidence and reject predicted-to-calibrated promotion.
        Evidence: status invariant tests.
  - [x] M4.10.e Recompute/preserve history on item/model/data change and surface
        disagreement/manual review. Evidence: lifecycle tests.
  <!-- M4.10 completion evidence (2026-08-31):

       - Separate typed sources: Metis now owns a strict `4.10.0` contract for
         author, deterministic rule/model prediction, expert review, pilot
         observation, and empirical calibration sources. Each source carries
         its own identity, version, lifecycle, status, actor, exact item/context
         hashes, values, provenance, and semantic checksum. Source-kind/status/
         evidence combinations fail closed, so authored, predicted, reviewed,
         pilot-observed, and calibrated states cannot be interchanged.
       - Bounded exact estimates: a deterministic, explicitly untrained and
         uncalibrated rule profile inspects non-answer features from one exact
         selected-response, written-response, math/code, or media/interactive
         item revision in one authorized planning-ready course-map context. It
         emits ordered basis-point difficulty bounds, Bloom taxonomy/level and
         measurable verb, ordered response-time seconds, and per-source
         confidence plus uncertainty. Known-value tests lock the feature vector
         and all four outputs; learner-safe item and context revision hashes are
         carried through every prediction and accepted source.
       - Complete visible provenance: every non-calibrated submission records
         exact method/model/config IDs, versions, hashes, corpus-use status,
         feature references, assumptions, applicability, and limitations.
         Expert review pins an exact prior estimate and must declare the complete
         set of changed dimensions; undeclared or stale changes fail. Rule,
         author, expert, pilot, and empirical sources remain separately visible,
         including stale/superseded history, rather than being blended into an
         unexplained number.
       - Calibration invariant: only a trusted registry record can create an
         empirical `calibrated` source. Its ratified evidence requires at least
         30 observations, exact data snapshot ID/version/hash and sample size,
         exact method ID/version/hash, collection window, uncertainty method,
         exact item/context hashes, and at least two distinct post-collection
         ratifiers. Registry provenance must match that data snapshot and method.
         Callers submit only the opaque evidence ID/version; user-authored values
         or provenance, stale evidence, mismatched registry identity, and
         predicted-to-calibrated promotion are rejected. The production-safe
         default registry is empty until a trusted empirical provider is wired.
       - Recalculation, disagreement, and immutable history: item, context,
         model-profile, pilot-data, and empirical-data triggers are validated
         against the actual transition. Item/context/model changes stale the
         affected old source and append a new rule prediction while preserving
         independent current sources. Difficulty, cognitive-level, and response-
         time spreads produce explicit manual-review signals. Optimistic pins,
         idempotency drift/no-change rejection, content/source/revision hashes,
         and lineage validation preserve every accepted revision and audit event.
       - Persistence and automated verification: migration `022`, chained from
         `021`, passed a fresh 001→022 upgrade and a 022→021→022 round-trip on
         disposable PostgreSQL. Two database triggers reject UPDATE/DELETE on
         estimate revision and audit ledgers. The live integration preserves two
         exact revisions, a ratified calibration, and two audit events, and proves
         both ledgers immutable. All 33 focused M4.10 service/API/migration/live-
         PostgreSQL tests pass. The consolidated affected M4.1–M4.10 and OpenAPI
         set passes 289 tests, with 19 existing optional database/sandbox cases
         skipped by their environment gates. Checked-in OpenAPI and generated
         Metis TypeScript client drift checks pass. Focused Ruff covers 16
         Python/migration/test files and strict mypy (Python 3.12 target) covers
         five source modules.
       - Authoring workbench: following `frontend-skill`, the responsive,
         instructor-only estimate observatory uses an editorial three-ledger
         composition for exact scope, bounded source declaration, and source/
         history evidence. It exposes all four submission paths, keeps rule and
         pilot states visibly uncalibrated, accepts empirical records only by
         registry reference, displays ratifier/sample facts, provenance,
         disagreement, and immutable audit, and is linked from item-bank,
         capture, and learning-link entry points. All 66 Metis web unit files
         (265 tests), ESLint with zero warnings, and TypeScript pass. Eighteen
         M4.10 Playwright flows pass serially across Chromium, Firefox, and
         WebKit with automatic serious/critical Axe analysis; the adjacent 24
         capture and learning-link flows also pass across all three engines.
       - Evidence boundary: the deterministic `metis-item-estimates.json`
         inventory hashes every implementation, test, contract, UI,
         traceability, and producer input and asserts M4.10.a–M4.10.e. Browser
         API transport uses deterministic doubles. This does not prove
         production deployment, connection of a production empirical provider,
         representative educator usability, physical assistive-technology
         behavior, or formal accessibility conformance. A Next production build
         was not run under the less-than-32-GiB host approval gate; affected
         compile, type, lint, unit, contract, database, accessibility,
         responsive, and cross-browser gates pass. The disposable verification
         database was removed after use.
       -->
- [x] M4.11 Build distractor plausibility and misconception mapping. Evidence:
      known-value rules and human review.
  - [x] M4.11.a Define versioned misconception concepts, evidence/source,
        objective/learner band, locale, lifecycle, and option mapping contract.
        Evidence: schema tests.
  - [x] M4.11.b Check option uniqueness, grammatical/semantic parallelism,
        length/ cue imbalance, implausibility, overlap, absolute wording, and
        correct- answer leakage using known-value rules. Evidence: lint
        fixtures.
  - [x] M4.11.c Evaluate distractor-to-misconception relevance separately from
        plausibility and never infer an individual learner misconception solely
        from a response. Evidence: boundary tests.
  - [x] M4.11.d Present attributed rule/model suggestions with uncertainty and
        require human accept/edit/reject mappings. Evidence: review tests.
  - [x] M4.11.e Preserve empirical option statistics separately and trigger re-
        review when distractors/items/misconception vocabularies change.
        Evidence: lifecycle tests.
  <!-- M4.11 completion evidence (2026-09-01):

       - Versioned vocabulary and exact mappings: Metis now owns a strict
         `4.11.0` contract for misconception concept identity, revision,
         evidence/source identity and hash, objective scope, learner band,
         locale, lifecycle, correction guidance, indicators, and exact option-
         to-concept mappings. Optimistic revision pins, semantic and revision
         hashes, idempotency drift rejection, unique scope values, and exact
         supersession lineage fail closed; a first revision cannot claim to be
         superseded without an actual predecessor.
       - Known-value surface evidence: a content-addressed `1.0.0` rule profile
         always evaluates all eight required categories: normalized option
         uniqueness, grammatical parallelism, semantic parallelism, length/cue
         imbalance, overt implausibility, option overlap, absolute wording, and
         keyed-answer leakage. Findings preserve exact option IDs, severity,
         evidence, rule identity/hash, and uncertainty. Fixture tests cover
         every rule family, while bounded plausibility explicitly states that
         lexical surface checks cannot establish domain plausibility.
       - Relevance and human-review boundary: distractor plausibility and
         misconception relevance are separate typed results. Rule and submitted
         model suggestions carry exact method/model/corpus attribution,
         confidence, uncertainty, concept revision, and a literal human-review
         requirement. A reviewer must accept the suggestion unchanged, edit it
         explicitly, or reject it; every suggestion must receive exactly one
         decision. Keyed options, stale concepts, incomplete decisions, and
         silent accept-time edits are rejected. Both contracts and UI hard-code
         that one response neither establishes nor permits an individual
         learner misconception inference.
       - Separate empirical lifecycle: option counts, rates, point-biserial
         values, sample/method provenance, collection window, and uncertainty
         live in a distinct no-inference statistics contract and ledger. Item,
         distractor-set, vocabulary, and statistics changes independently mark
         prior evidence for re-analysis/re-review. Preparation and immutable
         history expose exact current and reviewed hashes instead of blending
         observed behavior into a mapping claim.
       - Persistence and API verification: migration `023`, chained from `022`,
         passed a clean 001→023 upgrade and a 023→022→023 round-trip on
         disposable PostgreSQL. Four database triggers reject UPDATE/DELETE on
         misconception, analysis, human-review, and statistics ledgers. The live
         integration preserves two concepts plus one analysis, review, and
         statistics record for its isolated tenant and probes every trigger.
         Seven instructor-only routes publish vocabulary, preparation,
         analysis, review, statistics, and history operations; learner requests
         are denied before the service. All 46 focused M4.11 tests, including
         live PostgreSQL, pass. The consolidated affected M4.1–M4.11 and OpenAPI
         set passes 331 tests, with 20 existing optional database/sandbox cases
         skipped by their environment gates. Checked-in OpenAPI and generated
         Metis TypeScript client drift checks pass. Focused Ruff covers 17
         Python/migration/test files and strict mypy (Python 3.12 target) covers
         five source modules.
       - Distractor review desk: following `frontend-skill`, the responsive,
         instructor-only workbench uses a restrained three-desk editorial
         composition for exact scope, vocabulary, surface evidence, and human
         judgment, with observed behavior and immutable lineage kept below in
         separate ledgers. It supports concept revision, all eight checks,
         attributed rule/model suggestions, explicit accept/edit/reject, sealed
         statistics, stale-state notices, and hard non-diagnosis language, and
         is linked from item-bank, capture, selected-response, and estimate
         entry points. A rendered desktop visual pass, all 67 Metis web unit
         files (273 tests), ESLint with zero warnings, and TypeScript pass.
         Eighteen M4.11 Playwright flows pass serially across Chromium, Firefox,
         and WebKit with automatic serious/critical Axe analysis; the adjacent
         selected-response and estimate suites add 27 passing cross-browser
         regressions, including 390-pixel reflow and learner-denial paths.
       - Evidence boundary: the deterministic
         `metis-distractor-mapping.json` inventory hashes every implementation,
         test, migration, API/client contract, UI, traceability, and producer
         input and asserts M4.11.a–M4.11.e. Browser API transport uses
         deterministic doubles. This does not prove production deployment,
         individual learner diagnosis, representative educator usability,
         physical assistive-technology behavior, or formal accessibility
         conformance. A Next production build was not run under the less-than-
         32-GiB host approval gate; affected compile, type, lint, unit, contract,
         database, accessibility, responsive, and cross-browser gates pass. The
         disposable verification database was removed after use.
       -->
- [x] M4.12 Build single-correct-answer and ambiguity verification. Evidence:
      adversarial crafted-item corpus.
  - [x] M4.12.a Define applicability by item/response/scoring kind and explicit
        uniqueness/ambiguity verdicts, evidence, uncertainty, and reviewer
        state. Evidence: verifier schema.
  - [x] M4.12.b Verify keyed response satisfies prompt and every nonkey fails
        under stated assumptions, tolerances, sources, and response rules.
        Evidence: known-value tests.
  - [x] M4.12.c Detect multiple defensible interpretations/answers, missing
        context, contradictory sources, locale/cultural ambiguity, option
        overlap, and rubric underdetermination. Evidence: adversarial corpus.
  - [x] M4.12.d Use symbolic/executable/source/model-assisted checks only as
        attributed evidence with timeout/error/N/A and human review bands.
        Evidence: checker tests.
  - [x] M4.12.e Block single-correct publication on unresolved ambiguity while
        preserving raw findings and exception limits. Evidence: gate tests.
  <!-- M4.12 completion evidence (2026-09-01):

       - Exact applicability and proof contract: Metis now owns a strict
         `4.12.0` contract that pins one protected selected-response revision,
         item/response/scoring kind, locale, exact option partition, response
         rule, sources, assumptions, tolerances, interpretations, rubric,
         uncertainty, automated verdict, and completed reviewer state. The
         verifier explicitly returns applicable only for exact single-response
         scoring; all four selected-response kinds have applicability coverage.
         A stored structural key is never treated as semantic proof.
       - Known-value and adversarial verification: the deterministic truth
         matrix requires the key to satisfy prompt, source, and response rule
         while every nonkey independently fails under the submitted boundary.
         A seven-item crafted corpus contains one unique control and exercises
         multiple defensible answers, missing context, contradictory source
         editions, locale/cultural differences, option overlap, and rubric
         underdetermination. Every result retains exact evidence attribution
         and uncertainty; ambiguous or incomplete evidence fails closed.
       - Checker and human-authority boundary: symbolic, executable, source,
         and model-assisted checks carry exact method/configuration and, where
         applicable, source/model identity. Pass, fail, timeout, error, and N/A
         shapes are explicit, including time limits, error codes, rationales,
         evidence, uncertainty, and human-review bands. Checker output remains
         advisory. Human review must decide every immutable raw finding and
         cannot declare uniqueness over an incomplete key/nonkey proof.
       - Publication gate, persistence, and API verification: unresolved
         semantic ambiguity blocks the authoritative single-correct publish
         gate while raw findings remain unchanged. The sole bounded exception
         is one checker-unavailable finding, approved by an administrator, for
         no more than 30 days and one use. Migration `024`, chained from `023`,
         passed a clean 001→024 upgrade and a 024→023→024 round-trip on
         disposable PostgreSQL. Three database triggers reject UPDATE/DELETE
         on verification, human-review, and gate ledgers. Each isolated live
         workflow preserves two revisions in every ledger. Five instructor-
         only routes publish preparation, verification, human review, gate,
         and history; learner requests are denied before the service. All 45
         focused M4.12 tests, including live PostgreSQL, pass. The consolidated
         affected M4.1–M4.12 and OpenAPI set passes 375 tests, with 11 optional
         live-database cases skipped by their environment guards. Checked-in
         OpenAPI and generated Metis TypeScript client drift checks pass.
         Focused Ruff covers 17 Python/migration/test files and strict mypy
         (Python 3.12 target) covers five source modules.
       - Answer proving desk: following `frontend-skill`, the responsive,
         instructor-only workbench uses a restrained three-desk editorial
         composition for the exact claim, adversarial evidence, and
         authoritative publication gate. It exposes option-by-option status,
         versioned sources, assumptions, missing context, locale/culture,
         rubric evidence, all four checker kinds, immutable raw findings,
         explicit human decisions, stale witnesses, exception limits, and
         gate state, and is linked from item-bank, selected-response,
         distractor, and estimate entry points. A rendered desktop visual pass,
         all 68 Metis web unit files (281 tests), ESLint with zero warnings, and
         TypeScript pass. Eighteen M4.12 Playwright executions pass serially
         across Chromium, Firefox, and WebKit with automatic serious/critical
         Axe analysis; the adjacent selected-response and distractor suites add
         27 passing cross-browser regressions, including 390-pixel reflow and
         learner-denial paths.
       - Evidence boundary: the deterministic `metis-answer-ambiguity.json`
         inventory hashes every implementation, corpus, test, migration,
         API/client contract, UI, traceability, and producer input and asserts
         M4.12.a–M4.12.e. Browser API transport uses deterministic doubles.
         This does not prove production deployment, representative educator
         usability, physical assistive-technology behavior, or formal
         accessibility conformance. A Next production build was not run under
         the less-than-32-GiB host approval gate; affected compile, type, lint,
         unit, contract, database, accessibility, responsive, and cross-browser
         gates pass. The disposable verification database was removed after
         use.
       -->
- [x] M4.13 Build answer-key leakage scan across learner-visible fields, assets,
      alt text, hints, exports, logs, and URLs. Evidence: negative suite.
  - [x] M4.13.a Enumerate learner-visible UI/API/renderer/search/cache/event/
        analytics/export/package/log/error/URL channels and protected key/
        rationale/test fields. Evidence: coverage manifest.
  - [x] M4.13.b Scan stem/options/feedback/hints/metadata/filenames/IDs and
        accessibility text/tracks for exact, normalized, encoded, indirect, and
        positional key leakage. Evidence: text leakage tests.
  - [x] M4.13.c Scan media pixels/audio/transcripts/captions/descriptions/
        documents/code/runtime output/interactive state and hidden metadata.
        Evidence: asset leakage tests.
  - [x] M4.13.d Inspect QTI/LTI/SCORM/xAPI/course packages, previews, deep
        links, URLs, client bundles/state, errors/logs/traces, and support
        exports. Evidence: channel tests.
  - [x] M4.13.e Test answer variations/multiple locales/randomized order/
        encryption/role changes and fail release for any unresolved exposure.
        Evidence: adversarial negative suite.
  <!-- M4.13 completion evidence (2026-09-01):

       - Closed coverage and protected-material contract: Metis now owns a strict
         `4.13.0` contract and executable manifest for all 22 learner-visible
         UI, API, renderer, search, cache, event, analytics, package, preview,
         client, error, log, trace, and support-export channels. Seven protected
         key, localized key text, rationale, misconception, response-rule, and
         test-field kinds are represented only by nonreversible descriptors in
         preparation and scan responses. Coverage is evaluated across every
         required channel, surface, authored locale, and learner/anonymous role;
         missing or unavailable evidence fails closed.
       - Text, asset, and channel negative suite: exact, Unicode-normalized,
         base64/hex/URL-encoded, indirect, and positional detection scans content,
         decoded locators, filenames, identifiers, hidden metadata, and attributed
         scanner output. The narrow option allowlist admits only the exact key
         identity or localized key text on its own option/accessibility surface.
         OCR pixels, ASR audio, transcripts, captions, descriptions, documents,
         code, runtime output, interactive state, archives, QTI/LTI/SCORM/xAPI,
         client bundles/state, and operational output are all exercised. Scanner
         method/version/hash, timeout, outcome, and extracted witness are retained;
         missing observations, timeouts, errors, and invalid decrypted-observation
         lineage are blocking evidence failures.
       - Adversarial release authority: the exhaustive safe fixture contains 144
         attributed artifacts and eight presentation cases spanning two locales,
         two learner-visible roles, two randomized orders per locale/role, and
         four encrypted cases. Client-decryptable ciphertext, role or locale gaps,
         incomplete surfaces, unresolved exposures, incomplete human decisions,
         stale exact pins, or tampered lineage block release. Every raw finding is
         decided exactly once, remains immutable, and has no exception path.
       - Persistence, API, and contract verification: migration `025`, chained
         from `024`, passed a clean 001→025 upgrade and a 025→024→025 round-trip
         on disposable PostgreSQL. Three triggers reject UPDATE/DELETE on scan,
         review, and release-gate ledgers; the isolated live workflow preserves
         two revisions in each ledger. Five instructor-only routes publish
         preparation, scan, review, release gate, and history; learner requests
         are denied before the service. All 48 focused M4.13 tests, including live
         PostgreSQL, pass. The affected M4.1–M4.13 and OpenAPI set passes 422
         tests, with 22 optional integration cases skipped by their existing
         environment guards. Checked-in OpenAPI and generated Metis TypeScript
         client drift checks pass. Focused Ruff covers 17 Python/migration/test
         files and strict mypy covers five source modules.
       - Leakage review desk: following `frontend-skill`, the responsive,
         instructor-only workbench uses a restrained three-desk editorial layout
         for coverage, attributed capture and raw findings, and the authoritative
         no-exception gate. It exposes the 22-channel register, exact revision
         pins, capture parsing, findings defaulted to confirm, explicit decisions,
         immutable history, and stale state without retaining protected answers,
         and is linked from the item-bank, selected-response, distractor, and
         answer-verification entry points. A rendered desktop visual pass, all 69
         Metis web unit files (286 tests), ESLint with zero warnings, and
         TypeScript pass. Eighteen M4.13 Playwright executions pass serially
         across Chromium, Firefox, and WebKit with automatic serious/critical Axe
         analysis; adjacent selected-response, distractor, and answer-verification
         suites add 45 passing cross-browser regressions, including 390-pixel
         reflow and learner-denial paths.
       - Evidence boundary: the deterministic `metis-answer-leakage.json`
         inventory hashes every implementation, test, migration, API/client
         contract, UI, traceability, and producer input and asserts
         M4.13.a–M4.13.e. Scanner observations and browser API transport use
         deterministic test doubles. This does not prove production deployment,
         representative educator usability, physical assistive-technology
         behavior, or formal accessibility conformance. A Next production build
         was not run under the less-than-32-GiB host approval gate; affected
         compile, type, lint, unit, contract, database, accessibility, responsive,
         and cross-browser gates pass. The disposable verification databases were
         removed after use.
       -->
- [x] M4.14 Build item accessibility checklist and automated lint. Evidence: QTI
      Elevated Accessibility-relevant support matrix.
  - [x] M4.14.a Define versioned checklist by item/media/interaction kind,
        applicable WCAG/QTI requirements, PNP supports, severity, evidence, and
        manual-only criteria. Evidence: checklist registry.
  - [x] M4.14.b Lint
        structure/semantics/names/instructions/order/focus/keyboard/
        timing/drag/motion/color/contrast/zoom/reflow/error behavior. Evidence:
        interaction lint tests.
  - [x] M4.14.c Lint images/math/code/tables/charts/audio/video/3D/interactions
        for required equivalents, captions/transcripts/descriptions, and sync.
        Evidence: content lint tests.
  - [x] M4.14.d Test locale/bidi, screen reader, keyboard, zoom, theme,
        viewport, reduced motion, and configured accommodation previews.
        Evidence: automation matrix.
  - [x] M4.14.e Map each support to QTI accessibility metadata/content with
        supported/degraded/unsupported status and block unresolved mandatory
        checks. Evidence: support matrix.
  <!-- M4.14 completion evidence (2026-09-01):
       - Runtime checklist: `item_accessibility.py` and
         `item_accessibility_helpers.py` publish immutable schema version 4.14.0
         and checklist version 2026.09.01 for WCAG 2.2, QTI 3.0 Elevated
         Accessibility, and AccessForAll PNP 3.0. Twenty-seven requirements
         carry closed item/media/interaction applicability, WCAG criteria, QTI
         metadata paths, PNP support codes, blocker/error severity, mandatory
         state, evidence/remediation text, and explicit manual-only state. The
         registry admits all 15 item kinds, nine media kinds, and 11 interaction
         kinds while exact source snapshots resolve the selected-response,
         written-response, math/code, and media/interactive revision families.
       - Executable lint: automatic observations bind a named/versioned/hashed
         tool and configuration, timeout, elapsed time, outcome, evidence, and
         an exact observation digest. Negative suites exercise structure,
         semantics, names, instructions, reading/focus order, keyboard, timing,
         drag, motion, color, contrast, zoom, reflow, and errors. Timeout, tool
         error, invalid not-applicable claims, missing observations, missing or
         failed manual reviews, digest tampering, and duplicate evidence all
         fail closed. Image, MathML/spoken math, code, table, chart, transcript,
         captions, audio description, 3D, and custom-interaction equivalence and
         synchronization requirements are executable and tested together.
       - Automation/support matrix: every locale requires the exact ten-cell
         locale/BiDi, named screen-reader, keyboard, 200% zoom, 400% zoom, light
         theme, dark theme, 320-CSS-pixel viewport, reduced-motion, and configured
         accommodation matrix. RTL direction mismatch and incomplete PNP support
         previews are rejected. Every applicable PNP support must map to QTI
         metadata and/or content with evidence and an explicit supported,
         degraded, or unsupported disposition; degraded, unsupported, and
         missing mandatory supports block. Current item, registry, audit, and
         gate hashes are atomic, stale evidence reopens release, and the schema
         contains no exception path.
       - Persistence/API/UI: migration 026 chains from 025 and adds two
         constrained append-only audit/gate ledgers with two PostgreSQL mutation
         triggers. Clean 001->026 and 026->025->026 migrations pass. A live
         PostgreSQL scenario preserves two audits (clear then failed), two gates
         (pass then blocked), raw findings, and rejects both UPDATE and DELETE.
         Four instructor-only endpoints expose preparation, audit append, gate
         append, and immutable history through closed OpenAPI/generated-client
         contracts; learner requests are denied before service construction.
         The instructor workbench renders a keyboard-accessible 27-check
         register, locale preview matrix, attributed evidence intake, immutable
         findings, QTI/PNP support dispositions, exact witnesses, history, and a
         no-exception release decision. Browser-authored empty evidence claims
         are rejected before transport.
       - Verification: all 40 focused M4.14 backend tests, including live
         PostgreSQL, pass. The affected M4.1-M4.14 and OpenAPI set passes 461
         tests with 13 existing database-gated skips. Ruff passes 16 focused
         files, strict mypy passes five source files, deterministic OpenAPI export
         and Metis client drift checks pass, web lint/typechecking pass, and the
         complete web unit set passes 70 files / 291 tests. Eighteen M4.14
         Playwright executions pass serially across Chromium, Firefox, and WebKit
         with automatic serious/critical Axe analysis, RTL, clear, failed,
         timeout, learner-denial, and 390-pixel paths. Eighty-four adjacent item
         bank, selected/written/math/media authoring, answer-verification, and
         answer-leakage cross-browser executions pass after constraining the
         expanded item-bank workflow navigation at desktop widths.
       - Evidence boundary: deterministic
         `metis-item-accessibility.json` hashes every implementation, test,
         migration, API/client contract, UI, traceability, and producer input and
         asserts M4.14.a-M4.14.e. Scanner captures, named screen-reader cases, and
         browser API transport use deterministic test doubles. This does not
         prove production deployment, representative educator usability,
         physical assistive-technology behavior, or formal accessibility
         certification. A Next production build was not run under the
         less-than-32-GiB host approval gate; affected compile, lint, type, unit,
         contract, database, accessibility, responsive, and cross-browser gates
         pass. The disposable verification database was removed after use.
       -->
- [x] M4.15 Build item compare and decision records. Evidence: stem, response,
      rationale, accessibility, and gate differences are visible.
  - [x] M4.15.a Resolve two authorized exact item/stimulus/rubric revisions and
        define typed semantic diff model. Evidence: comparison schema.
  - [x] M4.15.b Diff stem/instructions/sources/objectives/standards and
        response/ options/interaction/scoring/key separately. Evidence: content
        diffs.
  - [x] M4.15.c Diff rationales/feedback/hints/exposure, accessibility/locales/
        media, estimates/psychometrics, gates/reviews/use impacts. Evidence:
        protected/status diffs.
  - [x] M4.15.d Render role-redacted accessible comparison and exact revision/
        provenance/impact without leaking answer data. Evidence: UI tests.
  - [x] M4.15.e Record select/reject/request-changes/merge/supersede with actor,
        rationale, evidence, immutable source refs, and new revision if merged.
        Evidence: decision tests.
  <!-- M4.15 completion evidence (2026-09-01):
       - Exact typed comparison: `item_comparison.py` and
         `item_comparison_helpers.py` publish closed schema version 4.15.0 and
         resolve authorized selected-response, written-response, math/code, and
         media/interactive histories by draft, revision, and revision digest.
         Exact stimulus locators on the immutable item source are resolved
         through authorized history, optional client stimulus digests are
         verified, unlinked or wrong-revision pins fail closed, and embedded
         written-response scoring guides expose exact rubric revision witnesses.
         The snapshot binds item/stimulus/rubric provenance and exactly 21
         separately typed semantic fields.
       - Semantic and protected proof: stem, instructions, sources, objectives,
         standards, response, options, interaction, scoring, key, rationales,
         feedback, hints, exposure, accessibility, locales, media, estimates,
         psychometrics, gates/reviews, and use impacts each retain independent
         change state, count, digest, and explanation. Scoring, key, rationale,
         feedback, and hint values never enter the stored snapshot: only a
         protected structural summary and nonreversible digest remain. Missing
         psychometric, exposure, and usage state stays explicitly unknown.
       - Decisions and persistence: migration 027 chains from 026 and adds
         comparison and decision ledgers with same-family/distinct-revision,
         decision-shape, merge-witness, supersession, and composite
         comparison/tenant foreign-key constraints plus two UPDATE/DELETE
         rejection triggers. Select, reject, request-changes, merge, and
         supersede records bind actor, rationale, attributed evidence, both
         immutable source revision hashes, optimistic latest-decision pins, and
         content-addressed witnesses. Merge dispatches through the real typed
         append service and records its newly attributed item revision; existing
         authority can change only through an explicit exact-latest supersede.
         Tampered comparison and decision payloads fail closed.
       - API and workbench: four instructor-only operations publish comparison
         creation/catalog, exact workspace retrieval, and decision append through
         deterministic OpenAPI and generated TypeScript contracts. The linked
         comparison press renders exact revision folios, a keyboard-scrollable
         and category-filterable 21-row proof sheet, a conspicuous protected
         boundary, consequence registers that display unknown state, an
         immutable decision folio, and a responsive authority desk. Learners are
         denied before comparison transport, invalid evidence is rejected in the
         browser, and the protected sentinel never renders. A desktop rendered
         visual pass confirmed the hierarchy and redaction boundary.
       - Verification: 29 focused backend tests including live PostgreSQL pass;
         the affected M4.1-M4.15 and OpenAPI set passes 489 tests with 14 expected
         environment-gated database skips. Ruff passes 13 focused files, strict
         mypy passes five source files, 48 OpenAPI/API contract tests pass, and
         canonical OpenAPI export plus generated-client drift checks pass. Full
         Metis web lint and typechecking pass with zero warnings, and all 71 web
         unit files / 296 tests pass. Eighteen M4.15 Playwright executions pass
         serially across Chromium, Firefox, and WebKit with automatic
         serious/critical Axe analysis; 102 adjacent item-bank, authoring,
         answer-gate, and accessibility cross-browser regressions also pass,
         including clean WebKit confirmation of the one transient retried case.
         Clean 001->027 and 027->026->027 migrations pass. Live PostgreSQL
         preserves one comparison, one merge decision, and three total item
         revisions, verifies all constraints and two immutable triggers, and
         rejects both UPDATE and DELETE.
       - Evidence boundary: deterministic `metis-item-comparison.json` hashes
         every implementation, test, migration, API/client contract, UI,
         traceability, and producer input and asserts M4.15.a-M4.15.e. Browser
         API transport uses deterministic test doubles. This does not prove
         production deployment, representative educator usability, physical
         assistive-technology behavior, or formal accessibility conformance. A
         Next production build was not run under the less-than-32-GiB host
         approval gate; affected compile, lint, type, unit, contract, database,
         accessibility, responsive, and cross-browser gates pass. The disposable
         verification database was removed after use.
       -->
- [x] M4.16 Build test/section assembly with blueprint constraints and exposure
      controls. Evidence: deterministic solver fixtures.
  - [x] M4.16.a Define assessment/section blueprint for objective/standard/
        content/cognitive level/item kind/difficulty/time/points/locale/
        accessibility/security/exposure constraints. Evidence: schema tests.
  - [x] M4.16.b Build authorized candidate pool from exact eligible item/
        stimulus/rubric revisions and current gates/rights/calibration state.
        Evidence: eligibility tests.
  - [x] M4.16.c Solve required/optional/min/max/balance/enemy/group/stimulus/
        ordering/randomization constraints deterministically with seed/version.
        Evidence: solver goldens.
  - [x] M4.16.d Enforce prior/current exposure, overlap, reuse windows, secure
        pools/forms, and institution policy without leaking bank membership.
        Evidence: exposure tests.
  - [x] M4.16.e Return complete solution, infeasible diagnostics, alternatives,
        blueprint coverage, estimate, and human decision; never silently relax
        constraints. Evidence: assembly tests.
  <!-- M4.16 completion evidence (2026-09-01):
       - Closed blueprint and authorized pool: `item_assembly.py` publishes
         closed schema version 4.16.0 for all ten planning dimensions plus exact
         count, time, points, required/optional, min/max, balance, enemy, group,
         stimulus, fixed-position, order, seed, exposure, reuse, secure-pool,
         and institution-policy constraints. The service resolves each exact
         item, stimulus, rubric, estimate, and learning-link revision through
         authorized history, then requires current accessibility, answer,
         leakage, rights, calibration, exposure, and operational evidence.
         Institution and receipt issuer UUIDs are bound to the authorized tenant
         and authenticated actor before candidate resolution; stale, missing,
         foreign, future, or expired evidence fails closed.
       - Deterministic solving and diagnostics: solver version
         `item-assembly-solver:1.0.0` canonicalizes and content-addresses the
         candidate pool, uses SHA-256 seed ranks for selection and display, and
         evaluates the complete constraint set without a relaxation path. The
         order-independent golden solution digest is
         `19797660f46e8693e10c63388dca44c292c84e3cd50d41624b12e22882342be9`.
         Search is bounded at 500,000 nodes. Complete results retain exact
         selection/order, coverage, points, time, section stimulus use, and
         whole-form prior overlap; infeasible results retain bounded exact
         witnesses and explicitly unapplied alternatives.
       - Persistence, authority, and API: migration 028 chains from 027 and adds
         immutable assembly and decision ledgers with closed checks, composite
         tenant/run ownership, idempotency, and two UPDATE/DELETE rejection
         triggers. Accept, reject, and request-changes decisions bind attributed
         evidence, the exact assembly digest, and the exact predecessor digest.
         Reads independently reconstruct and verify the decision chain and
         reject forks, cycles, orphans, cross-run witnesses, and acceptance of an
         infeasible result. Four instructor-only operations publish catalog,
         assembly creation/read, and decision append through deterministic
         OpenAPI and generated TypeScript contracts; learner requests are denied
         before bank or secure-pool transport.
       - Workbench: the linked assembly desk presents the manifest and preflight
         boundary, explicit exact-form verdict, eligibility ledger, selected
         order, ten-dimension coverage, points/time/overlap totals, diagnostics,
         unapplied alternatives, and immutable human-decision folio. Browser
         builders reject malformed, incomplete, relaxed, duplicate, wrong-tenant,
         or wrong-issuer manifests before transport. The proof ledger remains
         keyboard-scrollable without page overflow at 390 pixels, and a rendered
         desktop visual inspection confirmed its hierarchy and protected-data
         boundary.
       - Verification: 52 focused M4.16 backend tests including live PostgreSQL
         pass. The affected item-authoring, answer-gate, QTI, migration, API,
         OpenAPI, and generated-contract matrix passes 562 tests with no skips.
         Ruff passes 13 focused files and strict mypy passes five source files.
         Clean 001->028 and 028->027->028 migrations pass; live PostgreSQL
         preserves one assembly and two chained decisions and rejects both
         UPDATE and DELETE. Full Metis web lint/typechecking pass with zero
         warnings, and all 72 web unit files / 303 tests pass. Twenty-seven
         M4.16 Playwright executions pass serially across Chromium, Firefox, and
         WebKit with automatic serious/critical Axe analysis. The 201-case
         adjacent item-authoring matrix passes across all three engines; its one
         retried Firefox timing assertion was synchronized to the new request
         and its complete stimulus-set spec then passes 12/12 without retry.
       - Evidence boundary: deterministic `metis-item-assembly.json` hashes all
         implementation, migration, contract, test, UI, traceability, and
         producer inputs and asserts M4.16.a-M4.16.e. Browser API transport uses
         deterministic test doubles. The pinned institution policy is an
         authenticated actor attestation, not an external policy-registry
         signature. This does not prove production deployment, representative
         educator usability, physical assistive-technology behavior, or formal
         accessibility conformance. A Next production build was not run under
         the less-than-32-GiB host approval gate; affected lint, type, unit,
         contract, migration, database, accessibility, responsive, visual, and
         cross-browser gates pass. The disposable verification database was
         removed after use.
       -->
- [x] M4.17 Build secure preview that cannot record a learner attempt or
      contaminate calibration. Evidence: event and gradebook assertions.
  - [x] M4.17.a Use the real delivery renderer with exact draft build and
        synthetic preview identity/session marked nonlearner/nonoperational.
        Evidence: preview contracts.
  - [x] M4.17.b Isolate preview responses/events/files/timers/cookies/storage
        from learner attempt, gradebook, integrity, analytics, calibration, LTI,
        and credential systems. Evidence: side-effect tests.
  - [x] M4.17.c Render learner-visible content only; expose protected answer/
        scoring overlays through a separately authorized author mode. Evidence:
        property tests.
  - [x] M4.17.d Simulate scoring/feedback/navigation/accommodations/offline with
        disposable state and explicit preview labels. Evidence: behavior tests.
  - [x] M4.17.e Assert no real attempt/grade/event/calibration/exposure count is
        written across completion, refresh, crash, and malicious requests.
        Evidence: integration assertions.
  <!-- M4.17 completion evidence (2026-09-01):
       - Exact nonoperational renderer: closed schema version 4.17.0 requires
         tenant, family, draft, revision, revision SHA-256, content SHA-256,
         locale, seed, and content-addressed build witnesses. Each selected,
         written, math/code, and media/interactive revision is resolved through
         authorized exact history and projected through its existing learner-
         only builder. The workbench imports the same `AssessmentQuestion`
         component used by live assessment; the contract fixes its live import
         path and requires identical learner-document, build, and renderer
         witnesses while keeping preview chrome outside that renderer.
         Synthetic identity `preview_nonlearner` has no enrollment, attempt,
         grade, event, calibration, or exposure authority. Sessions expire in
         5-30 minutes, issue no learner or bearer token, are nonresumable, and
         retain at most 64 local events in browser parent memory.
       - Isolation and protected material: all 18 attempt/submission/grade/
         gradebook/event/xAPI/Caliper/analytics/integrity/calibration/exposure/
         credential/LTI/notification/cookie/local-storage/IndexedDB/file sinks
         are explicitly denied. Responses, events, files, and timers remain in
         React parent memory; file responses retain metadata and hashes only,
         with no upload bytes. Refresh, crash, reset, and actual reload discard
         the state, and browser tests find no preview cookie or storage key. The
         service requires a clean SQLAlchemy unit of work, disables autoflush,
         and fails if resolution dirties it. Learner documents are
         adversarially scanned for protected key/value families. Answers,
         scoring, feedback, rationales, misconceptions, hidden tests, expected
         values, and moderation details appear only in the distinct instructor-
         only author-overlay operation carrying literal scope
         `item-preview:protected-author-mode`; that response never re-embeds the
         learner surface.
       - Disposable behavior and API: three instructor-only preview operations
         publish closed OpenAPI and generated TypeScript contracts for prepare,
         simulation, and protected overlay; learners are rejected before the
         service or browser preview transport runs. The simulator covers exact
         selected-response scoring, written human-review state, exact math and
         media hooks, disconnected code execution, feedback, item/review/
         complete navigation, elapsed time, accommodations, online/offline
         behavior, event bounds, expiry, state tampering, and malicious family
         mismatch. Every result returns content-addressed state plus a zero-
         side-effect receipt. The linked responsive workbench states the
         nonattempt boundary before transport, and rendered desktop inspection
         confirmed the learner-frame hierarchy and separated protected overlay.
         No model or migration is introduced because the feature is
         intentionally stateless.
       - Verification: 19 focused M4.17 backend tests pass, including a live
         PostgreSQL gate that authors and edits a real draft, appends its real
         selected-response revision, resolves that exact history through the
         production service, then applies completion, refresh, crash, malicious
         response, and protected-overlay paths. Counts remain exactly unchanged
         before and after for submissions, xAPI statements, estimate revisions,
         estimate audit events, bank-search exposure revisions, assembly runs,
         and assembly decisions. The affected 70-file item-authoring, all-family,
         exact-history, accessibility, assembly, estimate, exposure, QTI,
         migration, API, and OpenAPI matrix passes 421 tests with no skips.
         Ruff passes nine focused files, strict Python 3.12 mypy passes four
         source files, and the deterministic OpenAPI export and generated client
         checks pass. Metis web lint/typechecking pass with zero warnings, and
         all 73 web unit files / 308 tests pass. Twenty-one M4.17 Playwright
         executions pass serially across Chromium, Firefox, and WebKit with
         automatic serious/critical Axe analysis, responsive 390-pixel checks,
         actual reload, reduced-motion/high-contrast, offline, and role-denial
         coverage.
       - Evidence boundary: deterministic `metis-item-preview.json` hashes the
         implementation, contracts, tests, real renderer, workbench,
         traceability, and evidence producer and asserts M4.17.a-M4.17.e.
         Browser API transport uses deterministic test doubles; live service
         and database isolation are exercised separately. Code execution stays
         disconnected and media scoring simulates the exact hooks without an
         operational player. This does not prove production deployment,
         representative educator usability, physical assistive-technology
         behavior, or formal accessibility conformance. A Next production build
         was not run under the less-than-32-GiB host approval gate; affected
         lint, type, unit, contract, database, accessibility, responsive,
         visual, and cross-browser gates pass. The disposable verification
         database was removed after use.
       -->
- [x] M4.18 Build bulk import with per-item diagnostics and no silent drops.
      Evidence: imported plus rejected equals input count.
  - [x] M4.18.a Define supported source formats/profiles, import batch/item IDs,
        limits, options, duplicate policy, and per-item outcome contracts.
        Evidence: import schema.
  - [x] M4.18.b Parse archives/files/rows with type/size/depth/count/path/macro/
        formula/external-ref protections and retain immutable source. Evidence:
        hostile import tests.
  - [x] M4.18.c Validate/migrate each item/stimulus/rubric/media/source/answer/
        accessibility/standard field independently with exact locator
        diagnostics. Evidence: validation fixtures.
  - [x] M4.18.d Preview create/update/skip/reject/conflict/loss/substitution and
        require authorization before committing successful items. Evidence:
        preview tests.
  - [x] M4.18.e Commit each accepted item idempotently with provenance and keep
        rejected inputs/diagnostics without partial silent fields. Evidence:
        persistence tests.
  - [x] M4.18.f Assert imported+updated+skipped+rejected equals discovered input
        items and reconcile nested assets/stimuli. Evidence: count invariant.
  <!-- M4.18 completion evidence (2026-09-01):
       - Closed intake contracts: schema version 4.18.0 publishes three source
         formats (Oshun JSON, CSV, and a bounded QTI 3 package subset), four
         exact profiles, content-addressed source/batch/item/preview/commit
         witnesses, hard nonrelaxable request limits, four policy families,
         seven preview outcomes, four commit dispositions, and exact bare-host
         HTTPS allowlists. A new idempotency key creates a distinct preview of
         the same bytes so an existing binding is visibly reconciled as skip;
         exact request replay returns the original immutable workspace.
       - Bounded parsing and no silent drops: source bytes, length, media type,
         path, and SHA-256 are verified before parsing and retained verbatim.
         JSON/legacy JSON, RFC-style CSV, and every declared QTI item resource
         are parsed with file/archive/row/item/node/cell/depth/size/compression
         limits. Traversal, normalized duplicate paths, symlinks, encrypted or
         nested archives, active macros/scripts, spreadsheet formulas, XML
         declarations/entities/stylesheets, unsafe or nonallowlisted external
         references, MIME drift, digest drift, and decompression bombs fail
         closed. Blank and overflow CSV rows, unknown package/item/CSV fields,
         non-XML declared QTI item resources, unreferenced hostile XML, assets,
         and archive entries receive explicit retained diagnostics or loss
         outcomes rather than disappearing.
       - Independent diagnosis and authorization: item, stimulus, rubric,
         media, source, answer, accessibility, and standard categories each
         receive a field status. Diagnostics carry exact source file, path,
         archive entry, row, JSON pointer, field, code, severity, observed
         value, and stable ID. Legacy and QTI migrations surface as explicit
         substitutions. Duplicate state yields create/update/skip/conflict;
         unknown fields yield reject/loss; invalid inputs remain reject. Commit
         requires the exact preview hash, one exact item decision per input, a
         literal instructor authorization scope, and a separate affirmative
         substitution bit. Protected answers and rationales remain in the
         immutable import ledger but are excluded from draft capture context.
       - Persistence and reconciliation: migration 029 adds append-only batch,
         item, commit, and publisher-binding tables with tenant-scoped
         idempotency, checks, foreign keys, indexes, content hashes, and UPDATE/
         DELETE rejection triggers. Accepted creates and updates use the real
         item-draft service and append hash-chained publisher bindings; rejected
         raw inputs and diagnostics stay queryable. Live PostgreSQL proved
         exact commit replay, create+reject, repeat skip+reject, update, and a
         revert to an earlier publisher version through one draft with four
         revisions and three binding revisions. Every preview and commit obeys
         its discovered-accounting equation, and nested asset/stimulus receipts
         are explicit and hash-verified.
       - Workbench: following the frontend skill, the linked instructor intake
         desk uses a restrained paper/ink/rust ledger rather than cards. It
         establishes scope, profiles, and hard limits before file transport;
         computes exact browser byte witnesses; renders every item, all eight
         field states, exact expandable diagnostics, and nested reconciliation;
         separates per-item decisions, substitution acceptance, and final
         exact-preview authorization; and ends with the immutable commit
         equation. A rendered desktop visual inspection confirmed the intended
         hierarchy and protected-material boundary.
       - Verification: 33 focused M4.18 backend tests including two live
         PostgreSQL gates pass. The affected import, item-draft, QTI, data-
         ownership, persistence, migration, API, export, and OpenAPI matrix
         passes 110 tests with no skips. Ruff passes 13 focused files; strict
         Python 3.12 mypy passes five source files; deterministic OpenAPI export
         and generated-client checks pass. Clean 001->029 and 029->028->029
         migrations pass; all four persistence tables reject UPDATE and DELETE.
         Metis web lint/typechecking pass with zero warnings, and all 74 web
         unit files / 312 tests pass. Eighteen M4.18 Playwright executions pass
         serially across Chromium, Firefox, and WebKit with automatic serious/
         critical Axe analysis, keyboard, role-denial, exact-byte, exact-
         authorization, and responsive 390-pixel coverage.
       - Evidence boundary: deterministic `metis-item-import.json` hashes all
         implementation, migration, contract, test, UI, traceability, and
         evidence-producer inputs and asserts M4.18.a-M4.18.f. Browser API
         transport uses deterministic test doubles; the live service and
         database paths are exercised separately. This does not prove
         production deployment, representative educator usability, physical
         assistive-technology behavior, or formal accessibility conformance. A
         Next production build was not run under the less-than-32-GiB host
         approval gate; affected lint, type, unit, contract, migration,
         database, accessibility, responsive, visual, and cross-browser gates
         pass. The disposable verification database was removed after use.
       -->

### M5 — Pedagogical, quality, psychometric, and fairness gates

- [x] M5.1 Implement objective coverage and construct-alignment gates. Evidence:
      crafted aligned and misaligned curricula.
  - [x] M5.1.a Compute direct/indirect introduced/practiced/assessed coverage,
        depth/weight/cognitive-level distribution, and stale/missing links from
        exact revisions. Evidence: coverage fixtures.
  - [x] M5.1.b Compare content/activity/item/rubric demands to objective
        criteria and intended construct; detect underrepresentation and
        construct- irrelevant variance. Evidence: alignment corpus.
  - [x] M5.1.c Apply versioned required/advisory thresholds by
        course/assessment/ risk and persist raw coverage separately. Evidence:
        verdict tests.
  - [x] M5.1.d Surface conflicting/unknown/N/A/manual-review state and
        remediation; block required misalignment. Evidence: gate UI/API tests.
      <!-- M5.1 evidence (2026-09-01): implemented a closed `5.1.0` exact-
           revision source contract over the M2.15 coverage snapshot, a
           deterministic server-side raw measurement, and versioned
           `metis.objective-construct-alignment` policy `2026.09.1`. The raw
           layer computes direct/indirect introduced, practiced, and assessed
           counts; declared weights; fixed depth and cognitive distributions;
           exact current/stale/missing/unsupported/duplicate/conflicting link
           state; criterion and intended-construct representation; and
           construct-irrelevant learner-demand signals across content,
           activity, item, and rubric revisions. Four course/assessment ×
           standard/high-stakes profiles each carry all 12 required/advisory
           threshold metrics. Unknown and conflicting evidence pauses in
           manual review, N/A remains explicit, confirmed required failures
           block release, and every finding receives named human remediation
           without an automatic content mutation.
         - Persistence/API/UI: Alembic 030 adds separate append-only
           `objective_alignment_raw_measurements` and
           `objective_alignment_gate_runs` ledgers with a tenant-bound foreign
           key, exact source/raw/policy/verdict/run hashes, idempotency, release
           constraints, and UPDATE/DELETE rejection. Four instructor-only
           operations expose the policy register, run catalog, gate creation,
           and exact workspace retrieval. The generated OpenAPI client is
           current. `/create/alignment-gates` follows the mandatory frontend
           skill as a raw-first curriculum-audit folio with an evidence spine,
           role distributions, exact link ledger, criterion/construct/variance
           comparison, explicit state ledger, remediation, history, and sticky
           release verdict; it is linked from the Create workflow.
         - Verification: crafted aligned, required-misaligned, stale, missing,
           unknown, conflicting, and not-applicable curricula pass the focused
           suite. The affected coverage/OpenAPI/export/release matrix passes 74
           tests with no skips; strict Python 3.12 mypy passes five source
           modules. A fresh PostgreSQL 001->030 migration, 030->029->030 round
           trip, and live persistence gate pass; raw reuse/new-verdict
           reapplication, tenant isolation, and both ledgers' mutation guards
           are exercised. Metis web lint/typechecking pass with zero warnings;
           all 75 unit files / 315 tests pass. Twenty-one Playwright executions
           pass serially across Chromium, Firefox, and WebKit with automatic
           serious/critical Axe analysis, keyboard operation, learner denial
           before transport, exact policy/source binding, aligned/misaligned/
           unknown/conflicting/N/A rendering, remediation, full-page visual
           inspection, and 390-pixel responsive coverage.
         - Evidence boundary: deterministic
           `metis-objective-alignment.json` hashes every implementation,
           migration, contract, corpus, test, UI, traceability, and evidence-
           producer input and asserts M5.1.a-M5.1.d. Browser API transport uses
           deterministic test doubles; the live service and database path is
           exercised separately. A passing declared-evidence gate is not proof
           of learner attainment or independent assessment validity, and this
           work does not establish production deployment, representative
           educator usability, physical assistive-technology behavior, or
           formal accessibility conformance. A Next production build was not
           run under the less-than-32-GiB host approval gate; affected lint,
           type, unit, contract, migration, database, accessibility,
           responsive, visual, and cross-browser gates pass.
       -->
- [x] M5.2 Implement reading-level and sentence-complexity measurements.
      Evidence: independently derived known values.
  - [x] M5.2.a Pin supported language/content applicability and formula/feature/
        tokenizer/normalization versions. Evidence: measurement schema.
  - [x] M5.2.b Compute reading indices and sentence/word/syntactic distributions
        server-side with exact units and excluded-content rules. Evidence:
        independent known-value fixtures.
  - [x] M5.2.c Report raw values/ranges/assumptions/uncertainty/N/A separately
        from learner-band policy. Evidence: result tests.
  - [x] M5.2.d Verify technical terms, math/code, quotations, multilingual,
        short, and malformed text behavior. Evidence: edge-case corpus.
  <!-- M5.2 completion evidence (2026-09-01):

       - The existing lesson readability boundary is upgraded in place to
         analysis schema 1.1.0 and deterministic English engine 2.0.0 rather
         than duplicated. Its profile independently pins formula and feature
         sets at 2.0.0 plus tokenizer, terminal-punctuation sentence segmenter,
         and offset-preserving casefold normalization at 1.0.0. Twelve typed
         block rules name the exact learner-facing fields for six prose-bearing
         kinds and return six code/math/media/reference kinds as N/A; seven
         versioned block/span/word exclusion rules state separately what remains
         in raw evidence and what leaves policy arithmetic.
       - The server now returns four exact frequency distributions for eligible
         words per sentence, heuristic syllables per eligible word, subordinate
         markers per sentence, and clause proxies per sentence. Every result
         carries its population, exact unit, sample and excluded-observation
         counts, minimum, P25, median, P75, P90, maximum, mean, frequency points,
         formula version, and pinned inclusive-linear-interpolation method.
         Decimal output uses ROUND_HALF_UP. A hand-derived fixture with sentence
         lengths [2,5], syllables [1,1,3,3,2,3,1], markers [0,1], and clause
         proxies [1,2] independently proves the four distributions plus Flesch
         Reading Ease 34.08 and Flesch-Kincaid Grade 9.38.
       - Raw measures, raw ranges, exact anchored/excluded spans, assumptions,
         uncertainty, block N/A, and derived-measure N/A are structurally
         separate from the unchanged learner-band policy. Fully quoted prose,
         for example, retains three raw words while reporting eligible words as
         zero and the reading indices as explicit N/A, never numeric zero. The
         policy produces no suggestion without eligible evidence, computes no
         aggregate score, and never rewrites content. New reviews persist the
         exact engine version used for analysis while legacy schema/engine 1.0.0
         review records remain loadable.
       - A checked-in seven-case corpus executes technical-term, executable-code,
         mathematical-notation, quotation, intentional-multilingual, short, and
         malformed-punctuation behavior. It proves raw-versus-policy term and
         quotation handling, code/math N/A, intentional multilingual N/A, a
         single unterminated sentence, and deterministic repeated/missing
         terminal punctuation. The editorial proof sheet exposes the five
         method versions, exact content/exclusion registries, a selectable
         frequency/percentile measurement ruler, and metric-level N/A before the
         policy overlay. The existing exact-witness review and immutable human
         decision flow remains intact.
       - Automated proof passes Ruff format/lint for six affected Python files,
         strict MyPy for three affected service modules, 112 readability/dossier/
         API/OpenAPI tests, live OpenAPI artifact and generated-client drift
         checks, Metis web ESLint and TypeScript checks, 316 Vitest tests across
         75 files, thirteen deterministic evidence tests, and fifteen axe-backed
         Playwright journeys across Chromium, Firefox, and WebKit. Browser proof
         includes exact version/distribution switching, explicit N/A, policy
         separation, immutable review, role restriction, unsupported code,
         390px containment, and reduced motion. Current traceability and input
         checksums are published at
         `evidence/v1-workbenches/metis-lesson-readability.json`.

       This is inspectable local contract, service, and browser-runtime evidence,
       not proof of production deployment, representative learner/author
       usability, observed comprehension, institutional band validity, formal
       accessibility conformance, or assistive-technology behavior. No database
       migration was required because readability review records remain in the
       existing typed dossier ledger. A Next production build was not run under
       the less-than-32-GiB host approval gate; affected lint, type, unit,
       contract, generated-client, accessibility, responsive, visual, and
       cross-browser gates pass.
       -->
- [x] M5.3 Implement item clarity and ambiguity rules. Evidence: curated
      adversarial corpus.
  - [x] M5.3.a Define versioned rules for incomplete context, unclear referent/
        instruction/response format, negatives, absolutes, double-barreled
        prompts, cues, language, and construct-irrelevant complexity. Evidence:
        rule registry.
  - [x] M5.3.b Run deterministic rules with exact text/field anchors and
        locale/item-kind applicability. Evidence: known-value tests.
  - [x] M5.3.c Run attributed model/human-assist checks only for semantic
        ambiguity with uncertainty and counterinterpretations. Evidence:
        calibrated assist tests.
  - [x] M5.3.d Require human resolution for blocking ambiguity and retain
        dismissed findings/reasons. Evidence: adversarial corpus gate.
  <!-- M5.3 completion evidence (2026-09-01):

       - Metis now publishes item-clarity schema 5.3.0 and content-addressed
         rule profile `metis.item-clarity-rules` 1.0.0. Eleven individually
         versioned rules cover incomplete context, unclear referents,
         instructions and response formats, negative and absolute wording,
         double-barreled prompts, answer cues, language clarity,
         construct-irrelevant complexity, and semantic ambiguity. Every rule
         pins its evaluator, default severity, source families, item kinds,
         locales, field purposes, and rule digest.
       - The service normalizes exact, hash-bound text fields from native
         selected-response, written-response, math/code, media/interactive, and
         performance-evidence item revisions. Deterministic findings carry the
         exact field path, character offsets, excerpt, and field digest. A
         twelve-case adversarial corpus includes a clean control, every ten
         deterministic categories, and non-calibrated-locale behavior; direct
         fixture tests also verify all four specialized revision normalizers.
       - Semantic ambiguity remains a separate attributed assist path. Model
         and human assists pin provider, method, configuration, and conditional
         model identities and hashes; pass/finding/uncertain/timeout/error/N/A
         outcomes remain visible. A semantic finding requires uncertainty and
         at least two counterinterpretations at an exact current anchor. Assists
         remain advisory and cannot decide a finding or grant release.
       - Three append-only PostgreSQL ledgers preserve raw scans, complete human
         decisions, and release gates. Every finding must be confirmed,
         resolved, or dismissed; resolve/dismiss decisions require explicit
         evidence, retained dismissed-finding IDs, reasons, and the original
         raw finding. Confirmed blocking ambiguity fails closed, while revision
         and lineage pins make stale scans, reviews, and gates non-releasable.
         Five instructor-only API routes expose prepare, scan, review, gate, and
         history flows through the closed OpenAPI contract and generated client.
       - The item-bank clarity desk follows the frontend-skill proofing-desk
         hierarchy: exact source text and selected anchor dominate, the compact
         finding ledger exposes applicability and assist attribution, and the
         resolution inspector requires evidence before gating. Existing item
         authoring desks link into the workflow. Learners are denied before API
         transport, and the three-pane layout reflows into a linear 390 px flow.
       - Executable evidence is frozen at
         `evidence/v1-workbenches/metis-item-clarity.json`. Verification passes:
         76 focused backend/schema/helper/service/API/migration/OpenAPI tests; a
         separate live PostgreSQL 031 probe spanning native authoring, scan,
         dismissal, and release plus rejected mutation of all three ledgers;
         targeted Ruff and strict MyPy; byte-current OpenAPI and TypeScript
         client generation; affected web lint and TypeScript; the full Metis
         web unit suite (76 files / 318 tests); and 15 Chromium/Firefox/WebKit
         Playwright executions covering exact requests and anchors, blocking
         and dismissal gates, retained findings, attributed semantic evidence,
         automatic serious/critical Axe analysis, role denial, and responsive
         reflow. Visual inspection at full desktop resolution confirmed the
         intended restrained proofing hierarchy.
       - These controls establish the bounded M5.3 technical implementation;
         they do not establish production deployment, representative educator
         usability, formal accessibility conformance, or physical
         assistive-technology behavior. A Next production build was not run
         under the less-than-32-GiB host approval gate; affected lint, type,
         unit, contract, migration, live-database, accessibility, responsive,
         visual, and cross-browser gates pass.
       -->
- [x] M5.4 Implement distractor plausibility and non-overlap rules. Evidence:
      known item fixtures.
  - [x] M5.4.a Check duplicates/normalization/subsumption/intersection and
        mutually compatible options under stated response rules. Evidence:
        non-overlap fixtures.
  - [x] M5.4.b Check implausibility, grammatical mismatch, length/style/cue
        imbalance, absolutes, joke options, and source/rationale inconsistency.
        Evidence: plausibility fixtures.
  - [x] M5.4.c Apply kind/locale/discipline-specific rules and expose exact
        option pairs/findings without guessing learner ability. Evidence:
        applicability tests.
  - [x] M5.4.d Bind verdict to item revision/rules and require review of
        semantic findings. Evidence: gate lifecycle tests.
  <!-- M5.4 completion evidence (2026-09-01):

       - Metis now publishes distractor-quality schema 5.4.0 and the
         content-addressed `metis.distractor-quality-rules` 1.0.0 profile.
         Thirteen individually versioned rules cover normalized duplicates,
         subsumption, enumerated intersection, single-response compatibility,
         facial implausibility, grammar, length/style/cue balance, absolute
         wording, joke options, and source/rationale consistency. Exact
         selected-response revision, key partition, cardinality, locale,
         discipline, sources, option text/rationale, rule definitions, and
         ordered option-pair digests remain bound to every scan.
       - Deterministic checks run only inside their recorded response-type,
         locale, and discipline applicability. Compatibility and source/
         rationale consistency accept only attributed model or human-assist
         evidence with exact method/config/model identity, uncertainty, and at
         least two counterinterpretations. The result explicitly disclaims any
         learner-ability or domain-truth inference.
       - Human review must decide every semantic finding. Resolve/dismiss
         decisions require governed evidence; deterministic blockers cannot be
         dismissed and instead require a new item revision. The release gate
         fails closed on changed item/rule/scan/review witnesses, confirmed
         semantic findings, or deterministic blockers while retaining every
         raw finding and disposition.
       - Alembic 032 chains from 031 and adds scan, semantic-review, and gate
         ledgers with database-level append-only triggers. Five instructor-only
         endpoints expose prepare, scan, review, release-gate, and immutable
         history operations. The generated OpenAPI client is current. The
         restrained option-quality workbench exposes exact pair selection,
         applicability reasons, attribution, evidence dispositions, and gate
         history beside the existing misconception/statistics workbench.
       - Machine-readable evidence is frozen at
         `evidence/v1-workbenches/metis-distractor-quality.json`: 13 known-item
         cases, ten deterministic and three semantic categories, four
         discipline profiles, five routes, three storage tables, and three
         immutable triggers. Its generator/test require four satisfied claims
         for every M5.4 subitem plus storage/API/generated-client/workbench
         coverage and current input checksums.
       - Verification passed: 74 focused M5.4/OpenAPI tests; 145 selected-
         response, distractor-mapping, distractor-quality, migration, service,
         API, and OpenAPI regression tests; one live PostgreSQL migration/
         semantic-dismissal/three-trigger mutation test; targeted Ruff and
         Python 3.12 mypy checks; OpenAPI export and generated-client checks;
         the complete Metis web unit suite (77 files / 320 tests); zero-warning
         web lint and typecheck; and 33 Chromium/Firefox/WebKit Playwright
         executions across the existing and new distractor desks. The new 15
         executions cover exact revision/applicability requests, option-pair
         focus, non-dismissible deterministic defects, attributed competing
         interpretations, mandatory resolution/dismissal evidence, retained
         raw evidence, fail-closed/pass gates, learner denial, 390px reflow,
         and automatic serious/critical Axe analysis. Desktop visual inspection
         confirmed the intended manuscript/finding/disposition hierarchy.
       - These controls establish the bounded M5.4 technical implementation;
         they do not establish production deployment, representative educator
         usability, or formal accessibility conformance. A Next production
         build was not run under the less-than-32-GiB host approval gate;
         affected lint, type, unit, contract, migration, live-database,
         accessibility, responsive, visual, and cross-browser gates pass.
       -->
- [x] M5.5 Implement answer uniqueness and rationale-consistency gates.
      Evidence: mutation tests.
  - [x] M5.5.a Verify key cardinality/uniqueness/equivalence/tolerance against
        item-kind response/scoring contract. Evidence: key tests.
  - [x] M5.5.b Verify keyed answers satisfy the stem/sources/assumptions and
        nonkeys do not, with explicit unsupported/manual state. Evidence:
        correctness tests.
  - [x] M5.5.c Verify correct and distractor rationales refer to stable response
        IDs and agree with key, scoring, feedback, and hints. Evidence:
        consistency tests.
  - [x] M5.5.d Mutation-test option/key/order/scoring/rationale/source changes
        and require invalidation/review. Evidence: mutation suite.
  <!-- M5.5 completion evidence (2026-09-01):

       - Metis now publishes answer-consistency schema 5.5.0 for the four
         native selected-response kinds. Every scan binds the exact response
         IDs, authored order/text, key kind and members, scoring method,
         equivalence/tolerance policy, stem/source assumptions, source
         references, rationales, learner feedback, and hint policy. Decimal
         tolerance bands and equivalence groups are evaluated without
         lossy-number conversion.
       - Deterministic checks enforce key cardinality, stable references,
         normalized uniqueness, authored order, scoring alignment, and every
         response-surface reference. Attributed source-checker, human-assist,
         or exact-identity model evidence records whether the key satisfies the
         stem/sources/assumptions and each nonkey fails them. Unsupported/manual
         judgments remain explicit and require two counterinterpretations;
         they are never silently converted into correctness claims.
       - Correct and distractor rationales, feedback, and the explicitly
         non-authored hint surface are joined through stable response IDs and
         checked against key, scoring, and correctness evidence. Human review
         must decide semantic uncertainty with governed evidence. Deterministic
         defects cannot be dismissed, raw findings remain immutable, and the
         release gate fails closed on stale scans or changed witnesses.
       - Alembic 033 chains from 032 and adds append-only scan, review, and gate
         ledgers with database-enforced update/delete rejection. Five
         instructor-only prepare/scan/review/release/history endpoints use
         idempotency, optimistic pins, immutable lineage, and exact witness
         verification. The generated OpenAPI contract and TypeScript client
         are current. The manuscript-first workbench exposes the exact key,
         scoring, truth matrix, assumptions, stable-ID learning surfaces,
         mutation witnesses, dispositions, and release history.
       - Mutation coverage rebuilds real selected-response sources after
         option text, key, order, scoring, rationale, source-reference, and
         learner-feedback edits and proves the exact invalidation reason. A
         direct component-hash matrix additionally covers the governed hint
         surface, while item-revision-only mutation has its own reason.
         Machine-readable evidence is frozen at
         `evidence/v1-workbenches/metis-answer-consistency.json`; its generator
         and tests require all four subitem claims, 15 finding categories,
         eight component hashes, nine invalidation reasons, storage/API/client/
         workbench coverage, and current input checksums.
       - Verification passed: 85 focused M5.5/OpenAPI tests; 158 selected-
         response, ambiguity, answer-consistency, API, migration, service, and
         OpenAPI regressions including three live PostgreSQL integrations; the
         new 033 integration proves all three ledger triggers reject mutation;
         targeted Ruff and Python 3.12 mypy; OpenAPI export and generated-client
         checks; the complete Metis web unit suite (78 files / 323 tests);
         zero-warning web lint and typecheck; and 36 Chromium/Firefox/WebKit
         Playwright executions across the existing and new answer desks. The
         new 18 executions cover exact scan witnesses, explicit manual state,
         stable-ID blockers, evidence-required semantic dismissal, retained raw
         findings, exact mutation reasons, learner denial, 390 px reflow, and
         automatic serious/critical Axe analysis. Desktop visual inspection
         confirmed the intended manuscript/correctness/mutation-gate hierarchy,
         and the production-source stub-indicator scan is clean.
       - These controls establish the bounded M5.5 technical implementation;
         they do not establish production deployment, representative educator
         usability, independent domain-truth proof, or formal accessibility
         conformance. A Next production build was not run under the
         less-than-32-GiB host approval gate; affected lint, type, unit,
         contract, migration, live-database, accessibility, responsive,
         visual, and cross-browser gates pass.
       -->
- [x] M5.6 Implement rubric weight, criterion coverage, level ordering, and
      descriptor-distinctness gates. Evidence: exact fixtures.
  - [x] M5.6.a Validate weights/points/totals/ranges/rounding/caps/floors and
        missing/N/A scoring for the selected rubric method. Evidence: arithmetic
        fixtures.
  - [x] M5.6.b Verify required objectives/evidence/performance dimensions are
        covered without duplicate or orphan criteria. Evidence: coverage tests.
  - [x] M5.6.c Verify level codes/scores/ranges are strictly ordered/nonoverlap/
        complete and descriptor direction matches scale. Evidence: ordering
        fixtures.
  - [x] M5.6.d Detect identical/subsumed/contradictory/nonobservable descriptors
        with human review for semantic distinctness. Evidence: descriptor
        corpus.
  - [x] M5.6.e Bind raw checks/verdicts to rubric revision and invalidate
        dependent item/assessment approval on change. Evidence: lifecycle tests.
  <!-- M5.6 completion evidence (2026-09-01):

       - Metis now publishes rubric-quality schema 5.6.0 over the existing
         immutable lesson-rubric revision rather than introducing a second
         rubric authority. The selected analytic weighted-points contract is
         checked for exact 10000-basis-point criterion and per-item totals,
         representative points, complete/nonoverlapping ranges, half-up
         rounding, zero-valued missing scores, N/A reweighting, and explicit
         unconfigured caps/floors. Native Decimal fixtures prove full = 100.00,
         missing = 40.00, and N/A reweight = 100.00.
       - Seventeen deterministic/semantic categories cover required objective,
         evidence, and performance-dimension coverage; orphan and duplicate
         criteria; ordered codes, scores, and ranges; normalized-identical,
         subsumed, contradictory, nonobservable, unsupported/manual, and
         wrong-direction descriptors; and dependent approval freshness.
         Semantic findings retain attribution, uncertainty, evidence, and two
         competing interpretations where meaning is unsupported/manual. Human
         review must disposition every semantic finding, dismissal requires
         evidence, and deterministic findings cannot be dismissed.
       - Six content-addressed receipts bind scoring, weights, coverage, level
         order, descriptors, and dependent approvals to the server-issued
         rubric revision. Those receipts plus rubric-revision lineage map to
         seven exact invalidation reasons. A changed rubric invalidates item
         and assessment approvals; explicitly invalidated dependencies may be
         evaluated but cannot be published. Three append-only ledgers and
         database triggers preserve scans, raw findings, human reviews, and
         release gates. Five instructor-only prepare/scan/review/release/history
         endpoints enforce idempotency, optimistic pins, immutable lineage,
         tenant scope, and witness verification. OpenAPI, the generated client,
         and the proof-first authoring desk expose the same closed lifecycle.
       - Machine-readable evidence is frozen at
         `evidence/v1-workbenches/metis-rubric-quality.json`; its generator and
         tests require all five subitem claims, nine arithmetic fixtures, all
         17 finding categories, six governed component hashes, seven mutation
         reasons, storage/API/client/workbench coverage, and current input
         checksums. Verification passed: 87 focused M5.6/OpenAPI tests; 158
         selected lesson-rubric, dossier, M5.6, API, migration, service, and
         OpenAPI regressions; the live PostgreSQL 034 integration proving all
         three ledger triggers reject mutation; targeted Ruff and Python 3.12
         mypy; OpenAPI export and generated-client checks; the complete Metis
         web unit suite (79 files / 326 tests); zero-warning web lint and
         typecheck; and 33 Chromium/Firefox/WebKit Playwright executions across
         the existing rubric workbench and new quality desk. The 18 new
         executions cover exact request witnesses, manual descriptor evidence,
         non-dismissible structural defects, evidence-required dismissal,
         retained raw findings, evaluation/publication separation, approval
         rebinding, learner denial before transport, 390 px reflow, and
         automatic serious/critical Axe analysis. Desktop and mobile visual
         inspection confirmed the intended manuscript/proof/release hierarchy,
         and the production-source stub-indicator scan is clean.
       - These controls establish the bounded M5.6 technical implementation;
         they do not establish production deployment, representative educator
         usability, independent descriptor-truth proof, or formal accessibility
         conformance. A Next production build was not run under the
         less-than-32-GiB host approval gate; affected lint, type, unit,
         contract, migration, live-database, accessibility, responsive,
         visual, and cross-browser gates pass.
       -->
- [x] M5.7 Implement accessibility completeness gates per item modality.
      Evidence: missing alternative assets block publication.
  - [x] M5.7.a Resolve applicable checklist/support requirements from item kind,
        media/interaction, target, locale, and PNP profile. Evidence:
        applicability matrix.
  - [x] M5.7.b Verify structure/keyboard/focus/timing/drag/color/motion/zoom/
        reflow/instruction/error requirements. Evidence: interaction gate tests.
  - [x] M5.7.c Verify modality-specific alt/long descriptions, table/math/code
        semantics, captions/transcripts/descriptions/sign, and noninteractive
        equivalents. Evidence: asset gate tests.
  - [x] M5.7.d Validate referenced alternative asset
        revision/locale/sync/rights/ availability and outcome equivalence.
        Evidence: alternative tests.
  - [x] M5.7.e Block publication on missing/stale/failed mandatory requirements;
        preserve N/A/manual review with evidence. Evidence: publication gate.
      <!-- completion-evidence:
       - `evidence/v1-workbenches/metis-accessibility-completeness.json` is the
         deterministic M5.7 manifest. It binds the current item revision and
         M4.14 registry/audit/gate revisions, resolves all 27 inherited checks
         plus 13 alternative kinds across finite target/locale/PNP cells, and
         retains explicit attributed automatic, manual, and N/A evidence.
       - The fail-closed evaluator independently verifies requested/resolved
         asset revision, source hash, locale, target/profile, synchronization,
         rights, availability, automated or reviewer-attributed outcome
         equivalence, authority hash, and receipt hash. Missing alternatives
         are incomplete; stale or failed witnesses are blocked. Sixteen finding
         categories and five item/baseline/policy/matrix mutation reasons keep
         publication closed without erasing the original assessment findings.
       - Alembic 035 adds separate append-only assessment and publication-gate
         ledgers. Four instructor-only API operations, exact OpenAPI/generated
         TypeScript contracts, and the accessibility publication desk expose
         preparation, assessment, gate, history, coverage, blockers, immutable
         lineage, and the explicit Isis authority boundary.
       - Verification passed 69 focused schema/helper/service/API/migration/
         OpenAPI tests plus the live PostgreSQL integration at migration head
         035. The live probe released a complete item and proved both ledger
         triggers reject UPDATE and DELETE. The selected M4.7 media-authoring,
         M4.14 accessibility, M5.7 completeness, and OpenAPI regression set
         passed 139 tests; Ruff format/lint, strict Python 3.12 mypy, OpenAPI
         drift, and generated-client checks are clean.
       - The complete Metis web unit suite passed 80 files / 330 tests with
         zero-warning lint and typecheck. Eighteen new and 36 combined
         Chromium/Firefox/WebKit Playwright executions cover the contextual
         matrix at 390 px, explicit N/A/manual rows, exact asset receipts,
         missing-asset blocking, baseline mutation, learner denial before
         transport, and automatic serious/critical Axe analysis. The targeted
         production-source stub-indicator scan is clean.
       - These controls establish the bounded M5.7 technical implementation.
         Alternative-asset authority receipts remain attributed inputs rather
         than live Isis reads; M5.8 owns that integration. This does not claim
         production deployment, physical assistive-technology validation,
         representative learner usability, or formal conformance. A Next
         production build was not run under the less-than-32-GiB host approval
         gate; targeted contract, database, lint, type, unit, accessibility,
         responsive, and cross-browser gates pass.
       -->
- [x] M5.8 Implement media technical and caption/transcript gates through Isis.
      Evidence: live contract integration.
  - [x] M5.8.a Map Metis media kind/target/profile/accessibility needs to
        canonical Isis asset/gate/profile identities and versions. Evidence:
        integration mapping.
  - [x] M5.8.b Request/read exact asset revision technical measurements and
        captions/transcript/sync gates through audience-authorized contracts.
        Evidence: live contract tests.
  - [x] M5.8.c Revalidate gate freshness against media/profile/evaluator/policy
        versions and represent unavailable/not-configured/partial honestly.
        Evidence: freshness tests.
  - [x] M5.8.d Translate results to Metis lesson/item/build eligibility without
        copying or overriding Isis raw evidence. Evidence: authority tests.
  - [x] M5.8.e Propagate media replacement/quarantine/recall/gate change and
        block affected publication. Evidence: lifecycle integration test.
      <!-- completion-evidence:
       - `evidence/v1-workbenches/metis-media-readiness.json` is the
         deterministic M5.8 manifest. It binds the canonical Isis gallery
         projection, authenticated BFF routes, fail-closed Metis HTTP client,
         exact-read eligibility service, lesson attachment/delivery path,
         OpenAPI artifacts, generated clients, workbench, and test corpus.
       - Isis now publishes one target/locale-specific media-readiness witness
         for each exact asset and rendition revision. Three media kinds and
         four targets resolve canonical profiles over eight accessibility
         needs. Technical, caption, transcript, and synchronization gates bind
         current profile, policy, evaluator, asset/alternative revisions,
         timestamps, expiry, and evidence; missing, unavailable,
         not-configured, partial, failed, stale, or mismatched required state is
         ineligible. Non-applicable gates remain explicit and evidence-free.
       - The existing audience-authorized Isis query/resolve contract remains
         the sole authority. Tenant, actor, and Metis audience are derived from
         authentication rather than request fields, and exact resolution is
         private/no-store and re-evaluates recall, quarantine, expiry, profile,
         evaluator, policy, measurements, and gate state. Metis forwards the
         caller bearer and signed exact selection; learner access is denied
         before the authority call.
       - The instructor-only `/api/media-readiness/evaluations` endpoint
         translates the live result for lesson, item, and assessment-build
         subjects. Authorized callers receive an ephemeral Isis snapshot for
         inspection, while the separate Metis eligibility artifact contains
         only identities, versions, states, expiry, and content-addressed
         fingerprints with `raw_isis_evidence_copied=false`. Governed lesson
         attachments likewise retain only readiness profile/policy identities,
         authority revision, and snapshot hash—never raw measurements or Isis
         evidence locators.
       - Learner delivery re-resolves every governed attachment. Selection,
         kind, digest, profile/policy, authority-revision, or readiness-snapshot
         change withholds media and removes its asset from preview. Executed
         fixtures cover replacement, recall, quarantine, technical
         unavailability, caption not-configuration, partial transcript, policy
         change, evaluator change, and readiness gate change without silently
         substituting another revision.
       - Verification passed 82 focused Metis gallery/readiness/dossier
         regressions, 12 Isis output-gallery/BFF/runtime-contract tests, and
         three focused Metis OpenAPI checks. OpenAPI exports and both generated
         clients are current; targeted Ruff/format/lint, Metis web and BFF
         typechecks, and changed-source lint are clean. The complete Metis web
         unit suite passed 81 files / 333 tests. Thirty Chromium, Firefox, and
         WebKit executions cover exact witnesses, all three eligibility scopes,
         unavailable/not-configured/partial states, recall/quarantine/gate
         change, learner denial, the raw-authority boundary, 390 px reflow, and
         automatic serious/critical Axe analysis. The targeted production-source
         stub-indicator scan is clean.
       - The full BFF lint target was also executed with a supervised 6 GiB V8
         heap and remains red on its unrelated existing backlog (150 errors / 265
         warnings across pre-existing Aja, Hathor, Yemaya, and boundary files);
         no finding names the curated-gallery files changed for M5.8. The BFF
         typecheck, focused route tests, runtime-drift test, changed production
         lint, and OpenAPI formatting checks pass.
       - These controls establish the bounded M5.8 live-contract integration;
         they do not claim a deployed production Isis probe, production
         deployment, representative educator usability, or formal
         accessibility conformance. A Next production build was not run under
         the less-than-32-GiB host approval gate; targeted contract, lint, type,
         unit, accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M5.9 Implement declared-versus-empirical difficulty checks. Evidence:
      calibrated fixture.
  - [x] M5.9.a Resolve declared target/band/source and empirical item revision,
        population/sample, method, statistic, uncertainty, and calibration
        status. Evidence: input schema.
  - [x] M5.9.b Compare on a ratified common scale or explicit mapping with
        tolerance/uncertainty; prohibit raw incomparable subtraction. Evidence:
        comparison fixtures.
  - [x] M5.9.c Return aligned, material mismatch, insufficient, stale, N/A, or
        conflicting state with direction/magnitude. Evidence: verdict tests.
  - [x] M5.9.d Route mismatch to item/blueprint review and never overwrite
        empirical or author-declared values. Evidence: calibrated lifecycle
        test.
      <!-- Completion evidence (2026-09-01):
      `evidence/v1-workbenches/metis-item-difficulty.json` is the deterministic,
      checksum-bound M5.9 manifest.
       - The instructor-only evaluation contract pins one immutable M4.10
         estimate-set revision/hash and one exact code-owned policy
         ID/version. It resolves the existing current author and ratified
         empirical-calibration sources without introducing a second source of
         truth. Declared evidence carries target, bounded band/label, scale,
         exact source identity, method, interval method, and uncertainty;
         empirical evidence carries exact item/context and evidence pins,
         population, sample identity/size, method, statistic, interval,
         uncertainty method, collection window, and ratification state.
       - The sealed policy names the common probability-proxy difficulty scale,
         tolerance, minimum empirical sample, and two exact, hashed,
         dual-ratified mappings for author and psychometric scale v1. Mapping
         schemas require complete bounded coverage and strict monotonicity.
         Evaluation proceeds only when both sides already share the common
         scale or exactly one explicit ratified mapping exists; missing or
         conflicting mappings withhold the comparison and
         `raw_incomparable_subtraction_performed` remains literally false.
       - The interval/tolerance evaluator returns all six required states:
         aligned, material mismatch, insufficient, stale, not applicable, and
         conflicting. Comparable mismatches report empirical-harder or
         empirical-easier direction plus the material gap beyond the accepted
         band. Stale exact pins remain distinguishable from insufficient and
         contradictory evidence rather than being collapsed into a score.
       - Material mismatch and mapping conflict route explicitly to item and
         blueprint review. The response is marked evaluation-only with literal
         false declared/empirical overwrite flags. The calibrated lifecycle
         test snapshots every source, revision-row count, and audit-row count
         before evaluation and proves all remain unchanged afterward, so the
         derived check adds no persistence table or mutation path.
       - The estimate observatory now presents an evidence-led declared versus
         empirical proof panel with source bands, empirical sample/method,
         common scale, tolerance, direction, gap, reason codes, immutable-value
         guard, and review links. Learners remain denied before estimate
         transport. Visual trace inspection confirmed the desktop composition;
         the first browser pass also found and fixed a serious color-contrast
         issue before the final gate.
       - Verification passed 28 focused difficulty/lifecycle backend tests, all
         43 Metis OpenAPI contract tests, and the existing estimate migration
         plus live PostgreSQL immutability gate. Ruff and formatting passed on
         eight changed Python/test files; strict Mypy passed four production
         source files. The OpenAPI export and generated Metis TypeScript client
         are current. Metis web lint, focused E2E lint, and typecheck passed; the
         complete web unit suite passed 82 files / 335 tests. Thirty-six
         Chromium, Firefox, and WebKit executions cover the prior estimate
         lifecycle plus aligned, both-source mismatch proof, insufficient,
         stale, not-applicable, and conflicting presentations, exact request
         pins, review routes, immutability copy, learner denial, 390 px reflow,
         and automatic serious/critical Axe analysis. The targeted
         production-source stub-indicator scan is clean.
       - These controls establish the bounded M5.9 comparison and review
         control plane. They do not claim a connected production psychometric
         provider, production deployment, representative educator usability,
         or formal accessibility conformance. A Next production build was not
         run under the less-than-32-GiB host approval gate; targeted contract,
         migration, live-database, lint, type, unit, accessibility, responsive,
         and cross-browser gates pass.
       -->
- [x] M5.10 Implement IRT parameter bounds and fit diagnostics. Evidence:
      1PL/2PL/3PL known-value fixtures.
  - [x] M5.10.a Validate model-specific difficulty/discrimination/guessing/
        scaling/linking parameter presence, numeric bounds, standard errors, and
        covariance. Evidence: parameter fixtures.
  - [x] M5.10.b Validate convergence, identifiability, sample/item sufficiency,
        information, residual/global/item fit, and local dependence. Evidence:
        diagnostic fixtures.
  - [x] M5.10.c Distinguish 1PL/2PL/3PL applicability and prohibit unsupported
        parameter interpretation across models. Evidence: model matrix.
  - [x] M5.10.d Persist raw diagnostics/tool/config/version separately from
        calibrated/flagged/ineligible verdict. Evidence: result tests.
      <!-- Completion evidence (2026-09-01):
      `evidence/v1-workbenches/metis-irt-diagnostics.json` is the deterministic,
      checksum-bound M5.10 manifest.
       - The closed instructor/admin contract pins an exact learner-safe item
         revision, run/input/output/sample/population/ability-scale identities,
         software build and runtime, configuration version/hash/seed, model
         family, intended use, completion time, and every raw diagnostic. A
         versioned code-owned policy defines operational bounds for b, a, c,
         and D, allowed scaling constants, model-specific sample/item minima,
         identification and information limits, residual/global/item-fit
         limits, and local-dependence Q3. The policy records its sources and
         limitations and carries a reproducible SHA-256 witness.
       - Estimated parameters require a finite value, nonnegative standard
         error, and ordered interval containing the value; fixed parameters
         require a reason and prohibit estimated uncertainty. The full
         estimated-parameter covariance matrix must contain exactly every cell,
         be symmetric and positive semidefinite, and reconcile each diagonal to
         the corresponding squared standard error. Linked transformations pin
         an exact target scale, unique anchor revisions, method/evidence,
         positive slope, uncertainty, and a reconciled 2x2 covariance; unlinked
         runs cannot claim transformation inputs.
       - Exact known-value 1PL/2PL/3PL fixtures execute the logistic response and
         item-information formulas. Every submitted information point is
         recomputed from the raw b/a/c/D values; conditional standard error must
         equal one over square-root information. The evaluator produces one
         typed check for each of thirteen categories: model applicability,
         bounds, covariance, convergence, identification, sample sufficiency,
         item sufficiency, information, residuals, global fit, item fit, local
         dependence, and linking. Core failures are ineligible; structurally
         sound runs with fit/dependence concerns are flagged; otherwise they are
         calibrated.
       - The complete model matrix fixes a=1 and c=0 for 1PL, estimates a while
         fixing c=0 for 2PL, and estimates a/c for 3PL; all families estimate b
         and use one supported fixed D. Unsupported roles, unavailable required
         values, inadequate covariance, nonconvergence, nonidentification,
         insufficient sample/items/information, or rejected linking fail
         closed and expose no parameter interpretations. Review-only fit flags
         remain distinct from ineligibility.
       - Alembic 036 adds separate append-only
         `item_irt_diagnostic_runs` and `item_irt_diagnostic_verdicts` tables
         with tenant-bound foreign keys, idempotency, exact hashes, and database
         triggers rejecting update/delete. Raw JSON contains the exact tool,
         configuration, and diagnostics but no policy or verdict. Verdict rows
         persist the exact policy/checks/status separately and enforce
         `promotion_applied = false`; the live PostgreSQL test also proves the
         M4.10 estimate-revision count is unchanged.
       - The read-only calibration ledger presents model/verdict filtering,
         parameter uncertainty, raw tool receipt, sample/convergence/
         identification/linking evidence, all thirteen checks, independent raw,
         policy, and verdict hashes, and a prominent no-promotion receipt. Its
         catalog integrity-checks compact run summaries instead of loading every
         raw information curve; selecting a run loads and revalidates the full
         evidence workspace. Learners are denied before diagnostic transport.
         Visual inspection confirmed the desktop composition; the first
         automated browser pass found and corrected one serious footer contrast
         defect.
       - Verification passed 28 focused M5.10 backend tests, including the
         migration and live PostgreSQL separation/immutability/no-promotion
         gate. Ruff and strict Mypy pass on the production contract, model,
         evaluator, persistence, and API files; OpenAPI export and the generated
         TypeScript client are current. Metis web lint/typecheck and focused
         data tests pass. Twelve Chromium, Firefox, and WebKit executions cover
         1PL/2PL/3PL, calibrated/flagged/ineligible, raw/verdict separation,
         filters, empty state, learner denial, 390 px reflow, and automatic
         serious/critical Axe analysis. The complete OpenAPI and web-unit gates,
         targeted production-source stub scan, and directly affected evidence
         manifests are also current and passing.
       - These controls establish the bounded M5.10 diagnostic control plane;
         they do not claim a connected production psychometric provider,
         production deployment, universal IRT thresholds, representative
         psychometrician usability, or formal accessibility conformance. A Next
         production build was not run under the less-than-32-GiB host approval
         gate; targeted contract, migration, live-database, lint, type, unit,
         accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M5.11 Implement sample-size and standard-error eligibility for calibration
      promotion. Evidence: low-evidence items cannot become calibrated.
  - [x] M5.11.a Define minimum effective sample, response distribution,
        administration diversity, data quality, missingness, and standard-error/
        confidence criteria by model/use. Evidence: eligibility policy.
  - [x] M5.11.b Calculate included/excluded/weighted counts and privacy-safe
        cohort coverage from exact attempt/item revisions. Evidence: sample
        tests.
  - [x] M5.11.c Verify parameter/statistic standard errors and convergence
        against all required thresholds without treating missing as zero.
        Evidence: boundary tests.
  - [x] M5.11.d Block promotion to calibrated when any required evidence is low/
        stale/conflicting and route field-test/manual review. Evidence: state
        transition tests.
  <!-- M5.11 completion evidence (2026-09-01):
       - The closed `5.11.0` contracts and content-addressed `2026.09.1`
         policy define all nine 1PL/2PL/3PL x pilot/operational/research
         profiles. Each profile pins minimum included/effective samples,
         distinct learners, response-category count/rate, administrations,
         institutions, coverage, missingness, invalidity, weight ratio,
         privacy-cell size, freshness, and required parameter/statistic SE,
         confidence-level, and interval-width limits. Thresholds are explicit
         Metis operational policy; automatic promotion is literally false.
       - Exact pseudonymous attempt revisions pin the learner, item revision,
         administration, institution, occurrence, eligibility/exclusion,
         outcome, weight, and approved coverage dimensions. The pure sample
         reducer reconciles base/included/excluded and outcome counts, exclusion
         reasons, sum of weights and squared weights, Kish effective n, distinct
         learner/administration/institution counts, missing/invalid/coverage
         rates, and privacy-safe released/suppressed cell counts. Tests exercise
         nonuniform weights and prove no cohort token or cell identity is
         returned.
       - The evaluator emits exactly ten typed checks over exact revisions,
         effective sample, response distribution, administration diversity,
         data quality, missingness, uncertainty, M5.10 convergence/fit,
         freshness, and privacy coverage. Every required estimated parameter
         and reported statistic must carry a non-missing SE and interval;
         missing remains insufficient, never zero. Exact inclusive policy
         boundaries are floating-point tolerant, while excessive SE, inadequate
         confidence, excessive interval width, nonconvergence, and noncalibrated
         M5.10 verdicts fail closed.
       - Low/sparse evidence routes to `field_test` / field-data collection;
         stale, conflicting, duplicate, quality-failed, or convergence-failed
         evidence routes to psychometric manual review. Only a completely
         passing assessment receives a content-addressed, time-bounded
         eligibility receipt for a separate human ratification step. The M4.10
         empirical-calibration boundary now requires that exact matching,
         untampered, unexpired M5.11 receipt; no M5.11 path creates or mutates an
         estimate and every record asserts `promotion_applied = false`.
       - Alembic 037 adds separate tenant-bound append-only promotion-input and
         assessment ledgers linked to the exact M5.10 verdict, with idempotency,
         policy/sample/check hashes, status/route/expiry constraints, and
         update/delete rejection triggers. Live PostgreSQL tests prove eligible
         versus low-evidence routing, idempotent replay, cross-tenant denial,
         append-only enforcement, compact catalog integrity, no receipt for low
         evidence, and no new calibrated estimate revision.
       - The instructor-only promotion-control workbench presents the sample
         funnel, effective n, response balance, diversity, missing/invalid
         rates, privacy-safe coverage, exact policy profile, all ten checks, the
         required route, and either a bounded receipt or an explicit blocked
         receipt. Learners are denied before transport. Visual inspection
         confirmed the evidence-led desktop composition; responsive automation
         proves the full ledger reflows without horizontal overflow at 390 px.
       - Verification passed 19 focused M5.11 evaluator/API tests, 24 trusted
         calibration regression tests, the static migration guard, both live
         PostgreSQL gates, and all 43 Metis OpenAPI contract tests. Ruff, strict
         Mypy at the Python 3.12 runtime, OpenAPI export/client checks, targeted
         web lint/typecheck, the production stub scan, 84 files / 339 web unit
         tests, eight evidence-generator tests, and twelve Chromium/Firefox/
         WebKit workbench executions with automatic serious/critical Axe
         analysis all pass.
       - These controls establish the bounded M5.11 calibration-promotion gate;
         they do not claim a connected production psychometric provider,
         production deployment, universal psychometric thresholds,
         representative psychometrician usability, or formal accessibility
         conformance. A Next production build was not run under the
         less-than-32-GiB host approval gate; targeted contract, migration,
         live-database, lint, type, unit, accessibility, responsive, and
         cross-browser gates pass.
       -->
- [x] M5.12 Implement DIF analysis across ratified cohorts with minimum sample
      and privacy rules. Evidence: synthetic fairness corpus.
  - [x] M5.12.a Define ratified privacy-reviewed cohort dimensions/reference/
        focal groups, method/model, matching variable, thresholds, correction,
        minimum samples, and authorized purpose. Evidence: DIF policy.
  - [x] M5.12.b Build deidentified analysis inputs with exact item revisions,
        valid responses/weights, missingness, group privacy thresholds, and no
        small-cell outputs. Evidence: data tests.
  - [x] M5.12.c Compute uniform/nonuniform DIF effect/significance/uncertainty/
        model fit with pinned tools and multiple-testing handling. Evidence:
        synthetic corpus.
  - [x] M5.12.d Return insufficient/N/A/error/conflict and subgroup-safe results
        separately from fairness decision. Evidence: result tests.
  - [x] M5.12.e Require psychometric/fairness human review before restrict/
        revise/retire; prohibit individual inference. Evidence: decision tests.
  <!-- M5.12 completion evidence (2026-09-01):
       - The closed `5.12.0` contracts and content-addressed `2026.09.1`
         policy ratify locale-family, language-support, region, and explicitly
         consented-demographic cohort dimensions. Each dimension pins its data
         classification, consent rule, reference/focal groups, authorized
         aggregate-item-fairness purpose, minimum source sample, and privacy
         floor. The analysis family pins weighted logistic regression, rest
         score matching, uniform and nonuniform materiality thresholds, a 0.05
         family alpha, Holm correction, quality/weight/model-fit limits, and
         explicitly prohibits automatic fairness decisions and individual
         inference.
       - Deidentified rows bind exact response, item, administration, cohort,
         occurrence-time, and pseudonymous learner revisions. The reducer
         reconciles valid/missing/invalid responses, binary outcomes, matching
         scores that exclude the analyzed item, positive weights, duplicate
         learner participation, weight dispersion, and cohort receipt seals.
         Release eligibility uses the maximum of the policy floor and both
         current ratified privacy-receipt floors. Every small cell suppresses
         counts, rates, weights, effects, significance, and uncertainty; no
         learner or cohort token enters a released result or API workspace.
       - The pinned pure-Python estimator fits intercept, centered rest score,
         focal-group, and focal-by-rest-score terms with deterministic weighted
         Newton-Raphson estimation and final observed information. It reports
         uniform/nonuniform coefficients, standard errors, two-sided Wald
         p-values, 95% intervals, convergence/fit evidence, and one Holm family
         across every eligible item-term hypothesis. The synthetic corpus
         recovers uniform, nonuniform, and null patterns and covers exact
         threshold, multiplicity, privacy, duplicate, and failure boundaries.
       - Eligibility is represented independently from the statistical finding
         and independently again from the human disposition and operational
         action. `eligible`, `insufficient`, `not_applicable`, `error`, and
         `conflict` remain distinct; all noneligible states fail closed and
         expose only subgroup-safe reasons. A signal is evidence for human
         review, never a causal finding of bias.
       - Alembic 038 adds tenant-bound append-only raw-input, analysis-run, and
         human-decision ledgers with idempotency, exact policy/evidence hashes,
         update/delete rejection triggers, and revalidation on retrieval.
         Live PostgreSQL tests prove eligible and suppressed persistence,
         cross-tenant denial, immutable inputs/runs/decisions, compact catalog
         integrity, and that review records never apply an item action.
       - Restrict/revise/retire requires an eligible signal plus two independent
         named approvals with psychometrician and fairness-review authority,
         matching dispositions/actions, and administrator submission. Null,
         insufficient, and not-applicable states have narrower allowed routes;
         error/conflict states allow no action. Every decision retains
         `action_applied = false`, leaving the actual lifecycle mutation to a
         separate operational control plane.
       - The instructor DIF review ledger keeps cohort eligibility, statistical
         evidence, human disposition, and operational action visibly separate;
         only administrators can submit dual review. The responsive 390 px flow
         has no horizontal overflow, the cohort ledger is keyboard-focusable,
         learners are denied before evidence transport, and automatic
         serious/critical Axe analysis passes in Chromium, Firefox, and WebKit.
       - Verification passed 27 focused M5.12 contract/evaluator/API/migration/
         live-PostgreSQL tests, 45 psychometric-authority regressions, all 43
         Metis OpenAPI contract tests, Ruff, strict Python 3.12 Mypy, current
         OpenAPI/client generation checks, targeted changed-file web lint, the
         full Metis web typecheck, 85 files / 342 web unit tests, the production
         stub scan, nine evidence-generator tests, and 15 three-engine browser
         executions with automated accessibility and mobile coverage.
       - These controls establish the bounded M5.12 DIF review control plane;
         they do not claim a connected production fairness dataset, production
         deployment, universal fairness thresholds, causal bias attribution,
         representative psychometric/fairness-reviewer usability, or formal
         accessibility conformance. A Next production build was not run under
         the less-than-32-GiB host approval gate; targeted contract, migration,
         live-database, lint, type, unit, accessibility, responsive, and
         cross-browser gates pass.
       -->
- [x] M5.13 Implement exposure and compromise-risk gates. Evidence: overexposed
      items are automatically restricted or retired.
  - [x] M5.13.a Define exposure metrics/windows/populations/channels,
        secure-item classification, thresholds, known breach signals, and
        lifecycle policy. Evidence: exposure schema.
  - [x] M5.13.b Count delivered/seen/responded/reused/exported/shared
        occurrences by exact item revision with preview/test exclusion and
        privacy-safe aggregation. Evidence: counting tests.
  - [x] M5.13.c Ingest verified compromise/leak/reconstruction reports with
        evidence, scope, confidence, and incident status. Evidence: report
        tests.
  - [x] M5.13.d Calculate normal/warning/restricted/quarantined/retired state
        and enforce pool/assembly/delivery/export changes server-side. Evidence:
        threshold tests.
  - [x] M5.13.e Propagate replacement/notification/blast radius while retaining
        historical attempts/grades and allowing authorized appeal. Evidence:
        incident journey.
  <!-- M5.13 completion evidence (2026-09-01):
       - The closed `5.13.0` contracts and content-addressed `2026.09.1`
         policy extend the binding `metis.protected-assessment:1.0.0`
         authority. They define delivered, seen, responded, reused, exported,
         and shared occurrences; seven delivery/export/share channels; learner,
         instructor, reviewer, institution, and public populations; standard,
         secure, and high-stakes classifications; 30/90-day rolling windows;
         per-class privacy floors; ordered warning/restriction/retirement
         thresholds; six known compromise signals; and the normal, warning,
         restricted, quarantined, and retired lifecycle.
       - Every occurrence pins an exact item UUID, revision, revision SHA,
         channel, population, environment, pseudonymous actor hash, context
         revision, optional administration revision, and occurrence time.
         Immutable batches accumulate across the exact revision, freeze its
         classification, reject duplicate occurrence IDs across batches, and
         serialize concurrent writers with a PostgreSQL advisory transaction
         lock. Preview/test and outside-window occurrences do not spend the
         budget. Aggregate and per-metric actor floors independently suppress
         counts; private weighted points can still automatically restrict or
         retire an item without releasing a small cell.
       - Administrator-only incident intake requires a known signal, scope,
         severity, open/contained/remediating/resolved status, at least 75%
         confidence, authenticated tenant verifier, ordered discovery/
         verification times, and one or more security-restricted evidence
         witnesses. Report, evidence, request, and stored-field hashes are
         revalidated; released workspaces contain compact incident witnesses,
         never raw occurrence tokens or forensic material.
       - Metric and incident precedence deterministically resolves all five
         states: warning/restriction/retirement thresholds, verified compromise
         restriction, high-severity quarantine, and critical/public/answer-key
         retirement. One persisted assessment drives four server decisions.
         Item-bank search and direct preview exclude blocked exact revisions;
         assembly denies before expensive candidate resolution; assessment GET
         and submit check the current exact indexed revision; selected-response,
         written-response, math/code, and media/interactive protected exports
         all deny from the same state. Standalone admission fails closed when
         no current M5.13 assessment exists.
       - Alembic 039 adds tenant-bound append-only occurrence-batch,
         compromise-incident, lifecycle-assessment, and appeal ledgers with
         idempotency, composite tenant foreign keys, state/enforcement checks,
         content hashes, and update/delete rejection triggers. Live PostgreSQL
         proves automatic retirement, all-surface denial, pool exclusion,
         tenant isolation, idempotent replay, immutable records, authorized
         appeal, and unchanged historical submission count.
       - Enforced states propagate replacement-required status, the affected
         context count or its privacy suppression, security/assessment-owner/
         institution-reviewer notification audiences, and affected-learner
         notification for public incidents. Attempts and grades are always
         preserved. An administrator may append an uphold/overturn decision;
         recomputation excludes an overturned incident but cannot erase metric
         exposure, the incident, the prior assessment, or appeal history.
       - The instructor exposure-control ledger presents lifecycle before the
         four admissions, then privacy-safe measurements, evidence witnesses,
         response/blast radius, history, and admin-only appeal. Learners are
         denied before evidence transport; the 390 px layout has no horizontal
         overflow; and automatic serious/critical Axe analysis passes in
         Chromium, Firefox, and WebKit.
       - Verification passed 31 focused M5.13 schema/counting/lifecycle/service/
         API/migration tests, 100 directly affected service regressions, the
         migration plus live-PostgreSQL gate, all 43 Metis OpenAPI contract
         tests, Ruff, strict Python 3.12 Mypy, current OpenAPI/client generation
         checks, targeted changed-file web lint, the full Metis web typecheck,
         86 files / 346 web unit tests, the production stub scan, nine evidence-
         generator tests, and 15 three-engine browser executions with automatic
         accessibility and mobile coverage.
       - These controls establish the bounded M5.13 exposure and compromise
         control plane. They do not claim connected production occurrence
         telemetry, an external breach feed, delivered outbound notifications,
         production deployment, universal security thresholds, representative
         security-reviewer usability, or formal accessibility conformance. A
         Next production build was not run under the less-than-32-GiB host
         approval gate; targeted contract, migration, live-database, lint, type,
         unit, accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M5.14 Implement content and answer freshness gates. Evidence: source or
      standard updates mark dependencies stale.
  - [x] M5.14.a Bind lesson/item/answer/rationale/feedback/hint to exact source/
        claim/citation/standard/objective/tool/policy revisions and review
        dates. Evidence: dependency tests.
  - [x] M5.14.b Detect source update/retraction, standard supersession, factual/
        temporal review expiry, dependency change, and conflicting current
        evidence. Evidence: freshness fixtures.
  - [x] M5.14.c Classify current/due/stale/blocked/unknown/N/A by content risk
        and use, preserving prior review evidence. Evidence: verdict tests.
  - [x] M5.14.d Propagate stale state to tutor prompts, assessments, builds, and
        publication/delivery according to policy. Evidence: propagation test.
      <!-- M5.14 bounded completion evidence (2026-09-01):
       - Added the versioned `metis.content-answer-freshness` policy, exact
         subject/dependency/review/signal/verdict contracts, deterministic
         hash sealing and evaluation, tenant-safe service/API, generated
         OpenAPI client, and Alembic 040. Applicable reviews cover all six
         named content kinds and bind any of the seven required dependency
         kinds to exact revision, SHA-256, authority, bound time, current
         observation, evidence witness, reviewer, and review/evaluation dates.
       - The evaluator detects source update/retraction, standard or other
         dependency supersession, exact revision drift, unavailable current
         authority, conflicting current evidence, missing observation, due
         windows, and factual/temporal expiry. Four risk bands and declared
         uses deterministically resolve the ordered current/due/stale/blocked/
         unknown/not-applicable vocabulary. Not-applicable is an explicit,
         reviewed low/moderate authoring-only result and cannot conceal
         learner-facing content.
       - `content_freshness_reviews`, `content_freshness_signals`, and
         `content_freshness_verdicts` are append-only, tenant-linked ledgers.
         Idempotency conflicts, cross-tenant reads, reporter mismatches, and
         evidence/hash drift fail closed. Dependency signals append new
         verdicts for every affected exact revision while retaining the sealed
         review and prior verdict history; PostgreSQL advisory locks serialize
         review and dependency writers.
       - Runtime admission re-evaluates the current clock and signal ledger,
         not merely the stored label. It executes before tutor session/start
         generation, assessment question delivery/submission and publication,
         lesson build, course/admin publication, and published learner
         delivery. Legacy integrated content without an applicable M5.14
         review remains available; the standalone admission API fails closed
         when an exact review is absent.
       - Added the instructor/admin Freshness dependency desk with a compact
         exact-revision queue, six-state rail, five-surface admission matrix,
         bounded review clock, reviewed-versus-observed dependency ledger,
         findings, immutable history, and admin-only dependency signal intake.
         Learners are denied before evidence transport. The restrained
         ink/linen ledger layout follows the frontend skill, uses amber only
         for operational change, supports reduced motion, and has no
         horizontal overflow at 390 px.
       - Deterministic evidence is serialized in
         `evidence/v1-workbenches/metis-content-freshness.json`. Verification:
         38 focused schema/helper/service/API/migration/propagation tests, 68
         affected service regressions, Alembic head plus one live PostgreSQL
         propagation/tenant/three-ledger immutability test, 47 OpenAPI/export
         tests, generated-client drift check, targeted Ruff and strict MyPy,
         targeted web lint and full web typecheck, 87 files / 351 web unit
         tests, production stub/random scan, eight evidence-generator tests,
         and 15 Chromium/Firefox/WebKit Playwright executions with automatic
         serious/critical axe analysis and mobile overflow coverage all pass.
       - These controls establish the bounded M5.14 freshness plane. They do
         not claim a connected production authority registry, periodic
         reconciliation scheduler, production deployment, representative
         reviewer usability, or formal accessibility conformance. A Next
         production build was not run under the less-than-32-GiB host approval
         gate; targeted contract, migration, live-database, lint, type, unit,
         accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M5.15 Implement cultural, regional, and language review assignments.
      Evidence: required locale reviewers block publication.
  - [x] M5.15.a Define required reviewer qualifications/locale/region/community/
        discipline/independence by content/item/audience/risk/target. Evidence:
        assignment policy.
  - [x] M5.15.b Match eligible reviewers without exposing protected attributes,
        apply workload/conflict rules, deadlines, substitutes, and escalation.
        Evidence: assignment tests.
  - [x] M5.15.c Provide exact locale variant/source/context/comparison/evidence
        and structured cultural/regional/language review decisions. Evidence:
        review tests.
  - [x] M5.15.d Require fresh decision/quorum for each applicable locale/region
        and block publication on missing/expired/conflicted assignment.
        Evidence: gate tests.
      <!-- M5.15 bounded completion evidence (2026-09-02):
       - Added the content-addressed `metis.cultural-regional-language-review`
         policy and closed contracts for eight content kinds, four audiences,
         four risk bands, three review dimensions, exact target revision/hash,
         locale, region, community context, discipline, qualifications,
         independence, workload, deadline, reserve, escalation, and fresh
         decision/quorum rules. High/critical scopes require both exact region
         and community context; audience and risk rules raise quorum up to four
         distinct reviewers per exact requirement.
       - Matching accepts only closed capability, availability, workload, and
         conflict/independence envelopes. Eight named protected attributes are
         rejected as extra input; the response is reduced to opaque reviewer
         identity, eligible requirement identities, reason codes, active load,
         and deterministic rank. Declared financial, institutional, or personal
         conflicts always disqualify; risk-specific independence separately
         controls authorship. Capacity limits, one reserve per requirement,
         assignment windows, conflict declarations, reserve activation, and
         overdue escalation are enforced server-side.
       - Review packets bind the exact target and locale variant to source and
         comparison hashes, audience/risk/discipline/region/community context,
         unique dimension-specific evidence, and a canonical packet hash.
         Structured findings cannot cite evidence outside the packet or evidence
         captured after the decision; material/change-required findings require
         exact evidence; approvals cannot conceal blocking or change-required
         findings. Decisions and their validity windows are hash sealed.
       - Alembic 041 adds tenant-linked append-only assignment-run, lifecycle-
         event, decision, and quorum-verdict ledgers with idempotency, composite
         tenant foreign keys, state/value checks, hash identities, and update/
         delete rejection triggers. Live PostgreSQL proves exact matching,
         idempotent assignment and admission replay, full locale/region/community
         quorum, tenant isolation, clock expiry, runtime denial/admission, and
         immutable ledgers.
       - Runtime admission recomputes every applicable exact requirement from
         current events, decisions, expiry, conflict, and target revision. It
         executes before course/admin and assessment publication, assessment
         delivery/submission, lesson build, and published lesson delivery.
         Explicitly registered pending, missing, negative, conflicted, expired,
         or superseded scopes deny before protected mutation, artifact writing,
         or learner delivery. Legacy content with no declared M5.15 scope remains
         available; the standalone admission API fails closed for a missing or
         mismatched registered witness.
       - Added the instructor/admin Locale review assignment desk: exact target
         and policy witness, privacy-reduced matching composer, locale/region/
         community quorum rail, assignment/deadline/reserve lifecycle, exact
         source-to-variant context/comparison/evidence sequence, structured
         decision form, and immutable history. Learners are denied before review
         transport; the 390 px layout has no horizontal overflow; entrance motion
         is transform-only with reduced-motion support; and automatic serious/
         critical Axe analysis passes in Chromium, Firefox, and WebKit.
       - Deterministic evidence is serialized in
         `evidence/v1-workbenches/metis-cultural-review.json`. Verification:
         24 focused M5.15 schema/helper/service/API/migration/propagation tests,
         including the live PostgreSQL gate; 57 affected service regressions;
         50 OpenAPI and release-coverage tests; targeted Ruff and strict Python
         3.12 Mypy; byte compilation; current OpenAPI export/generated-client
         checks; targeted zero-warning web lint and full web typecheck; 88 files
         / 355 Metis web unit tests; production stub/random scan; eight evidence-
         generator tests; and 18 Chromium/Firefox/WebKit Playwright executions
         with automatic accessibility, authorization, transport, keyboard, and
         mobile-overflow coverage all pass.
       - These controls establish the bounded M5.15 cultural-review assignment
         plane. They do not claim a connected production qualification registry,
         an independent protected-attribute audit, production deployment,
         representative reviewer usability, or formal accessibility conformance.
         Browser API transport is mocked. A Next production build was not run
         under the less-than-32-GiB host approval gate; targeted contract,
         migration, live-database, lint, type, unit, accessibility, responsive,
         and cross-browser gates pass.
       -->
- [x] M5.16 Persist raw measures independently from policy verdicts. Evidence:
      threshold changes recompute without data loss.
  - [x] M5.16.a Define immutable measurement records with subject, evaluator/
        model/code/config/corpus versions, inputs, values/units/regions,
        uncertainty/applicability, and hash. Evidence: schema tests.
  - [x] M5.16.b Define verdict records referencing exact measurements plus gate/
        policy/threshold/profile versions and rationale. Evidence: verdict
        schema.
  - [x] M5.16.c Recompute new verdict identities on threshold/policy change
        without modifying/reprocessing valid raw measures. Evidence: recompute
        tests.
  - [x] M5.16.d Require new measurement when subject/evaluator/input changes and
        retain all historical projections. Evidence: lifecycle invariants.
      <!-- Evidence (2026-09-02):
       - Added closed, immutable `RawMeasurementRequest` /
         `RawMeasurementRecord` contracts with exact subject revision hashes;
         evaluator and evaluator-artifact hashes; explicit model, code, config,
         and corpus used/not-applicable witnesses; ordered input snapshots;
         bounded regions; typed values and units; uncertainty, applicability,
         rationale, evidence references, execution time, input-set hash, and a
         canonical content-addressed `raw-measurement:<sha256>` identity. Raw
         contracts reject undeclared gate, policy, threshold, verdict, and
         outcome fields.
       - Added independent `PolicyVerdictRecord` contracts whose facts are only
         exact measurement ID/hash references. Gate, policy, profile, threshold
         snapshot, ordered rule decisions, rationale, predecessor, evaluation
         time, outcome, and `policy-verdict:<sha256>` identity remain in the
         policy projection. Unit mismatches, missing metrics, cross-subject
         projections, unchanged-policy recomputes, and history branching fail
         closed. The recompute request has no raw payload or replacement
         measurement-ID field.
       - Alembic revision 042 and SQLAlchemy models persist three separate
         append-only tenant-linked tables:
         `measurement_ledger_raw_measurements`,
         `measurement_ledger_policy_verdicts`, and
         `measurement_ledger_verdict_measurements`. Composite foreign keys bind
         verdict history to exact same-tenant raw records, a self-reference
         retains predecessor history, a one-successor constraint prevents
         branching, and PostgreSQL triggers reject every update/delete.
       - `/api/measurement-ledger` now exposes authenticated catalog, exact-
         subject workspace, raw append, initial projection, and policy-only
         recompute operations. Exact replay is content-idempotent. Live
         PostgreSQL tests prove a threshold change flips pass to fail while the
         raw count, raw identity, raw hash, and exact verdict-to-measurement link
         remain unchanged; all projections stay readable. Subject, evaluator,
         and input mutations append distinct raw facts. Unit tests additionally
         cover model, code, config, corpus, region, value, uncertainty, and
         applicability identity changes.
       - Added `/create/measurement-ledger`, a restrained fact-ledger workbench
         that keeps immutable readings, units, uncertainty, exact regions,
         inputs, component provenance, and hashes visually separate from the
         versioned policy lens. It supports raw append, first projection,
         successor recompute, full non-destructive history, role denial, text-
         labelled outcomes, reduced motion, and 390px responsive use. Browser
         assertions inspect outbound bodies to prove raw transport is policy-
         free and recompute cannot replace/resend a measurement.
       - Canonical generated evidence is
         `evidence/v1-workbenches/metis-measurement-ledger.json`. Verification:
         29 focused M5.16 schema/helper/API/migration tests including three live
         PostgreSQL lifecycle gates; 58 OpenAPI/export/release-coverage tests;
         targeted Ruff and strict Python 3.12 Mypy; byte compilation; current
         OpenAPI export and generated-client drift checks; targeted zero-warning
         web lint and full web typecheck; 89 files / 361 Metis web unit tests;
         production stub/random scan; eight evidence-generator tests; and 24
         Chromium/Firefox/WebKit Playwright executions with automatic Axe,
         authorization, transport, lifecycle, and mobile-overflow coverage all
         pass.
       - These controls establish the bounded M5.16 measurement-ledger plane.
         The richer M5.17 state vocabulary and M5.18 exact evaluator, corpus,
         policy, threshold, and environment registry are now integrated. They
         do not claim production registry deployment, representative steward
         usability, or formal accessibility conformance. Browser API transport
         is mocked. A Next
         production build was not run under the less-than-32-GiB host approval
         gate; targeted contract, migration, live-database, lint, type, unit,
         accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M5.17 Expose uncertainty, non-applicability, and insufficient-data states.
      Evidence: no unknown state renders as pass.
  - [x] M5.17.a Define pass/fail/advisory/manual-review/N/A/insufficient/
        unavailable/error/stale/unknown states and permitted transitions.
        Evidence: state vocabulary.
  - [x] M5.17.b Require reason/evidence/uncertainty/sample/applicability/
        remediation fields appropriate to each state. Evidence: schema tests.
  - [x] M5.17.c Aggregate mandatory gates without converting
        missing/unknown/N/A/ unavailable/insufficient into numeric zero or pass.
        Evidence: aggregation property tests.
  - [x] M5.17.d Render/export every state with text/semantics, freshness, and no
        color-only or false green representation. Evidence: UI/API tests.
      <!-- Evidence (2026-09-02):
       - Added a closed, content-addressed ten-state vocabulary for `pass`,
         `fail`, `advisory`, `manual_review`, `not_applicable`,
         `insufficient_data`, `unavailable`, `error`, `stale`, and `unknown`.
         Each state defines its mandatory-gate effect, required evidence fields,
         and remediation obligation. Seven explicit transition causes bind
         before/after state pairs to raw-measurement identity: policy-only
         recompute may change only derived state; new measurement, freshness
         expiry, evaluator failure/recovery, and manual review must carry the
         corresponding identity witness. Invalid transitions fail closed.
       - Measurement schema version 1.1 now carries typed observation/sample,
         freshness, applicability, uncertainty, reason/evidence, and remediation
         witnesses. Absolute, relative, and confidence-interval uncertainty is
         evaluated as an interval; a threshold crossing routes to manual review.
         Expired current evidence becomes stale, raw non-observed states remain
         non-observed, and legacy records without state evidence become unknown
         rather than inheriting a numeric pass.
       - Mandatory aggregation allows release only when every mandatory decision
         is explicitly `pass`; advisory-only rows do not satisfy mandatory
         requirements. Tests exhaust all 729 three-gate combinations over the
         nine non-advisory mandatory states, plus no-gate, legacy, malformed-
         evidence, uncertainty-crossing, and freshness-expiry cases. No missing,
         N/A, insufficient, unavailable, stale, error, manual-review, or unknown
         decision converts to numeric zero or pass.
       - Authenticated outcome-vocabulary and workspace-export APIs expose the
         exact vocabulary, current/history ledger, transition witness, and
         content hash. The measurement-ledger workbench presents the state word,
         release effect, reason, evidence, uncertainty, sample, applicability,
         freshness, and remediation as text; color is a secondary border cue.
         Its JSON export includes all ten states without false green semantics.
         The restrained evidence-sheet hierarchy and 390px layout were visually
         inspected under the mandatory frontend workflow.
       - Canonical generated evidence is
         `evidence/v1-workbenches/metis-outcome-states.json`. Verification: 50
         focused schema/helper/API/migration tests including three live
         PostgreSQL gates; 58 OpenAPI/export/release-coverage tests; targeted
         Ruff and strict Python 3.12 Mypy; byte compilation; current OpenAPI and
         generated-client drift checks; targeted zero-warning web lint and full
         web typecheck; 89 files / 361 Metis web unit tests; production
         stub/random scan; eight evidence-generator tests; and 24 Chromium,
         Firefox, and WebKit Playwright executions covering all ten states,
         structured evidence, export, release blocking, authorization, exact
         transport, 390px reflow, and automatic serious/critical Axe analysis.
       - These controls establish the bounded M5.17 outcome-state plane. The
         M5.18 exact provenance registry is now integrated. They do not claim
         production registry deployment, representative steward usability, or
         formal accessibility conformance. Browser API transport is mocked. A
         Next production build was not run under the less-than-32-GiB
         host approval gate; targeted contract, database, lint, type, unit,
         accessibility, responsive, visual, and cross-browser gates pass.
       -->
- [x] M5.18 Version every evaluator, policy, corpus, and threshold. Evidence:
      gate result provenance.
  - [x] M5.18.a Register evaluator code/model/config/runtime/dependency version/
        hash/source/license/lifecycle and supported inputs. Evidence: registry.
  - [x] M5.18.b Register corpus manifest/labels/splits/hashes/provenance/rights/
        coverage/privacy and calibration status. Evidence: corpus registry.
  - [x] M5.18.c Register policy/rubric/threshold/band/aggregation/profile
        versions with approval/effective dates and code floors. Evidence: policy
        registry.
  - [x] M5.18.d Bind every measurement/verdict to exact versions/environment and
        reproduce or explicitly label nonreproducibility. Evidence: provenance
        tests.
      <!-- M5.18 completion evidence (2026-09-02):
       - `metis.schemas.provenance_registry` closes immutable evaluator, corpus,
         policy-bundle, threshold-band, and execution-environment contracts.
         Used artifacts require version, SHA-256, source URI/source revision,
         and license; not-applicable artifacts require an explicit rationale and
         cannot carry false version claims. Evaluators additionally pin runtime,
         dependencies, supported inputs, determinism, replay, effectivity,
         ownership, and lifecycle evidence.
       - Corpus records pin manifest, labels, immutable splits, source lineage,
         rights windows, coverage and known gaps, privacy classification and
         retention, calibration evidence/approval, and lifecycle. Policy bundles
         pin gate, policy, rubric, profile, threshold artifact/rule IDs/bands,
         aggregation, approval/effective dates, code floors, determinism, and
         replay procedure. Environments pin OS/architecture, runtimes, container,
         dependency lock, variables, infrastructure, hardware, locale/timezone,
         network mode, capture, retention, and availability.
       - Alembic 043 creates four append-only PostgreSQL registry tables, five
         tenant-scoped ledger foreign keys, supporting indexes and uniqueness
         constraints, and the complete ten-state outcome constraint. Live
         PostgreSQL gates prove idempotent content addressing, exact measurement
         and verdict pins, successor recomputation without remeasurement,
         registry immutability, tenant isolation, and a 5.18 export containing
         only the referenced registry subset.
       - New writes require schema 1.2 plus exact registry IDs and content hashes.
         Service validation rejects inactive/expired evaluators, corpora,
         environments and policies; unsupported inputs; rights expiry;
         mismatched components, thresholds, rules, approval, code floors, or
         source replay. Deterministic executions may claim bit-exact replay;
         seeded executions require a captured seed; nondeterministic, incomplete,
         and legacy records remain explicitly nonreproducible with reason codes,
         limitations, and replay instructions.
       - Authenticated instructor/admin APIs expose the registry catalog and
         append endpoints for every registry kind. Regenerated OpenAPI and
         TypeScript clients carry the 5.18 contracts. The measurement workbench
         resolves writes against active/effective registry versions, disables
         complete-looking mismatches, renders the exact evaluator/corpus/policy/
         environment spine with approval, coverage, rights, privacy, calibration,
         code-floor and content-hash evidence, and states replay truth in text.
       - Verification: 69 focused schema/helper/API/migration/live-PostgreSQL
         tests; 53 OpenAPI/export/release-coverage tests; Python lint and strict
         typecheck; OpenAPI and generated-client drift checks; full Metis web
         lint/typecheck; 89 files / 361 web unit tests; production stub/random
         scan; eight M5.18 evidence-generator tests plus refreshed eight-test
         M5.16 and M5.17 generators; and 24 Chromium/Firefox/WebKit Playwright
         executions with automatic serious/critical Axe, authorization, exact
         transport, registry mismatch, export, visual, and 390px reflow coverage.
       - These controls establish the bounded M5.18 provenance-registry plane.
         Browser API transport is mocked. They do not claim production registry
         deployment, representative steward usability, or formal accessibility
         certification. A Next production build was not run under the
         less-than-32-GiB host approval gate; targeted contract, migration,
         database, lint, type, unit, accessibility, responsive, visual, and
         cross-browser gates pass.
       -->
- [x] M5.19 Implement atomic-claim decomposition and claim-to-source-span
      linking for lesson, feedback, hint, worked-solution, and lecture content.
      Evidence: labeled corpus measures missed and incorrectly split claims.
  - [x] M5.19.a Define atomic claim identity/revision, subject/predicate/object
        or normalized proposition, qualifiers/scope/modality/time, content kind/
        locator, source-span links, and status. Evidence: claim schema.
  - [x] M5.19.b Segment each named content kind into factual, procedural,
        normative, interpretive, mathematical, attribution, and nonclaim text
        with content-specific rules. Evidence: segmentation fixtures.
  - [x] M5.19.c Preserve compound relationships, coreference/context, equations/
        conditions, negation, examples, quotations, and pedagogical scaffolding
        without losing meaning. Evidence: adversarial corpus.
  - [x] M5.19.d Link each claim to exact source/extraction revision and one or
        more typed spans/locators with support role and human confirmation.
        Evidence: linking tests.
  - [x] M5.19.e Evaluate missed/spurious/over-split/under-split/boundary/link
        precision/recall by content/discipline/locale and uncertainty. Evidence:
        labeled corpus report.
      <!-- M5.19 completion evidence (2026-09-02):
       - `metis.schemas.claim_decomposition` closes atomic claim/revision/status,
         normalized proposition or triple, scope, modality, qualifiers, exact
         content revision/locator, semantic relationships, typed source spans,
         link confirmation, immutable decision history, engine witness, corpus
         metrics, error taxonomy, strata, and workspace contracts. Exact content
         and source spans must match their length and SHA-256; revision history
         must be contiguous.
       - The deterministic 5.19 rule engine covers lesson, feedback, hint,
         worked-solution, and lecture content and the closed factual,
         procedural, normative, interpretive, mathematical, attribution, and
         nonclaim taxonomy. Content-specific clause rules preserve compound
         relations, coreference context, equations, conditions, negation,
         examples, quotations, and pedagogical scaffolding through explicit
         features, relationships, and exact context locators.
       - Runs accept only current human-authorized M2.6 canonical source claim
         revisions. Every candidate link pins the canonical claim/revision,
         extraction/output hash, ingestion item/source hash, node/anchors,
         half-open character span, exact quote/hash, source proposition, typed
         role, similarity, and uncertainty. Append-only administrator decisions
         confirm, correct, reject, or defer a claim and decide every candidate
         link; stale source authority blocks confirmation without rewriting
         prior evidence. Idempotency, tenant visibility, integrity hashes, and
         crash-safe immutable file writes are enforced.
       - The checked-in 12-case adversarial corpus covers all five content kinds,
         all seven segment kinds, nine disciplines, and en-US/es-ES. Runtime
         evaluation reports claim detection P/R/F1 0.85/0.85/0.85, boundary
         detection 0.60/0.60/0.60, and source linking 0.722222/0.8125/0.764706,
         each with 95% Wilson intervals. It retains three missed, three spurious,
         two over-split, two under-split, six boundary, three link false-positive,
         and one link false-negative error receipts, plus content-kind,
         discipline, and locale strata rather than presenting perfect scores.
       - Authenticated instructor APIs create/read exact runs and expose the
         report; administrator APIs expose the cross-package workspace, create
         runs, and append human decisions. Regenerated OpenAPI and TypeScript
         contracts carry the full surface. The admin evidence ledger aligns the
         selected content and source highlights, exposes semantic context and
         immutable history, creates hash-bound runs, locks stale decisions, and
         displays corpus error/uncertainty evidence at desktop and 390px widths.
       - Verification: 17 focused decomposition/source-authority/API tests; 53
         OpenAPI/export/release-coverage tests; checked OpenAPI and generated-
         client drift; Python lint/compile; admin
         lint and full typecheck; 27 files / 40 admin unit tests; eight evidence-
         generator tests; 82 product-graph tests; and six Chromium/Firefox/
         WebKit Playwright executions
         covering exact synchronized spans, corpus results, immutable human
         link review, stale locks, five-kind creation transport, 390px reflow,
         and automatic serious/critical Axe analysis.
       - These controls establish the bounded M5.19 decomposition and candidate-
         linking plane. Candidate links expressly do not claim source support,
         entailment, faithfulness, contradiction resolution, or factual
         correctness; those remain M5.20 gates. Browser API transport is mocked.
         Production deployment, representative reviewer usability, and formal
         accessibility certification are not established. A Next production
         build was not run under the less-than-32-GiB host approval gate;
         targeted contract, lint, type, unit, accessibility, responsive, and
         cross-browser gates pass.
       -->
- [x] M5.20 Implement retrieval-grounded support, citation sufficiency,
      faithfulness, contradiction, and cross-source conflict gates separately.
      Evidence: entailment, partial support, irrelevant citation, conflict,
      inaccessible source, and abstention fixtures.
  - [x] M5.20.a Resolve exact claim, cited/retrieved source spans, authority/
        rights/currency/accessibility, retrieval version/rank, and evaluation
        context. Evidence: gate input tests.
  - [x] M5.20.b Measure support/entailment per source span with full/partial/
        unsupported/contradicted/unclear/N/A and qualifiers. Evidence: support
        fixtures.
  - [x] M5.20.c Evaluate citation sufficiency for claim coverage, source
        authority/diversity/recency/directness and detect irrelevant/overbroad/
        inaccessible citations. Evidence: sufficiency fixtures.
  - [x] M5.20.d Compare content wording to supported claim for added certainty,
        omitted qualifiers, scope/number/time/causal/attribution changes.
        Evidence: faithfulness fixtures.
  - [x] M5.20.e Detect within-source and cross-source contradiction/conflict/
        version/jurisdiction/perspective differences without forcing consensus.
        Evidence: conflict fixtures.
  - [x] M5.20.f Keep each raw result/verifier/version/uncertainty separate and
        abstain/manual-review on inaccessible/insufficient/ambiguous evidence.
        Evidence: state tests.
  - [x] M5.20.g Apply risk-specific policy verdicts without averaging one failed
        dimension into pass. Evidence: aggregate gate tests.
      <!-- M5.20 completion evidence (2026-09-02):
       - `metis.schemas.grounding_gate` closes the exact confirmed M5.19 claim,
         content revision, retrieved/cited source links and spans, query/index/
         retriever versions and hashes, rank/score, risk and use context, source
         authority IDs, rights, currency, accessibility, directness, scope,
         jurisdiction, raw-result, verifier, uncertainty, policy, review,
         corpus, and workspace contracts. Response evidence arrays are required
         even when empty, and every run retains an integrity hash.
       - The append-only service accepts only the current human-confirmed M5.19
         claim revision and every confirmed link, re-resolves approved M2.5
         governance, rejects false authority/rights/currency/jurisdiction
         snapshots, prepares governance-bound eligible contexts, locks stale
         evidence, enforces tenant visibility and idempotency, and recovers
         interrupted immutable run/decision writes without rewriting raw policy.
       - Five separately witnessed deterministic verifiers retain per-span
         support, nine citation criteria, wording faithfulness, pairwise source
         conflict, and risk policy. Semantic support auto-passes only an exact
         normalized proposition match; deterministic polarity/number conflicts
         can block, while non-exact or inaccessible evidence abstains or requires
         manual review. Full/partial/unsupported/contradicted/unclear/N/A,
         qualifiers, irrelevant/overbroad/inaccessible links, all seven wording
         changes, and within/cross-source/version/jurisdiction/perspective
         differences remain explicit. A single source never implies consensus.
       - General/elevated/high/critical profiles set independent authority,
         diversity, recency, and directness floors. The policy outcome follows
         the worst required raw dimension without an aggregate score; only an
         all-dimension pass permits release, and an administrator disposition
         cannot override a block, abstention, or manual-review result.
       - The checked-in 24-case adversarial corpus covers ten required fixture
         families, all six support states, all seven faithfulness changes, all
         five conflict kinds, and all four risks. Runtime evaluation reports
         support 23/23, citation 24/24, faithfulness 24/24, conflict 6/6, and
         policy 24/24 fixture matches with 95% Wilson intervals and expressly
         labels this bounded conformance rather than open-domain NLI accuracy or
         proof that an authoritative source is true.
       - Authenticated instructor APIs create/read exact runs and expose corpus
         results; administrator APIs expose the cross-package docket, create
         runs, and append dispositions. Regenerated OpenAPI and TypeScript
         contracts publish the full surface. The admin evidence court aligns
         claim/source highlights, switches among independent dimensions,
         exposes verifier/version/uncertainty receipts and non-consensus
         conflict states, creates governance-bound retrieval receipts, locks
         stale review, and reflows at 390px.
       - Verification: 13 focused service/contract tests; checked OpenAPI and
         generated-client drift; Python lint, strict typecheck, and compile;
         admin lint/typecheck and 28 files / 41 unit tests; eleven evidence-
         generator tests; product-graph gates; and six Chromium/Firefox/WebKit
         Playwright executions covering synchronized evidence, all dimensions,
         immutable non-overriding disposition, staleness, critical-risk
         transport, 390px overflow, and automatic serious/critical Axe.
       - These controls establish a conservative bounded M5.20 grounding-gate
         plane, not a production semantic-entailment model. Browser API transport
         is mocked. Open-domain NLI accuracy, source truth, production
         deployment, representative reviewer usability, and formal accessibility
         certification are not established. A Next production build was not run
         under the less-than-32-GiB host approval gate; targeted contract, lint,
         type, unit, accessibility, responsive, and cross-browser gates pass.
       -->
- [x] M5.21 Implement executable mathematics verification using ratified
      symbolic/numeric tooling and assumption/domain tracking. Evidence: proofs,
      equations, units, edge cases, and unsupported constructs do not reduce to
      an LLM opinion.
  - [x] M5.21.a Define supported math languages/constructs, parser/CAS/numeric/
        unit/proof tool versions, assumptions/domains, tolerances, resource
        limits, and explicit unsupported behavior. Evidence: verifier contract.
  - [x] M5.21.b Parse semantic math, bind
        variables/units/definitions/assumptions/ domains, reject ambiguous
        notation, and retain source spans. Evidence: parsing fixtures.
  - [x] M5.21.c Verify symbolic
        equivalence/identity/equation/inequality/solution sets under stated
        domains and detect extraneous/missing cases. Evidence: symbolic goldens.
  - [x] M5.21.d Verify numeric computations/approximations/rounding/error
        bounds/ edge and randomized property cases with deterministic seeds.
        Evidence: numeric goldens.
  - [x] M5.21.e Verify dimensional/unit consistency/conversion/significant
        figures and physical-domain constraints. Evidence: unit fixtures.
  - [x] M5.21.f Verify supported proof steps/rules separately; return localized
        error/unknown/timeout/unsupported rather than provider opinion.
        Evidence: proof fixtures.
  - [x] M5.21.g Sandbox tooling and persist inputs/outputs/tool versions/traces
        separately from policy/human verdict. Evidence: security/provenance
        tests.
      <!-- M5.21 completion evidence (2026-09-02):
       - `metis.schemas.mathematics_verification` closes the 5.21.0 target,
         exact source-span/hash, explicitly bound variable/domain/definition,
         tolerance, resource-limit, symbolic/numeric/unit/proof obligation,
         parse artifact, localized diagnostic, raw trace/counterexample/
         missing/extraneous case, pinned tool/engine, sandbox receipt, worst-
         state policy, append-only run/review, corpus, and workspace contracts.
         The expression language is explicitly the bounded
         `metis-ascii-math-v1` subset; unsupported behavior fails closed.
       - The restricted Python-AST parser constructs only reviewed arithmetic,
         bounded-power, named-function, relation, and solution-set nodes. It
         never calls `eval`, `sympify`, or `parse_expr`, never admits implicit
         multiplication or arbitrary attributes/calls, binds every name before
         execution, preserves exact source spans, and returns unknown or
         unsupported for ambiguity, unbound names, conditional sets, and
         unsupported multivariate/proof shapes.
       - The exact worker uses SymPy 1.14.0 and mpmath 1.3.0 for equivalence,
         defined-domain comparison, equations, inequalities, and missing or
         extraneous solution cases; Decimal arithmetic for explicit, edge,
         rounding, maximum-error, and deterministic seeded property cases; and
         Pint 0.25.2 for independently traced conversion, dimensionality,
         significant-figure, and physical-domain checks. Supported proof steps
         compare each dependency's exact solution set and retain per-step
         pass/fail/unknown/timeout/unsupported state and localized diagnostics.
       - Bubblewrap 0.9.0 runs the worker with all namespaces unshared, no
         network, a read-only root, bounded tmpfs, uid/gid 65534, all
         capabilities dropped, a cleared environment, and wall/CPU/address-
         space/process/open-file/output/core limits. Tool versions and artifact
         hashes are checked before launch; missing or unratified tooling has no
         unsandboxed fallback. Network/root-write/uid probes, bounded stdout,
         stderr, exit state, timeout, hashes, and forced process-group teardown
         remain in the receipt.
       - Policy follows the worst executable task state without averaging. A
         failed task or high/critical unknown state blocks; lower-risk unknown,
         timeout, or unsupported results require review. Human dispositions are
         immutable audit entries and cannot change raw mathematics or policy.
         Tenant-scoped append-only files enforce exact spans, expression IDs,
         idempotency, integrity hashes, interrupted-write recovery, and stale-
         revision review locks.
       - The checked-in 22-case adversarial corpus covers 22 fixture families:
         ambiguity, unbound and unsupported constructs, symbolic equivalence,
         domain loss, equation/inequality sets, extraneous cases, numeric exact/
         edge/rounding/error/seeded cases, unit conversion/dimensionality/
         significant figures/physical bounds, valid and invalid proof steps,
         hidden assumptions, and unsupported proof dependency shapes. Actual
         sandbox evaluation reports parsing 3/3, symbolic 6/6, numeric 5/5,
         units 4/4, proofs 4/4, and isolation 1/1 with 95% Wilson intervals.
       - Authenticated instructor APIs expose evaluation and exact run creation/
         retrieval. Administrator APIs expose the docket, create runs, and
         append dispositions. Regenerated OpenAPI and TypeScript contracts
         publish the closed surface. The admin proof bench synchronizes exact
         source highlights with parse and proof steps, preserves separate
         symbolic/numeric/unit/proof traces and counterexamples, exposes pinned
         tools and isolation receipts, locks stale reviews, and reflows without
         horizontal overflow at 390px.
       - Verification: 14 focused engine/service/OpenAPI tests; checked OpenAPI
         and generated-client drift; Python lint, strict typecheck, and compile;
         admin lint/typecheck and 29 files / 42 unit tests; twelve deterministic
         evidence-generator tests; product-graph gates; and six Chromium/
         Firefox/WebKit Playwright executions covering synchronized spans,
         separate raw dimensions, localized proof steps, tool/sandbox receipts,
         immutable non-overriding review, staleness, exact create transport,
         390px overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-mathematics-verification.json` through
         `inventory:v1-metis-mathematics-verification` and its verification
         script. These controls establish deterministic conformance for the
         ratified subset, not correctness for all mathematics. Browser API
         transport is mocked. Production deployment, representative reviewer
         usability, physical assistive-technology testing, and formal
         accessibility certification are not established. A Next production
         build was not run under the less-than-32-GiB host approval gate;
         targeted contract, lint, type, unit, accessibility, responsive, and
         cross-browser gates pass.
       -->
- [x] M5.22 Implement sandboxed code correctness using compilation/execution,
      tests, resource limits, language/runtime versions, and deterministic or
      explicitly stochastic expectations. Evidence: correct, wrong, unsafe,
      flaky, timeout, and unsupported fixtures.
  - [x] M5.22.a Define supported language/runtime/compiler/dependency/test
        harness versions, input/output/files/API, deterministic seed/repetition,
        scoring, and unsupported policy. Evidence: verifier contract.
  - [x] M5.22.b Build immutable source/dependency/test artifacts and compile/
        static-check with exact diagnostics in an isolated environment.
        Evidence: compile fixtures.
  - [x] M5.22.c Execute visible/hidden/example/property/metamorphic/performance
        tests with independent cases and exact expected/tolerant/stochastic
        assertions. Evidence: correctness suite.
  - [x] M5.22.d Enforce CPU/memory/wall/process/file/output/network/syscall/
        secret limits and terminate entire sandbox on breach/timeout. Evidence:
        unsafe/timeout tests.
  - [x] M5.22.e Detect nondeterminism/flakiness through repeated seeded runs and
        report distribution/unstable state rather than lucky pass. Evidence:
        flaky fixtures.
  - [x] M5.22.f Persist per-test outputs/resources/tool versions and separate
        raw execution from score/verdict/manual review. Evidence: result tests.
       <!-- M5.22 technically complete; operational readiness remains open.
       - The closed `5.22.0` contract binds dependency-free Python 3.12.14,
         CPython interpreter/compiler artifacts, the reviewed harness, exact
         Python image digest, Docker 29.5.3, the default-deny seccomp profile,
         immutable source/support artifacts, entrypoint/API/stdin/arguments/
         output mode, dependency policy, deterministic or explicitly
         stochastic repetitions, weighted scoring, resource ceilings, and
         fail-closed unsupported behavior. There is no host-execution or
         runtime-install fallback.
       - Every source artifact is content-hash checked, traversal-safe,
         materialized read-only, compiled without submission execution inside
         the same pinned least-privilege image, and returned with exact syntax
         locations, imported-module evidence, conservative boundary warnings,
         artifact hashes, compiler/tool/image/seccomp witnesses, bounded output,
         and confirmed container teardown.
       - Fresh containers execute visible and hidden examples, explicit
         property cases with generator witnesses, metamorphic pairs,
         performance ceilings, and adversarial security probes independently.
         Exact, normalized-text, JSON, numeric-tolerance, deterministic, and
         declared stochastic assertions remain explicit. Hidden expected output
         is never mounted into submission execution and is redacted from raw
         attempt projections.
       - CPU share, memory/swap, wall time, process count, open files, bounded
         writable tmpfs, aggregate output, network-none, read-only root,
         unprivileged UID, dropped capabilities, no-new-privileges, protected
         secret non-forwarding, and default-deny syscalls are enforced. Timeout
         or resource breach forcibly removes the container and terminates the
         client process group, then proves the container is absent. Live tests
         cover wall timeout, memory pressure, output bombs, network, filesystem,
         process, syscall, secret, and teardown boundaries.
       - Repetitions derive stable per-case seeds and persist every attempt plus
         output distributions. Divergent deterministic output becomes
         `unstable`; declared stochastic output must remain inside its bounded
         allowed set and meet minimum diversity, so a lucky single pass cannot
         hide flakiness.
       - Tenant-scoped append-only storage binds compiler/test/score receipts
         back to the exact request, rejects divergent idempotency reuse and
         tampering, recovers interrupted idempotency receipt writes, locks stale
         revision review, and appends non-overriding dispositions. Instructor
         and admin APIs expose immutable runs, workspace/evaluation evidence,
         and review. Raw compilation, per-test attempts/resources/distributions,
         the 10,000-basis-point score, five independent policy dimensions, and
         manual review remain structurally separate.
       - The admin forensic bench synchronizes a line-addressable immutable
         source receipt, exact compiler diagnostics, separate visible/hidden/
         property/metamorphic/performance/stochastic traces, expandable stdout/
         stderr/expected/resource receipts, output distributions, tool/image/
         seccomp witnesses, score and policy, staleness, and append-only review.
         It reflows without horizontal viewport overflow at 390px and honors
         reduced motion.
       - The executable corpus contains 17 correct, wrong, hard-coded-hidden,
         property, metamorphic, performance, timeout, output-limit, unsafe,
         secret, flaky, stochastic, compile, and unsupported-dependency cases.
         All 17 observed outcomes match labels across nine separately reported
         dimensions with Wilson 95% intervals. This is bounded conformance for
         the ratified runtime, not correctness for arbitrary languages,
         dependencies, APIs, or programs; static import warnings are not a
         complete vulnerability or malware analysis.
       - Verification: 22 focused live Docker engine, append-only service, and
         closed OpenAPI tests; OpenAPI and generated-client drift checks; Python
         lint, strict typecheck, and compile; admin lint/typecheck and 30 files /
         43 unit tests; eleven deterministic evidence-generator tests; product-
         graph gates; and six Chromium/Firefox/WebKit Playwright executions
         covering source/diagnostic synchronization, independent raw tests,
         hidden redaction, distributions, resources, tool/isolation receipts,
         stale locking, immutable non-overriding review, exact create transport,
         390px overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-code-verification.json` through
         `inventory:v1-metis-code-verification` and its verification script.
         Browser API transport is mocked. Production deployment,
         representative reviewer usability, physical assistive-technology
         testing, and formal accessibility certification are not established.
         A Next production build was not run under the less-than-32-GiB host
         approval gate; targeted contract, lint, type, unit, accessibility,
         responsive, and cross-browser gates pass.
       -->
- [x] M5.23 Implement step/process verification for worked solutions, not just
      final-answer matching. Evidence: localized error, invalid shortcut,
      circular reasoning, and correct-alternative-path fixtures.
  - [x] M5.23.a Define solution step identity/order/dependencies, expression/
        claim, transformation/rule/justification, assumptions/domain, evidence,
        and intermediate result. Evidence: process schema.
  - [x] M5.23.b Verify each step follows from permitted prior steps/premises via
        ratified symbolic/numeric/rule/source tools and localize first/all
        errors. Evidence: localized error fixtures.
  - [x] M5.23.c Detect hidden assumption/domain loss, division by zero,
        extraneous solution, unit error, invalid shortcut, circular reasoning,
        and unproved lemma. Evidence: adversarial fixtures.
  - [x] M5.23.d Accept correct alternative paths/orderings/equivalent forms when
        independently verified, without exact-template matching. Evidence:
        alternative-path fixtures.
  - [x] M5.23.e Distinguish correct/incorrect/partially supported/unknown/
        unsupported/timeout per step and aggregate without final-answer
        override. Evidence: aggregation tests.
  - [x] M5.23.f Present verifier trace and human-review corrections; keep final
        answer verification as a separate dimension. Evidence: review tests.
       <!-- M5.23 technically complete; operational readiness remains open.
       - The closed `5.23.0` contract states step identity, contiguous ordinals,
         declared dependencies, rule ID, justification, assumption and evidence
         bindings, an optional rule argument, and one intermediate result per
         step. Intermediate results are a discriminated relation, structured
         claim, or dimensioned quantity; every expression must equal the exact
         character span of the worked solution it cites, every excerpt and
         revision is SHA-256 bound, and variables carry an explicit solve
         domain. The contract is published closed on the instructor and admin
         OpenAPI surfaces and in the generated TypeScript client.
       - Fourteen ratified rules run inside a pinned bubblewrap 0.9.0 sandbox
         (all six namespaces unshared, read-only root, bounded tmpfs, uid 65534,
         all capabilities dropped, CPU/address-space/process/open-file/output
         rlimits) against pinned SymPy 1.14.0, Pint 0.25.2, and mpmath 1.3.0.
         In-sandbox network and root-write probes plus the observed uid are
         checked before any result is accepted; a missing, unratified, or
         unprobeable runtime fails closed with no host-execution fallback.
       - A step is checked against its declared earlier steps and premises: a
         forward or missing dependency, an undeclared assumption, an unstated
         premise, an unratified rule, or an unverified prerequisite each has its
         own code and its own state. Transformations are judged by exact
         solution-set comparison over the declared domain, quantities by Pint
         conversion and dimensionality, claims by exact revision-bound evidence
         or one explicit Horn implication. Every step carries its localized
         source span, and the aggregate reports both the first and all failing
         steps.
       - Hidden assumptions, domain loss, possible division by zero, extraneous
         solutions, unit errors, invalid shortcuts, circular reasoning, and
         unproved lemmas are eight separate verdicts, not one rejection. Nonzero
         factors must be proven against the declared domain rather than
         asserted, and a cycle in the dependency graph is localized to the steps
         that participate in it.
       - Correctness is solution-set equality, never template equality, so an
         alternative rule (divide-by-a-proven-nonzero instead of
         add-same-both-sides), an equivalent form (`x - 3 = 0` closing a
         conclusion stated as `x = 3`), and a different display order for two
         independent premises are all accepted on their own evidence.
       - The six step states stay structurally distinct: a SymPy conditional set
         is `unknown`, an unratified rule is `unsupported`, a representable
         claim with unavailable evidence is `partially_supported`, and a sandbox
         breach is `timeout`. Aggregation is the worst raw step state, and the
         service refuses to persist any aggregate that is not a faithful
         projection of the raw results. The final answer is verified separately
         and cannot convert a failed, unknown, or unsupported process into a
         correct one; process, final answer, domain, and evidence are four
         independently reported policy dimensions.
       - Tenant-scoped append-only storage binds every run to its request hash,
         rejects divergent idempotency reuse and tampering, recovers interrupted
         receipt writes, locks disposition on stale revisions, and rejects a
         correction naming a step outside the run. Reviews append a disposition
         and per-step corrections with `raw_result_overridden` structurally
         false.
       - The admin forensic bench synchronizes the immutable worked solution
         with the localized span highlighted in place, per-step rule,
         dependencies, assumptions, evidence, missing bindings, lost and
         extraneous solutions, the raw tool trace, the separate final-answer
         dimension, the aggregate, the four policy dimensions, the ruleset and
         isolation witness, and append-only review. It reflows without
         horizontal viewport overflow at 390px and honors reduced motion.
       - The executable corpus contains 19 labeled cases across 19 fixture
         families and seven dimensions. All 19 observed states and codes match
         their labels, reported per dimension with Wilson 95% intervals. This is
         bounded conformance for the ratified ruleset, not a proof system for
         arbitrary natural-language reasoning; multivariate relations and
         unratified rules fail closed as unsupported.
       - Verification: 35 focused sandbox, append-only service, and closed
         OpenAPI tests; OpenAPI and generated-client drift checks; Python lint,
         strict typecheck; admin lint/typecheck and 31 files / 44 unit tests;
         eleven deterministic evidence-generator tests; 19 shared admin layout
         and WCAG signoff executions; and nine Chromium/Firefox/WebKit Playwright
         executions covering first-error localization, span highlighting,
         distinct dependent-step states, non-overriding final answers, appended
         step corrections, stale locking, exact create transport, 390px
         overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-solution-process-verification.json` through
         `inventory:v1-metis-solution-process-verification` and its verification
         script. Browser API transport is mocked. Production deployment,
         representative reviewer usability, physical assistive-technology
         testing, and formal accessibility certification are not established.
         A Next production build was not run under the less-than-32-GiB host
         approval gate; targeted contract, lint, type, unit, accessibility,
         responsive, and cross-browser gates pass.
       -->
- [x] M5.24 Implement calibrated multi-evaluator panels only where deterministic
      verification is unavailable, with independence analysis, correlated-error
      controls, confidence calibration, abstention, and human-review bands.
      Evidence: gold-set and champion/challenger reports.
  - [x] M5.24.a Define applicability gate proving deterministic/rule/source/
        executable verification is unavailable or incomplete before panel use.
        Evidence: routing tests.
  - [x] M5.24.b Register evaluator
        identity/model/prompt/config/provider/training/ data/tool dependencies,
        rubric scope, calibration, and lifecycle. Evidence: panel registry.
  - [x] M5.24.c Record shared dependencies/prompts/data/providers as correlation
        groups and minimum independence/quorum/weight rules. Evidence:
        independence analysis.
  - [x] M5.24.d Collect blinded per-evaluator label/score/reason/evidence/
        confidence/abstention/error against exact subject/rubric revisions.
        Evidence: collection tests.
  - [x] M5.24.e Calibrate confidence/thresholds/manual bands on versioned gold
        sets and report overall/subgroup/content errors with uncertainty.
        Evidence: calibration report.
  - [x] M5.24.f Aggregate with correlated-error controls, preserve disagreement/
        minority/timeout/abstention, and never manufacture gate authority.
        Evidence: consensus fixtures.
  - [x] M5.24.g Run champion/challenger drift comparison before replacement and
        require human review for configured bands. Evidence: lifecycle report.
       <!-- M5.24 technically complete; operational readiness remains open.
       - The closed `5.24.0` contract is deterministic over recorded evaluator
         observations. The engine never calls an evaluator, so every result here
         is a property of the panel rules, not of any model.
       - A panel is admissible only for rubric criteria that no available
         deterministic verifier fully covers. Every criterion must carry a
         probe: an unexamined criterion is `probe_evidence_missing`, full
         coverage is `deterministic_verification_available`, and both refuse the
         run with a `not_applicable` verdict. Observations for a
         deterministically covered criterion are rejected, and the service
         refuses to persist a consensus that does not cover exactly the residual
         criteria in order.
       - Each evaluator registers its lifecycle, provider, model and version,
         prompt revision and hash, config hash, decoding seed, rubric scope, and
         its model/prompt/provider dependencies (training data, tools, and
         retrieval indexes are declarable). A criterion outside an evaluator's
         declared scope is refused rather than silently judged.
       - Correlation groups are derived from the declared dependencies and then
         audited against the declaration: a shared dependency with no declared
         group is `undeclared-correlation`, and a declared group no shared
         dependency justifies is `unjustified-declaration`. Evaluators are
         union-found into independent components, each component votes once, and
         its weight is capped by the declared ceiling.
       - Every observation must present the blinded subject alias derived from
         the exact content hash and the exact rubric revision hash, and its
         label must be on the criterion's declared scale. Scored, abstained,
         timed-out, and errored observations are structurally distinct shapes.
         Refused observations are kept as findings rather than dropped.
       - Calibration measures accuracy, a Wilson 95% interval, and a ten-bin
         expected calibration error on the versioned gold-set revision, reported
         overall and per subgroup and content kind. An evaluator below the
         sample, accuracy, or calibration-error floor is uncalibrated and
         carries no weight; the service refuses to persist a consensus in which
         an uncalibrated evaluator voted.
       - **A tie is not a majority.** A criterion without a strict plurality
         reports no majority at all and keeps every tied label. Disagreement,
         minority weights, abstentions, timeouts, errors, and exclusions all
         survive aggregation. The verdict is structurally advisory:
         `authority` is `advisory_only`, `replaces_deterministic_verification`
         and `gate_authority_manufactured` are `False`, and the strongest thing
         a panel can demand is human review.
       - Champion/challenger comparison runs on the shared gold-set items and
         reports overall drift, agreement, and the worst per-subgroup drift. A
         challenger that improves overall but regresses on a subgroup is
         rejected; an improvement only ever becomes
         `promotion_requires_human_review`. `automatic_promotion` is `False` and
         `automatic_replacements` is `0` by construction.
       - Tenant-scoped append-only storage binds each run to its request hash,
         rejects divergent idempotency reuse and tampering, recovers interrupted
         receipt writes, and locks disposition on stale subject revisions. A
         deterministic override must name the deterministic evidence it relies
         on, and `raw_panel_result_overridden` is structurally false.
       - The admin bench synchronizes the applicability probes, derived
         correlation groups and findings, per-evaluator calibration with
         subgroup and content-kind intervals, per-criterion consensus with the
         preserved minority, rejected-observation findings, the lifecycle
         comparison, the advisory verdict, and append-only review. It reflows
         without horizontal viewport overflow at 390px and honors reduced
         motion.
       - The executable corpus contains 25 labeled cases across 24 fixture
         families and seven dimensions. All 25 observed outcomes and codes match
         their labels, reported per dimension with Wilson 95% intervals.
       - Verification: 38 focused engine, append-only service, and closed
         OpenAPI tests; ruff and strict mypy; OpenAPI and generated-client drift
         checks; admin lint/typecheck and 32 files / 45 unit tests; thirteen
         deterministic evidence-generator tests; 22 shared admin layout and WCAG
         executions; and nine Chromium/Firefox/WebKit Playwright executions
         covering applicability refusal, correlated voices, per-subgroup
         calibration, a tie that names no majority, an unanswered residual
         criterion, advisory-only authority, append-only review with
         deterministic evidence, stale locking, exact create transport, 390px
         overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-evaluator-panel.json` through `inventory:v1-metis-evaluator-panel`
         and its verification script. This establishes the panel rules and
         nothing about any live model's judgement quality. Independence is
         derived from declared dependencies only — evaluators can still correlate
         through undeclared training data. Browser API transport is mocked;
         production deployment, representative reviewer usability, and formal
         accessibility certification are not established.
       -->
- [x] M5.25 Implement verifier-guided best-of-N generation without hiding
      rejected candidates or selection bias. Evidence: candidate, score,
      verifier version, selection rationale, and educator decision remain
      inspectable.
  - [x] M5.25.a Define request/candidate-set/count/diversity/seed/budget,
        generator versions, verifier plan/rubric/versions, and stop conditions.
        Evidence: workflow schema.
  - [x] M5.25.b Generate bounded unique candidate identities with complete
        prompt/source/model/config/cost lineage and preserve malformed/refused/
        failed candidates. Evidence: generation tests.
  - [x] M5.25.c Run applicable deterministic and calibrated verifiers on every
        eligible candidate with raw results, unavailable/error/abstention, and
        no selective rerun. Evidence: evaluation tests.
  - [x] M5.25.d Rank/filter only by the declared versioned rule, expose scores/
        uncertainty/rejections/tie/selection bias, and prohibit hidden candidate
        removal. Evidence: selection fixtures.
  - [x] M5.25.e Present all authorized candidates/evidence/cost and require an
        educator select/reject/edit/regenerate decision. Evidence: review tests.
  - [x] M5.25.f Create the accepted artifact as a new revision with candidate/
        verifier/human decision lineage and normal gates. Evidence: end-to-end
        trace.
       <!-- M5.25 technically complete; operational readiness remains open.
       - The closed `5.25.0` contract is deterministic over recorded candidates.
         The engine never generates a candidate and never runs a verifier, so
         every result here is a property of the retention, completeness,
         ranking, bias-reporting, and decision rules rather than of any
         generator or verifier.
       - The workflow declares candidate count, minimum unique outputs, the
         diversity rule, a deterministic seed, a budget, a deadline, an output
         ceiling, the versioned verifier plan with per-verifier weights totalling
         10,000 basis points, and its stop conditions. A plan whose weights do
         not total, that requires nothing, or a run that overspends its budget
         is refused at intake.
       - Every candidate carries its generator, provider, model and version,
         prompt revision and hash, config hash, source revisions, token counts,
         cost, and latency. `malformed`, `refused`, and `generation_failed`
         candidates must record why they were unusable and are retained: they
         are scored (as rejected), counted, and displayed. Duplicate outputs are
         named, not dropped. The service refuses to persist a run whose scored
         record does not contain every generated candidate.
       - Every eligible candidate must carry exactly one result for every planned
         verifier. A missing result and a second attempt for the same
         candidate/verifier pair are both findings, and either one moves the
         selection to `plan_incomplete` rather than quietly selecting from a
         partially evaluated set. `unavailable`, `error`, and `abstained`
         outcomes structurally carry no score and contribute nothing.
       - Ranking is the declared plan weights over the recorded scores, with the
         weighted uncertainty interval carried alongside. A failed required
         verifier or a total below the declared floor rejects the candidate with
         a named code, and the candidate stays in the record and out of the
         shortlist.
       - **Selection bias is measured against the score, not the rank.** Equal
         scores are ordered by generation position only to make the ranking
         total, so a rank-versus-position correlation would report that tiebreak
         back as a bias. Kendall tau-b relates the score to generation position
         and to output length, and the shortlist's generator concentration is
         reported beside them. A finding needs both the declared tau threshold
         and at least four eligible candidates, because a correlation over two
         points is arithmetic rather than evidence; the statistic is published
         either way.
       - The engine shortlists and never accepts. A tie is handed to an
         educator, `automatic_acceptance` is structurally false, and
         `hidden_candidate_removals` is structurally zero. The bench shows every
         candidate — its output or its failure record, its per-verifier
         contributions, its lineage and its cost — before the decision control.
       - A revision exists only after an educator acceptance. It binds the
         source candidate, the verifier results that examined it, the ranking
         rule, and the decision into one lineage hash, carries those verifier
         results forward as its gates, and records an educator who accepted a
         rule-rejected candidate as an explicit override with the codes it
         overrode. `created_without_educator_decision` is structurally false.
         Rejection and regeneration create nothing.
       - Tenant-scoped append-only storage binds each run to its request hash,
         rejects divergent idempotency reuse and tampering, refuses a decision
         naming an ungenerated candidate or quoting a stale run hash, and allows
         exactly one decision per run.
       - The executable corpus contains 31 labeled cases across six dimensions,
         all matched, reported with Wilson 95% intervals.
       - Verification: 44 focused engine, append-only service, and closed
         OpenAPI tests; ruff and strict mypy; OpenAPI and generated-client drift
         checks; admin lint/typecheck and 33 files / 46 unit tests; twelve
         deterministic evidence-generator tests; 25 shared admin layout and WCAG
         executions; and twelve Chromium/Firefox/WebKit Playwright executions
         covering an unresolved tie, a retained unusable candidate, declared
         weight arithmetic, revision-only-on-acceptance, append-only decisions,
         exact create transport, 390px overflow, and automatic serious/critical
         Axe analysis.
       - Fixed while building this cell: the three verification benches faded in
         with an opacity keyframe, so an accessibility audit taken mid-animation
         graded every colour under the bench against a blended background. The
         entrance is now transform-only. `code-verifications` still animates
         opacity and carries the same latent flake.
       - Evidence is reproducible at `evidence/v1-workbenches/metis-best-of-n.json`
         through `inventory:v1-metis-best-of-n` and its verification script. The
         bias report covers two nuisance variables at a declared threshold and
         minimum sample; a value below either is not evidence of an unbiased
         selection. Browser API transport is mocked; production deployment,
         representative educator usability, and formal accessibility
         certification are not established.
       -->

### M6 — Adaptive sequencing, tutor linkage, and learning analytics

- [x] M6.1 Build pathway/program authoring with entry criteria, objective DAG,
      cadence, pacing, and completion rules. Evidence: cycle and reachability
      tests.
  - [x] M6.1.a Define versioned program/pathway, cohort/audience, course/module/
        objective membership, owner, locale, lifecycle, and credential links.
        Evidence: pathway schema.
  - [x] M6.1.b Author typed entry prerequisites, prior evidence/credentials/
        assessment/mastery conditions, alternatives, expiry, and review.
        Evidence: entry-criteria tests.
  - [x] M6.1.c Author objective/course dependency DAG with required/optional/
        choice/remedial/parallel edges and cycle/orphan/reachability checks.
        Evidence: graph tests.
  - [x] M6.1.d Author calendar/cadence/start windows/deadlines/pacing/minimum/
        maximum/seat-time/break/load rules with timezone and accommodation.
        Evidence: schedule tests.
  - [x] M6.1.e Author deterministic completion rules over exact objectives/
        evidence/assessments/grades/mastery/approvals without credential
        issuance authority. Evidence: completion tests.
       <!-- M6.1 technically complete; operational readiness remains open.
       - The closed `6.1.0` contract states a versioned program/pathway revision
         with its owner, lifecycle, cohort or role audience, locale, and its
         course/module/objective members, each bound to an exact member revision
         hash and its declared seat time. Duplicate node IDs, a member included
         twice, a pathway that requires nothing, and an audience whose scope does
         not match its kind are each refused at intake.
       - Entry criteria are six typed condition kinds — prior credential, prior
         pathway, assessment score, objective mastery, evidence artifact, and
         manual approval — grouped into all-of and any-of requirements, where
         `any_of` is how a pathway declares alternatives. Evidence past its
         `expires_after_days` is **expired**, not unsatisfied: the learner met
         the threshold and only the clock stopped them, and the decision still
         reports the observed and required values. A pending approval is
         `review_required`, a missing subject is `evidence_missing`, and a
         blocked alternative still names the routes that remain open.
       - The dependency graph carries required, optional, choice, remedial, and
         parallel edges, and **only four of the five constrain order**: an
         optional edge is a suggestion, so the same edge that closes a cycle as
         `required` leaves the order intact as `optional`. Kahn's algorithm
         produces the topological order; Tarjan's strongly connected components
         localize every cycle, and a cyclic graph structurally has no order at
         all. Self-edges, edges naming a node outside the pathway, orphans,
         unreachable nodes, a choice group of one, and a remedial edge feeding a
         required node are each named with the nodes involved.
       - Cadence resolves the declared IANA zone through `zoneinfo` and reports
         when it cannot, so a planned day is a real local day rather than an
         assumed one. The plan lays the topological order out over calendar days
         from seat time, applies extended-time, reduced-load, deferred-start and
         alternate-deadline accommodations, and then checks the enrollment
         window, the deadline, the weekly load ceiling net of required break
         days, and the pacing bounds. An alternate-deadline accommodation
         rescues an overrunning plan without changing the plan itself.
       - Completion is exact criteria over the recorded learner evidence under an
         all-of or at-least-n rule. **Entry failure precedes completion**: a
         learner who met every completion criterion but never qualified to
         enrol is `entry_not_satisfied`, not complete.
       - **The engine has no credential issuance authority.** A pathway's
         credential links carry `issued_by_this_pathway = False`, the completion
         rule carries `issues_credentials = False`, and the decision carries
         `credentials_issued = 0`, `credential_issuance_authority = "none"`, and
         `issued_here = False` on every eligibility row. An incomplete pathway
         makes nobody eligible, and the service refuses to persist any analysis
         that claims otherwise. Issuance stays with the credential service.
       - Tenant-scoped append-only storage binds each authoring record to its
         request hash, rejects divergent idempotency reuse and tampering, and
         marks a superseded revision stale. Publishability names every blocking
         graph finding, schedule finding, unreachable required node, and
         non-publishable lifecycle.
       - The executable corpus contains 43 labeled cases across five dimensions,
         all matched, reported with Wilson 95% intervals.
       - Verification: 58 focused engine, service, and closed OpenAPI tests; ruff
         and strict mypy; OpenAPI and generated-client drift checks; admin
         lint/typecheck and 34 files / 47 unit tests; eleven deterministic
         evidence-generator tests; 19 shared admin layout and WCAG executions;
         and nine Chromium/Firefox/WebKit Playwright executions covering a
         blocked cyclic graph with no order, a publishable revision's plan and
         entry evidence, credential eligibility that issues nothing, exact
         create transport, 390px overflow, and automatic serious/critical Axe
         analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-learning-pathway.json` through `inventory:v1-metis-learning-pathway`
         and its verification script. Entry and completion read a learner
         evidence snapshot supplied with the request; nothing establishes that
         the snapshot faithfully reads a production gradebook. Pacing is planning
         arithmetic at a fixed sixty-minutes-per-day rate, not a prediction of
         how long a learner takes. Browser API transport is mocked; production
         deployment, representative author usability, and formal accessibility
         certification are not established.
       -->
- [x] M6.2 Build adaptive sequencing overlays that can tighten but not loosen
      platform safeguards. Evidence: property tests.
  - [x] M6.2.a Define overlay scope/version/applicability, eligible
        content/items, sequencing objectives, mastery/pacing/difficulty rules,
        constraints, owner, and lifecycle. Evidence: overlay schema.
  - [x] M6.2.b Merge platform/region/institution/program/course/learner-support
        constraints only by narrower eligibility, stricter limits, or additional
        requirements. Evidence: merge property tests.
  - [x] M6.2.c Prohibit overlays from weakening safety, privacy, accessibility,
        integrity, exposure, rights, prerequisite, assessment, or
        human-authority floors. Evidence: monotonicity tests.
  - [x] M6.2.d Preview affected paths/items/learners using privacy-safe
        aggregates and require review/versioning before activation. Evidence:
        impact tests.
  - [x] M6.2.e Revalidate effective overlay at each decision and preserve exact
        version/rationale in sequence history. Evidence: runtime tests.
       <!-- M6.2 technically complete; operational readiness remains open.

       - CORRECTION (2026-09-05, while building M6.10's privacy cell): the
         impact preview leaked a suppressed bucket. Subjects sit in exactly one
         bucket, so the buckets partition the population once and
         `changed_decision_count` is their margin; publishing it beside
         all-but-one bucket handed the withheld one back by subtraction.
         Demonstrated on this cell's own corpus fixture, where the published
         margin of 15 minus the published affected count of 12 yielded exactly
         item:beta's three affected subjects — and on a constructed 20/20/5
         case where 45 - 40 recovered the five-subject bucket entirely. The
         `residual_disclosure_note` claimed a reader could only *bound* such a
         value between one and nine; they could compute it. The margin is now
         withheld unless nothing is suppressed, or at least two buckets are and
         their combined affected count clears the floor, so subtraction yields a
         group rather than a cell. `tests/test_sequencing_overlay_privacy.py`
         pins it (7 tests), the note now describes the rule it applies, the
         workbench distinguishes "population below the minimum cell size" from
         "publishing it would recover a withheld bucket", and the corpus case
         that asserted the leak is relabelled
         `case:impact-total-withheld-when-subtractable`.
       - The closed `6.2.0` contract states an overlay revision with its owner,
         semantic version, revision hash, lifecycle, and an applicability that
         binds exactly one scope key — six scopes ordered platform, region,
         institution, program, course, learner support, where only platform
         names no key. Eighteen constraint dimensions across twelve families
         carry one of five kinds, and each numeric dimension declares the range
         it is written in, so a ceiling and a floor never share one anonymous
         integer domain. A duplicated dimension, an unsorted constraint set, a
         platform floor outside platform scope, a second platform floor, a
         review naming another overlay, and an overlay reviewing its own
         baseline are each refused at intake.
       - The merge is the **meet** of each kind: minimum for a ceiling, maximum
         for a floor, intersection for an allow-list, union for a required set,
         and logical or for a required flag. `permits` is written from the
         meaning of each kind and never consults `meet`, so the monotonicity
         checks are not grading the merge with the merge. The effective value is
         therefore no more permissive than any overlay that declared it, whatever
         an author writes, and it does not depend on the order the overlays
         arrive in.
       - **An attempt to widen is named rather than silently clamped.** A
         declaration at a scope is compared against the meet of every broader
         scope; when it permits more, the effective value still holds and the
         attempt is reported with the scope, the dimension, the value declared,
         and the value that stands. A silent clamp would be indistinguishable
         from an author who meant what they wrote.
       - **Only four of the five kinds can express an intent to widen.** M6.2.b
         allows *additional requirements*, so a `required_set` declaration is
         additive by nature: a course that writes `{support.reader}` has said
         nothing about the captions the platform already demands, and the union
         keeps both. The other four are complete statements — an allow-list that
         omits a member forbids it — so naming an excluded member is a widening.
       - **A set meet can land on a value no single overlay wrote**, and such a
         value has no author. Those constraints carry
         `combination = "combined_declarations"`, no binding overlay or version,
         every declarer, and a composed rationale. Naming one author would
         misattribute the constraint.
       - Each of the nine protected families of M6.2.c must carry a platform
         floor. **An unstated floor is not a floor**: a missing family leaves the
         decision `indeterminate_missing_protected_floor` and evaluates no
         candidate, rather than treating the family as unconstrained. Weakening a
         stated protected floor is its own `floor` severity, distinct from
         loosening a non-protected broader scope.
       - The impact preview computes which decisions activation would change by
         admitting every supplied subject under the baseline and again under the
         proposal. A bucket is published only when its population, its affected
         count, and its unaffected count are each zero or at least the minimum
         cell size, so **a suppressed cell is not a zero cell** and a genuine
         zero is still published as zero. The published type has no field that
         can carry a subject reference, and the store refuses to persist a
         preview that publishes a small cell. The residual inference a reader
         who already knows a suppressed population can still make is stated
         rather than denied.
       - Activation requires an approved review pinned to this exact revision
         hash and version, by someone other than the owner, over a clean merge
         with every protected floor stated and an impact preview present. A
         review of a different version is not a review of this one.
       - Every decision recomputes the effective overlay from the overlay set;
         a caller's asserted hash is only compared and never adopted. A stale
         assertion refuses to decide at all, which is recorded as distinct from
         refusing every candidate, and a missing protected floor outranks a
         stale assertion. The sequence history keeps every contributing
         overlay's version and revision hash, and every applied constraint's
         binding version and rationale.
       - The executable corpus contains 59 labeled cases across five dimensions,
         all matched, reported with Wilson 95% intervals. The property sweep
         executes six laws over a deterministic, byte-stable generated space:
         idempotence (26), commutativity (164), associativity (1,160),
         meet-is-a-lower-bound (164), decision monotonicity (1,864) and
         protected-floor preservation (21,912), with zero violations. The
         decision law counts only the cases where the merged set actually
         admitted, so the implication is never vacuous, and five negative
         controls break the merge on purpose to prove each law can fail —
         including one recording that a left projection is idempotent *and*
         associative, so only commutativity catches it.
       - Verification: 63 focused engine, property, service, and closed OpenAPI
         tests; ruff and strict mypy; OpenAPI and generated-client drift checks;
         admin typecheck and 35 files / 48 unit tests; twelve deterministic
         evidence-generator tests; 19 shared admin layout and WCAG executions;
         and 21 Chromium/Firefox/WebKit Playwright executions covering the
         merge, the combined value with no author, the nine floors, cell
         suppression, the activation gate, a weakened floor that still loses,
         refusal on overlay drift, the published property laws, 390px overflow,
         and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-sequencing-overlay.json` through `inventory:v1-metis-sequencing-overlay`
         and its verification script. The engine constrains a sequencing decision
         and does not rank pedagogy; selection among admitted candidates is the
         published deterministic rule. Impact aggregates are computed over the
         subject population supplied with the request, and nothing establishes
         that population is a faithful sample of a live cohort. The
         overlay-gated decision path is this engine's own endpoint;
         `metis.services.adaptive_learning_helpers` does not route through it,
         so no claim is made that production sequencing is overlay-gated today.
         Browser API transport is mocked; production deployment, representative
         author usability, and formal accessibility certification are not
         established.
       -->
- [x] M6.3 Build item-pool eligibility rules by objective, mastery band,
      exposure, accommodation, locale, and integrity mode. Evidence: known-value
      selector tests.
  - [x] M6.3.a Define typed rule inputs/outputs and exact item revision
        prerequisites for objective/coverage, difficulty/mastery band, exposure/
        compromise, locale, PNP support, delivery, and integrity state.
        Evidence: eligibility schema.
  - [x] M6.3.b Filter stale/unapproved/unavailable/incompatible/quarantined/
        overexposed items and unresolved mandatory gates with exact reasons.
        Evidence: exclusion tests.
  - [x] M6.3.c Apply current minimized learner-context projection without
        exposing diagnosis/grade/integrity detail to the item bank. Evidence:
        context boundary tests.
  - [x] M6.3.d Select deterministically from eligible items by declared
        strategy/ seed with tie-break and no hidden constraint relaxation.
        Evidence: selector goldens.
  - [x] M6.3.e Persist candidate/rejection/selection/rule/version rationale and
        test every dimension/boundary combination. Evidence: known-value suite.
       <!-- M6.3 technically complete; operational readiness remains open.
       - The closed `6.3.0` contract states an eligibility rule with its owner,
         version, and hash, the revision prerequisites an item must present, and
         one typed sub-rule per dimension: objective coverage, the difficulty
         window each mastery band may be served from, exposure and compromise,
         locale with declared fallbacks, personal-needs supports, delivery, and
         integrity state. **Every candidate is judged on all seven and keeps
         every reason**, so a report never shows only the first thing that went
         wrong.
       - **A verdict about another revision is not a verdict about this one.**
         Each piece of candidate evidence carries the exact item revision it was
         computed against, and evidence computed against an older revision is
         stale however the source graded it — the corpus case is an item whose
         estimate says `passed` and is refused anyway.
       - Twenty exclusion reasons are declared and each is produced in isolation
         by its own candidate. Stale, unapproved, unavailable, quarantined,
         compromised, and overexposed are distinct outcomes rather than one
         bucket, and **an unresolved mandatory gate is neither passed nor
         failed**: an unknown or stale gate excludes under its own reason,
         separate from a blocked one.
       - **The item bank is handed a projection, and the projection type has no
         field a diagnosis, a grade, or an integrity incident could occupy.**
         The property is checked as indistinguishability rather than inspection:
         two learner contexts that differ only in those three fields produce
         byte-identical projections, while a changed accommodation or mastery
         figure changes the hash. The store scans the serialized bytes as well,
         because a sensitive value can travel through a field that is allowed to
         exist. Supports come from accommodations, the band from the mastery
         figure against declared thresholds, and the integrity mode from the
         session — never from the incidents.
       - An unobserved mastery is treated as novice and **says it was not
         observed**, so a fail-closed default is never mistaken for a
         measurement. The band cut points (4,000 / 7,000 / 9,000 basis points)
         are pinned in the engine witness and exercised on both sides.
       - **There is no code path that relaxes a constraint to find something to
         deliver.** `relax_on_empty` and `constraints_relaxed` are `Literal`
         values, the store refuses any analysis claiming otherwise, and an empty
         eligible set is reported as `no_eligible_item` with the tally of which
         dimension excluded how many — distinct again from `empty_pool`, where
         nothing was judged at all. Selection orders eligible candidates by the
         declared strategy and breaks every tie on candidate ID, and the record
         says whether the strategy or the tie-break decided it.
       - A learner's granted accommodation is an *additional* required support,
         merged with the rule's own — the same principle M6.2 applies to a
         required set.
       - Every dimension is probed on both sides of its exact boundary: one
         candidate on the permitted edge, one a single step past it. All seven
         separate. A dimension that did not would be a rule deciding nothing.
       - The M6.2 overlay engine is genuinely called:
         `tighten_rule_with_effective_overlay` maps an effective overlay's
         exposure ceiling, repeat-spacing floor and difficulty ceiling onto the
         rule by meet, so an overlay can tighten a pool and a looser one leaves
         it exactly where it was.
       - The executable corpus contains 95 labeled cases across five dimensions,
         all matched, reported with Wilson 95% intervals.
       - Verification: 64 focused engine, service, and closed OpenAPI tests;
         ruff and strict mypy; OpenAPI and generated-client drift checks; admin
         typecheck, lint, and 36 files / 49 unit tests; eleven deterministic
         evidence-generator tests; 19 shared admin layout and WCAG executions;
         and 18 Chromium/Firefox/WebKit Playwright executions covering the
         projection with no learner detail in it, every exclusion reason, the
         selection and what decided it, an empty eligible set, the seven-way
         boundary probe, 390px overflow, and automatic serious/critical Axe
         analysis.
       - **Correction to M6.2 and M5.22 while here**: the `.bench` entrance
         animation on the sequencing-overlay, item-pool and code-verification
         pages faded opacity, so an Axe run sampling mid-animation graded every
         colour under it against a blended background and a 5.18:1 text colour
         measured 4.49:1. All three now animate transform only. The M6.2 browser
         matrix passed as shipped; it was passing a coin flip.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-item-pool-eligibility.json` through
         `inventory:v1-metis-item-pool-eligibility` and its verification script.
         Eligibility is decided from the candidate evidence an item bank presents
         with the request, and nothing establishes that a bank reports its own
         approval, availability, exposure, or gate state faithfully. Selection is
         a reproducible order, not a claim about which item teaches best. Browser
         API transport is mocked; production deployment, representative author
         usability, and formal accessibility certification are not established.
       -->
- [x] M6.4 Surface BKT, FSRS-5, graph-tracing, and IRT rationale separately.
      Evidence: UI does not collapse distinct models into one opaque score.
  - [x] M6.4.a Define separate model-result contracts with model/version/config/
        input projection/time/item-objective refs, outputs, uncertainty, and
        applicability. Evidence: model schemas.
  - [x] M6.4.b Present BKT knowledge-state/prior/learn/guess/slip/update
        evidence and assumptions. Evidence: BKT view tests.
  - [x] M6.4.c Present FSRS-5 memory stability/difficulty/retrievability/review
        history/desired retention/scheduling evidence. Evidence: FSRS view
        tests.
  - [x] M6.4.d Present graph-tracing matched/missing/extra/path/state/step
        evidence and concept dependencies. Evidence: graph view tests.
  - [x] M6.4.e Present IRT ability/item parameters/information/standard error/
        calibration/linking evidence. Evidence: IRT view tests.
  - [x] M6.4.f Show model disagreements/N/A/stale/insufficient data and combined
        sequencing rule explicitly; prohibit a generic mastery score. Evidence:
        UI contract tests.
       <!-- M6.4 technically complete; operational readiness remains open.
       - The closed `6.4.0` contract gives each model family its own result type.
         Every result carries the same envelope — model id, version, config hash,
         the exact learner projection it read, the objective and item it answered
         about, and **at least one stated assumption** — and then nothing else in
         common. `p_known` exists only on the BKT result, `retrievability` only
         on FSRS, `missing_concept_ids` only on the graph, `ability` only on IRT.
         A measured check confirms the four families share no output field name,
         and the store refuses a panel that claims otherwise.
       - **Abstention is a measured condition, not a label.** `insufficient_data`
         requires `observations_available < observations_required`, and `stale`
         requires an evidence age past a stated limit. A model that abstains
         still publishes everything it computed; it simply carries no verdict and
         names which of `not_applicable`, `insufficient_data`, or `stale`
         stopped it.
       - BKT applies the Corbett–Anderson update: condition on the observation
         with guess and slip, then apply the learn rate, publishing the before,
         the posterior, and the after for every step. **A model with guess + slip
         at or above one is refused at intake**, because such a model reads a
         correct answer as evidence *against* knowing. The recurrence is
         recomputed independently in the test rather than read back.
       - FSRS-5 uses the published power forgetting curve with decay -0.5 and
         factor 19/81. The constants are checked by their own definition:
         retrievability at an elapsed time equal to stability is exactly 90%, and
         the interval for 90% retention is exactly the stability, for every
         stability tried. Stability and difficulty are supplied by the scheduler;
         this engine trains neither and says so.
       - Graph tracing computes matched, missing and extra as exact set
         differences over the expected and observed paths, and marks a dependency
         **unresolved** when a concept the learner reached depends on one they
         missed. A concept is matched or missing, never both.
       - IRT sums three-parameter-logistic Fisher information over the answered
         items, derives the standard error as its reciprocal square root, and
         **reads the lower 95% bound rather than the ability point estimate** —
         the baseline panel is a case where the ability clears the threshold and
         the bound does not. **Zero information means no interval exists**, not a
         very wide one, and a model with no interval is not ready.
       - The combined rule is a conjunction over the models that could answer,
         published as a literal. Every pair that answered differently is named
         with both models and both bases in their own units. With nothing
         applicable the panel is `indeterminate_no_applicable_model` rather than
         `not_ready`, and a model that abstains does not vote — the corpus case is
         an IRT abstention that leaves the other three agreeing on `ready`.
       - **The prohibition on a generic mastery score is structural and
         measured.** No field on the decision is a cross-model number,
         `generic_score_fields` and `composite_scores_computed` are `Literal[0]`,
         and an equal-tally separation probe holds the ready-count fixed while
         varying which model said what: a surface that had collapsed four models
         into one score would answer identically, and all three probes show the
         panels still differ.
       - The executable corpus contains 63 labeled cases across six dimensions,
         all matched, reported with Wilson 95% intervals.
       - Verification: 36 focused engine, service, and closed OpenAPI tests; ruff
         and strict mypy; OpenAPI and generated-client drift checks; admin
         typecheck, lint, and 37 files / 50 unit tests; twelve deterministic
         evidence-generator tests; 19 shared admin layout and WCAG executions;
         and 21 Chromium/Firefox/WebKit Playwright executions covering each
         model's own parameters and assumptions, every disagreement, the explicit
         statement that nothing combines the four, a stale model that abstains
         without losing its numbers, an all-abstaining panel, the separation
         probe, 390px overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-learner-model-rationale.json` through
         `inventory:v1-metis-learner-model-rationale` and its verification
         script. Each model is computed from the parameters supplied with the
         request: this engine does not fit BKT parameters, train FSRS stability
         or difficulty, author a concept graph, or calibrate IRT items. The
         combined rule is a stated policy, not a claim that the four models are
         jointly calibrated or that their agreement implies correctness. The
         estate's `metis.services.learner_mastery_helpers` still collapses a
         single probability into one MASTERED-to-NOT_STARTED label with no model
         attribution — exactly what M6.4.f prohibits — and this surface neither
         reads it nor changed it. Browser API transport is mocked; production
         deployment, representative author usability, and formal accessibility
         certification are not established.
       -->
- [x] M6.5 Build author-facing simulation with synthetic learners only.
      Evidence: no real learner PII enters preview.
  - [x] M6.5.a Define versioned synthetic learner persona/profile distributions
        for prerequisites/mastery/history/preferences/PNP/locale/pacing/
        integrity states with no real source identity. Evidence: fixture schema.
  - [x] M6.5.b Generate deterministic seeded individuals/cohorts and label every
        record/event/result as synthetic. Evidence: generation tests.
  - [x] M6.5.c Run exact pathway/adaptive/tutor/item rules in isolated
        simulation with bounded steps/time/budget and no delivery/grade side
        effects. Evidence: simulation tests.
  - [x] M6.5.d Present paths/decisions/model rationales/outcomes/failures/
        distributions and allow scenario comparison. Evidence: UI tests.
  - [x] M6.5.e Scan source queries, stores, logs, exports, and analytics to
        prove no real learner ID/PII/profile/attempt enters simulation.
        Evidence: isolation suite.
       <!-- M6.5 technically complete; operational readiness remains open.
       - The closed `6.5.0` contract states a versioned, hashed cohort
         specification whose personas are *distributions*: a range for
         prerequisite satisfaction, mastery, history length, pacing, and learning
         gain, plus locale, PNP support, integrity mode, and preference sets.
         Every persona carries `derived_from_real_learner = False` as a `Literal`,
         and persona weights are refused unless they sum to ten thousand basis
         points — a distribution that does not sum to one is not a distribution.
       - **A generated learner reference is a hash of the seed, the specification,
         and the ordinal**, so there is no input through which a caller could
         supply a real identity. The reference pattern is
         `^synthetic:[a-f0-9]{16}$` and the type carries no `learner_id`,
         `email`, `name`, or `external_id` field for one to occupy.
       - Cohort composition uses largest-remainder Hare quota, so the split is
         exact rather than drawn: 40/45/15 over twelve learners is 5/5/2, and the
         same seed rebuilds a byte-identical cohort while another seed does not.
       - **Every emitted record is labelled synthetic** — learner, cohort, step,
         outcome, run, delta, and comparison. The isolation scan caught the two
         comparison types when they were not, which is the point of scanning
         rather than asserting.
       - A step calls the **same item-pool eligibility engine a real decision
         calls** — one engine call per step, counted and bounded. The run is
         bounded three ways, and it stops at the first bound it reaches and names
         which: `max_steps_per_learner`, `max_simulated_days`, or
         `max_engine_calls`. A simulation that silently truncated would report a
         shorter path as a finished one. `delivery_side_effects` and
         `gradebook_writes` are `Literal[0]`.
       - The response model is declared rather than discovered: a learner answers
         correctly with their persona's prerequisite-satisfaction probability,
         drawn from a generator seeded per learner so the same learner draws the
         same sequence in every scenario, and a correct answer closes the declared
         learning-gain fraction of the remaining gap. **A step that delivered
         nothing cannot change mastery**, and an incorrect answer changes nothing.
       - **Both scenarios walk one identical cohort**, so a difference between
         them is the scenario rather than the sample — the comparison carries the
         cohort hash and `cohort_identical` as a `Literal`. The two ratified
         scenarios differ only in their exposure ceiling and all twelve learners'
         runs change.
       - M6.5.e is two halves. The static half walks the **transitive import
         closure** of the modules a simulation runs and refuses any database, ORM,
         learner store, gradebook, progress, analytics, or export module; its
         negative control is `metis.api.admin`, which is not itself on the
         denylist and whose closure is, several imports deep. The dynamic half
         scans everything a run produced for an unlabelled record, an email, a
         version 1–5 UUID, a learner identifier, or a reference that is not
         seed-derived. The report cannot call itself isolated while holding a
         finding, and the store refuses to persist a simulation that is not.
       - **Network capability is reported rather than refused, and named.** The
         closure reaches `socket`, `ssl` and `http.client` through
         `metis.services.source_intake_network`, which the shared hashing helper
         imports for one pure function. Nothing on a simulation path calls it.
         M6.5.e asks about learner data, so this is surfaced as a capability
         finding instead of being quietly folded into a pass.
       - The executable corpus contains 32 labeled cases across five dimensions,
         all matched, reported with Wilson 95% intervals.
       - Verification: 46 focused engine, isolation, service, and closed OpenAPI
         tests; ruff and strict mypy; OpenAPI and generated-client drift checks;
         admin typecheck, lint, and 38 files / 51 unit tests; twelve deterministic
         evidence-generator tests; 19 shared admin layout and WCAG executions; and
         21 Chromium/Firefox/WebKit Playwright executions covering the seeded
         cohort, the bounded runs, a run that names the bound it reached, the
         identical-cohort comparison, the isolation report with its capability
         finding, a page-wide check that no string looks like a person, 390px
         overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-learner-simulation.json` through
         `inventory:v1-metis-learner-simulation` and its verification script. The
         response model reproduces no real learner and predicts no real outcome.
         Persona distributions are authored, and nothing establishes that any of
         them resembles a population an institution actually teaches. The
         isolation claim is about learner data and is not a capability sandbox. A
         simulation exercises item-pool eligibility; it does not exercise
         delivery, rendering, or tutoring dialogue. Browser API transport is
         mocked; production deployment, representative author usability, and
         formal accessibility certification are not established.
       -->
- [x] M6.6 Enforce and measure the adaptive next-item latency budget. Evidence:
      runtime gate, not a documentation assertion.
  - [x] M6.6.a Define latency SLI boundaries from response commit/context
        receipt through eligibility/model/selection to next-item usable
        response, with target percentiles and device/network tiers. Evidence:
        budget spec.
  - [x] M6.6.b Instrument each server/client/cache/model/store stage with
        bounded trace/metric attributes and exact timeout/deadline propagation.
        Evidence: instrumentation tests.
  - [x] M6.6.c Implement bounded candidate pools/model work, caching/fallback/
        degraded paths, cancellation, and honest unavailable state. Evidence:
        runtime tests.
  - [x] M6.6.d Load-test realistic concurrent learners/pools/models/failures/
        cold caches and measure p50/p95/p99/end-to-end error. Evidence: profile.
  - [x] M6.6.e Fail release/runtime health when the ratified budget or missing-
        measurement rule is breached; never assert compliance from design docs.
        Evidence: performance gate.
       <!-- M6.6 technically complete; operational readiness remains open.
       - The closed `6.6.0` contract states the SLI boundary as five ordered
         stages — context receipt, eligibility, model panel, selection, render
         handoff — and budgets each of four delivery tiers at p50, p95 and p99
         with a minimum sample count. A tier whose higher percentile carries a
         tighter budget is refused, a stage whose timeout falls below its own
         budget is refused, and **a budget whose stage budgets sum past the
         tightest end-to-end p50 is refused**: stages that cannot fit inside the
         whole are not a budget.
       - Instrumentation is a propagated deadline, not a stopwatch. Each span
         records what it entered with and what it left with; the contract refuses
         a span that ends holding more deadline than it started with, one whose
         spend does not equal its duration, and a trace where the deadline one
         stage left is not the deadline the next received.
       - **Trace attributes are a closed key vocabulary with bounded values.** A
         URL, an email, a UUID and a 64-character string all fail the value
         pattern, so a metric label fed from a raw identifier is not
         representable and cardinality cannot run away.
       - Candidate and model work are bounded before a stage runs, and a
         truncation is accounted rather than silent. A stage cut off at its
         timeout **spends the timeout, not however long the work took**; with a
         declared fallback the decision is `served_degraded` and names the stage,
         and without one it is `unavailable` with
         `stage_timed_out_without_a_fallback`. An exhausted deadline cancels with
         `deadline_exhausted_before_next_item`, and an exhausted candidate budget
         is `unavailable` having timed nothing at all.
       - The cancellation path is reachable because one ratified tier's p99
         (220 ms) is tighter than the sum of every stage timeout (245 ms), which
         is exactly the condition under which cancellation has anything to do. A
         budget where it could never fire would have left that branch dead.
       - Percentiles are nearest rank over the recorded microseconds, so **a
         reported percentile is a sample that was actually observed** rather than
         an interpolation between two that were. A percentile over no samples
         raises rather than returning zero, and a tier with no samples publishes
         no percentile at all. Durations are integers throughout: a float could
         carry a NaN, and a NaN passes a range check written as two comparisons.
       - A live measurement times the real eligibility, model-panel and selection
         engines on `time.perf_counter_ns`. **No absolute wall-clock figure is
         asserted anywhere** — the corpus checks the gate's logic, not the
         machine's speed — and the browser suite reads a fixed deterministic
         trace rather than whatever this machine happened to do.
       - **The gate reads a measured profile and nothing else.**
         `asserted_from_documentation` is a `Literal[False]`, a gate with no
         breach and no gap cannot pass having evaluated nothing, and **a tier
         below its minimum sample count fails before any budget is compared**,
         because a measurement that was not taken is not a passing one. A breach
         names the tier, the percentile, the budget, the observation, and exactly
         how far past it went. The store additionally refuses a gate that read a
         different profile or judged a different budget than the request carried.
       - The executable corpus contains 36 labeled cases across five dimensions,
         all matched, reported with Wilson 95% intervals.
       - Verification: 43 focused engine, service, and closed OpenAPI tests; ruff
         and strict mypy; OpenAPI and generated-client drift checks; admin
         typecheck, lint, and 39 files / 52 unit tests; thirteen deterministic
         evidence-generator tests; 19 shared admin layout and WCAG executions;
         and 24 Chromium/Firefox/WebKit Playwright executions covering the
         ratified budget, the measured percentiles, a passing gate that asserts
         nothing, a breach that says how far over, a short measurement that fails
         first, an instrumented trace with its propagated deadline and bounded
         attributes, 390px overflow, and automatic serious/critical Axe analysis.
       - Evidence is reproducible at `evidence/v1-workbenches/
         metis-adaptive-latency.json` through `inventory:v1-metis-adaptive-latency`
         and its verification script. A live measurement times this engine's
         in-process compute; it is not a measurement of a deployed service's
         network, database, cold start, or contention. The budget is a set of
         numbers a working group ratified and nothing establishes that they are
         the right ones for any learner or device. Delivery-tier names stand for
         a device and network pairing; this engine is told which tier a request
         belongs to rather than detecting it. Browser API transport is mocked;
         production deployment, representative author usability, and formal
         accessibility certification are not established.
       -->
- [x] M6.7 Link tutor personas, prompts, tools, sources, and course versions.
      Evidence: tutor cannot use unapproved or stale course material.
  - [x] M6.7.a Bind each tutor-config revision to exact persona/pedagogy/hint,
        system prompt/template, allowlisted tools, source set, and course/item
        revisions. Evidence: binding schema.
  - [x] M6.7.b Resolve only approved/current/published material and track
        source/ course update/retraction/rights blast radius. Evidence: resolver
        tests.
  - [x] M6.7.c Validate prompt/tool/source/course compatibility,
        safety/integrity/ PNP/locale policies, and grants before
        activation/session. Evidence: configuration tests.
  - [x] M6.7.d Persist effective versions in each session/response and reject
        stale/retired/direct substituted IDs. Evidence: stale-material tests.
  - [x] M6.7.e Present authorized version/diff/status and migrate/deactivate
        explicitly without rewriting historical sessions. Evidence: lifecycle
        tests.
       <!-- M6.7 evidence (2026-09-05)
         Built the 6.7.0 tutor configuration binding contract
         (apps/metis/service/src/metis/schemas/tutor_configuration_binding.py),
         the resolution/compatibility/session/migration engine
         (services/tutor_configuration_binding_engine.py), a 45-case labelled
         corpus over 30 fixture families and five dimensions
         (data/m6_7_tutor_binding_corpus.json, evaluated by
         services/tutor_configuration_binding_evaluation.py), an append-only
         tenant-scoped store (services/tutor_configuration_binding_service.py),
         instructor + admin routes (api/tutor_configuration_binding.py,
         api/admin.py), and the admin workbench at /tutor-bindings.

         The evidence clause is structural, not a promise. A MaterialRef has no
         bare-identifier shape: kind, material_id, revision_id and a 64-hex
         revision_sha256 are all required and additionalProperties is false, so
         no client can express "use the current one". Resolution compares the
         bound revision against the catalogue and yields exactly one success
         (`resolved`) and seven refusals (`stale`, `not_approved`,
         `not_published`, `retracted`, `rights_revoked`, `substituted`,
         `unresolvable`), each of which blocks activation.

         A CATALOGUE HOLDING A DIFFERENT REVISION UNDER THE SAME IDENTIFIER IS
         A SUBSTITUTION, NOT AN UPDATE. The unknown-revision check runs before
         any status check, so a material the catalogue has "moved on" from is
         named rather than followed — verified over the full 60-row cross
         product of status x published x rights_cleared x revision in
         test_an_unknown_revision_is_substituted_whatever_else_the_record_says.
         A superseded revision the catalogue still holds is `stale` instead;
         that distinction is only possible because MaterialRecord carries
         known_revision_sha256s, not just the current one.

         RESOLUTION IS PERFORMED AGAIN AT EVERY SESSION START. Approving a
         configuration is not a licence that survives its material: the engine
         re-resolves before it starts a session, and refuses rather than
         substituting (`refuse_the_session_rather_than_substitute_a_revision`).
         A refused session records no effective material at all. The
         stale-material sweep runs one probe per blocking outcome and asserts
         all three properties per probe — blocks activation, refuses the
         session, leaves the historical session intact: 7/7 separate.

         A MIGRATION MINTS A REVISION AND REWRITES NOTHING. MigrationRecord
         carries historical_sessions_rewritten: Literal[0] and TutorSessionRecord
         carries rewritten_by_migration: Literal[False], so a rewrite is
         unconstructible rather than merely unimplemented; both consts appear in
         the published OpenAPI. `revision_current` on the workspace projection is
         computed over what the tenant holds, never a stored flag.

         AN ALLOWLISTED TOOL NOTHING REFERENCES IS ADVISORY, NOT BLOCKING — the
         only advisory code in the vocabulary, and CompatibilityFinding's
         validator makes severity a function of the code, so it cannot be set
         wrong (a mutation that forced blocking=True was refused by the contract
         outright). A prompt reaching for an ungranted tool or citing an unbound
         source blocks; so does a locale, support, or integrity mode the course
         does not offer, a draft lifecycle, a missing grant, a self-grant, and a
         grant pinned to another revision.

         Verification: 58 focused Python tests (28 engine, 12 property, 11
         service, 7 API contract); corpus 45/45 matched across all five
         dimensions with Wilson 95% intervals; ruff + mypy --strict clean on
         every touched module; OpenAPI and the generated TypeScript client
         regenerated (5 paths, 31 schemas added, none removed); 42 browser
         checks green across chromium/firefox/webkit with automatic serious +
         critical axe analysis and a 390px overflow audit; 40 admin vitest files
         (53 tests); 14 evidence-generator tests, every executable clause
         satisfied.

         Mutation-tested rather than merely asserted: making substitution follow
         the catalogue mismatched 2 corpus cases and broke the sweep; letting a
         blocked configuration start its session mismatched 4; treating a
         superseded revision as resolved mismatched 6.

         Two defects this cell surfaced and fixed in shared code: the admin
         scroll containers were not keyboard-reachable (an overflow-x region
         needs focus and a name), and the fixed sidebar used minHeight: 100vh
         with no scroll, so every link past the viewport was unreachable by
         mouse or keyboard — it is now bounded with a scrolling nav. A name
         collision was also avoided rather than accepted: M6.7's activation
         types are BindingActivationBlocker/BindingActivationDecision because
         reusing M6.2's names would have silently renamed both in the published
         API and the generated client.

         Limitations, stated rather than implied: resolution reads the catalogue
         supplied with the request, and nothing here establishes that a
         catalogue faithfully reports what any content system currently
         approves, publishes, or has retracted. Tool and source references are
         read from the prompt body with a pinned pattern, so a template that
         reaches a capability another way — a tool the model selects at runtime,
         say — is outside what this check can see. Compatibility compares
         declared locales, supports and integrity modes; it does not inspect the
         material itself. The estate's existing tutoring service separately
         refuses source-grounded answers when a linked source package is not
         approved; this cell neither replaces that check nor claims to have
         changed it, and NO PRODUCTION TUTOR RUNTIME IS BOUND TO THIS ENGINE.
         Browser API transport is mocked; production deployment, representative
         author usability, and formal accessibility certification are not
         established.
       -->
- [x] M6.8 Build tutor-response grounding and pedagogical policy evaluation.
      Evidence: generated guidance cites current approved sources.
  - [x] M6.8.a Decompose tutor output into factual/procedural/hint/pedagogical/
        motivational/safety claims and attach exact course/source citations.
        Evidence: response schema.
  - [x] M6.8.b Verify support/sufficiency/faithfulness/contradiction/currency
        for claims and required current approved source use. Evidence: grounding
        fixtures.
  - [x] M6.8.c Evaluate hint-ladder stage, scaffolding, direct-answer exposure,
        learner agency, cognitive demand, tone, accessibility, and assessment
        mode against exact policy. Evidence: pedagogy fixtures.
  - [x] M6.8.d Preserve raw evaluator/rule/model outputs, uncertainty/N/A/error,
        policy verdict, and human review separately. Evidence: result tests.
  - [x] M6.8.e Block/redact/regenerate/escalate according to policy and verify
        no ungrounded response is presented as supported. Evidence: end-to-end
        gate.
       <!-- M6.8 evidence (2026-09-05)
         Built the 6.8.0 tutor-response grounding contract
         (apps/metis/service/src/metis/schemas/tutor_response_grounding.py), the
         decomposition/grounding/pedagogy/enforcement engine
         (services/tutor_response_grounding_engine.py), an 83-case labelled
         corpus over 26 fixture families and five dimensions
         (data/m6_8_tutor_response_grounding_corpus.json, evaluated by
         services/tutor_response_grounding_evaluation.py), an append-only
         tenant-scoped store (services/tutor_response_grounding_service.py),
         instructor + admin routes (api/tutor_response_grounding.py, api/admin.py),
         and the admin workbench at /tutor-responses.

         THE DECOMPOSITION LOSES NOTHING, STRUCTURALLY. Segments are contiguous,
         carry their own trailing whitespace, start at zero and end at the
         response length, and TutorResponseDecomposition re-hashes the
         concatenation of their exact text against the response digest. A
         decomposition that dropped a sentence is unconstructible, not merely
         untested — the probe that removes the last claim is refused with "claim
         spans must reach the end of the response". Verified over eight shapes
         including leading whitespace, no terminator, blank-line runs and
         "One.Two.Three.".

         "CURRENT APPROVED SOURCE" HAS ONE DEFINITION IN THIS ESTATE, NOT TWO.
         Citations carry M6.7's MaterialRef — the shape with no bare-identifier
         form — and currency is decided by calling M6.7's own resolve_material
         and carrying its outcome verbatim. All eight M6.7 outcomes are exercised
         from M6.8, and CitationCurrency's validator makes current_approved a
         function of the outcome, so this cell cannot hold a second opinion about
         what approved means. A mutation pinning current_approved to True was refused
         by the contract before any corpus case ran.

         AN INSTRUMENT THAT COULD NOT LOOK DOES NOT RETURN A VERDICT. Three blind
         seams each produce an EvaluatorError and an abstention, never a pass: no
         catalogue to resolve against (an "unresolvable" outcome there is the
         absence of an instrument, not a finding), an unreadable excerpt, and an
         absent answer key. A fourth was found during the build: the cognitive-
         demand dimension reported "the turn asks the learner to do nothing" for
         a turn that plainly did ask, when the Anderson & Krathwohl verb table
         simply had not recognised the task. That is now three distinct states —
         the policy sets no floor (not applicable), the turn asks nothing (not
         applicable), and the floor could not be checked (abstain) — and the
         detail says which.

         NOTHING IS AVERAGED. Each of thirteen dimensions keeps its own state and
         TutorPolicyVerdict's validator recomputes the worst one and the
         dimension that produced it; `averaged` is a Literal[False]. A mutation
         switching the engine to a majority vote was refused by the contract
         outright.

         REDACTION IS DELETION, AND A CITATION MARKER IS A CLAIM THE GATE STOOD
         BEHIND. presented_text must equal the concatenation of exactly the spans
         that survived redaction, and presented_as_supported is permitted only
         where the support verdict is `supported`, the claim is grounded and
         every cited revision resolved. Both are validators on the response.
         Ten ungrounded-presentation probes attempt the forbidden constructions
         against the real contract — presenting an unsupported claim as
         supported, presenting a redacted claim, keeping a redacted span in the
         presented text, a block or an escalate that still carries text, a
         redaction that removes nothing, a release that also redacts, a verdict
         softened below its worst dimension, a decomposition that drops a
         sentence, and a stale citation presented as supported. 10/10 refused.

         Four defects this cell surfaced and fixed in its own code before
         shipping:
         - THE EVALUATION ID DID NOT BIND EVERYTHING THE VERDICT DEPENDS ON. It
           hashed the turn, the policy and the config revision but not the
           catalogue, so the same turn judged against two catalogues that
           disagree about whether its citation is still current collided on one
           identifier — precisely the pair of runs an operator most needs to tell
           apart. Surfaced by generating the browser fixture from the real
           service, which refused the second write. The id now binds the whole
           semantic request.
         - A COMMENT NAMED A RULE THE CODE DID NOT APPLY. The enforcement table
           said "nothing survives a contradiction", but the flag it read covered
           only conflicts *between two cited excerpts*; a turn asserting
           something its own cited source denies was being trimmed and the
           remainder shipped. Both now end the turn: trimming a falsehood and
           releasing the rest treats a fabrication as a layout problem.
         - `safety_reviewer` WAS UNREACHABLE VOCABULARY. Every abstaining safety
           claim was caught by the definite-failure branch first. A safety claim
           whose grounding is merely uncertain now routes to a safety reviewer
           instead of being blocked outright — blocking would leave a learner
           with nothing at the moment they most need something.
         - `TutorPolicyOutcome.not_applicable` WAS A VALUE NO INPUT COULD
           PRODUCE, because the ladder, scaffolding, tone and accessibility
           dimensions always evaluate. Removed; the branch now raises.
         The producibility audit that caught the last two is a test: every value
         of every M6.8 vocabulary — 6 claim kinds, 6 support verdicts, 5
         sufficiency criteria (each also shown failing), 4 faithfulness changes,
         2 contradiction kinds, 12 pedagogy finding codes, 3 evaluator errors, 5
         dimension states, 3 policy outcomes, 5 enforcement actions, 7 reason
         codes, 3 escalation targets and all 5 ladder stages — is produced by a
         named input.

         Mutation-tested rather than merely asserted, eight mutations, all
         caught: three by the contract refusing to represent the wrong state
         (currency ignoring the catalogue, a majority-vote verdict, a redaction
         that keeps the text), five by corpus mismatch (an abstention read as
         support, an unclassified segment defaulting to a learner prompt, an
         ignored polarity conflict, a missing answer key read as no exposure, a
         source-contradicted claim trimmed instead of blocked). A methodology
         note worth keeping: two mutations of identical byte length applied
         within the same second reused a stale .pyc and reported the same result;
         the run is now made with bytecode caching off, and the second mutation
         proved to be caught only after that.

         Verification: 82 focused Python tests (34 engine, 33 property, 8
         service, 7 API contract) and the whole 2,714-test Metis suite run
         once (2,655 passed, 53 skipped, 6 failed — the OpenAPI staleness gate,
         since regenerated and passing, plus five pre-existing M5.x
         item-difficulty/item-assembly/evidence-bundle fixture failures whose
         every imported module this branch leaves untouched, e.g.
         test_item_difficulty_helpers asserting sample_size 64 against a fixture
         producing 640); corpus 83/83 matched across decomposition,
         grounding, pedagogy, separation and enforcement with Wilson 95%
         intervals; ruff + ruff format + mypy --strict clean on every touched
         module; OpenAPI and the generated TypeScript client regenerated (7
         paths, 36 schemas added, none removed, contract check clean); 30 browser
         checks green across chromium/firefox/webkit with automatic serious +
         critical axe analysis, a 390px overflow audit and a keyboard-reachability
         sweep over every scrolling region; 41 admin vitest files (54 tests). The
         browser fixture is generated from the real service rather than
         hand-written, so the mocked payloads are the contract's own output.

         M6.8.d is structural in the storage layout, not only in the schema: a
         verdict document has no review field at all, and a review is written to
         a separate file in the same tenant directory. The service re-reads the
         verdict bytes after appending and refuses if they changed; the
         projection restates the engine's action and outcome beside the reviews
         and validates that neither moved.

         Limitations, stated rather than implied: segment classification is a
         declared lexical vocabulary hashed into rules_sha256, and support is
         normalized content-token containment with polarity and numeral checks —
         bounded conformance on a checked-in corpus, not open-domain natural-
         language inference, and the Wilson intervals describe this corpus only.
         Readability is Flesch-Kincaid over a vowel-group syllable estimate and
         cognitive demand is an Anderson & Krathwohl verb table; both are named
         approximations with the tradeoff written down, and a task phrased
         outside the table abstains rather than passing. Tone is judged only
         against phrases the supplied policy bans. Direct-answer exposure is
         normalized containment of the supplied answer key, so a disclosure
         phrased differently is outside what this check can see. Nothing here
         establishes that a catalogue faithfully reports what any content system
         currently approves, nor that an approved source is true. NO PRODUCTION
         TUTOR RUNTIME IS BOUND TO THIS ENGINE, and the legacy per-session
         scorer in services/tutor_evaluation_helpers.py — which does average its
         dimensions into one 0-100 number — is untouched and not replaced by this
         cell. Browser API transport is mocked; production deployment,
         representative author usability, and formal accessibility certification
         are not established.
       -->
- [x] M6.9 Build assessment-mode integrity interlocks for tutor and
      visualization tools. Evidence: active assessed/exam contexts block
      forbidden assistance.
  - [x] M6.9.a Define assessment context assertion with exact assessment/item/
        attempt, mode, allowed aids/tools/hints/content, time window,
        institution policy, actor/session, and signature/freshness. Evidence:
        context schema.
  - [x] M6.9.b Map practice/homework/quiz/graded/exam/proctored/accommodation/
        review modes to allowed tutor/visualization/execution/source/answer
        operations. Evidence: policy matrix.
  - [x] M6.9.c Revalidate mode on every tool request/stream turn/deep link and
        fail closed on missing/stale/conflicting context. Evidence: runtime
        tests.
  - [x] M6.9.d Prevent direct routes, alternate modality, cached session, prompt
        injection, tool alias, graph/media, and accessibility-channel bypass.
        Evidence: adversarial tests.
  - [x] M6.9.e Provide safe refusal/permitted scaffolding/escalation and log
        privacy-minimized interlock events without auto-accusation. Evidence:
        journey tests.
       <!-- M6.9 evidence (2026-09-05)
         Built the 6.9.0 interlock contract
         (apps/metis/service/src/metis/schemas/assessment_interlock.py), the
         verification/matrix/decision engine
         (services/assessment_interlock_engine.py), a 67-case labelled corpus
         over 13 fixture families and five dimensions
         (data/m6_9_assessment_interlock_corpus.json, evaluated by
         services/assessment_interlock_evaluation.py), an append-only
         tenant-scoped store with mint and check
         (services/assessment_interlock_service.py), instructor + admin routes
         (api/assessment_interlock.py, api/admin.py), and the admin workbench at
         /assessment-interlocks.

         ONE PROPERTY ANSWERS ALL SEVEN NAMED BYPASSES. `disposition_for(mode,
         operation)` takes two arguments, and neither of them describes how the
         ask arrived. Modality, route, checkpoint and every free-text field the
         request carries are recorded for the operator and are not inputs to the
         decision, so a direct route, another modality, a resumed session, an
         injected instruction, a tool alias, a media probe and the accessibility
         channel all reach the same cell of the same matrix. The bypass sweep
         asks for one denied operation seven ways and reports whether the
         disposition or the outcome moved: 7/7 invariant. Three mutations that
         made the disposition depend on the channel, the route, or the
         checkpoint were each caught by that sweep and by nothing else.

         THE MATRIX IS TOTAL, AND SAYS SO FROM A WALK RATHER THAN A CLAIM.
         8 modes x 13 operations = 104 cells, every one decided, validated at
         import: a hole raises, and so does a mode whose row merely repeats
         another mode's, because a mode that decides nothing differently is a
         label rather than a mode. The report counts decided cells, lists
         undecided ones, names duplicate rows, and checks that all four declared
         dispositions are actually used — removing one cell and making proctored
         identical to exam were both refused at import. The workbench prints the
         engine's own 104 cells rather than reconstructing the grid from
         whichever cells the corpus samples.

         AN UNAUTHENTICATED MODE IS A CLAIM, NOT A FACT. When no assertion is
         presented, the signature does not match, or no signing key is
         configured, the decision reports `mode: null` — the contract makes a
         stated mode and a verified context equivalent, so an interlock cannot
         log a mode it never authenticated. A mutation that reported the
         assertion's own mode anyway was refused by the contract outright.

         THE TWO HALVES FAIL IN OPPOSITE DIRECTIONS, DELIBERATELY. Minting an
         assertion without a key RAISES (503) — you cannot sign without a key,
         and issuing an unsigned context would hand verification something it
         then has to trust. Verifying without a key DENIES, records
         `signature_checked: false`, and withholds the mode — an interlock that
         cannot check its context must not permit the operation it guards.

         AN INTERLOCK EVENT CANNOT CARRY LEARNER CONTENT OR AN ACCUSATION. There
         is no field on InterlockEvent able to hold a message, an answer, a note
         or an actor id — every field is an identifier, a closed-vocabulary
         value, or a digest — and `learner_accused` is a `Literal[False]`. The
         actor appears only as a pseudonym, and `pseudonym_keyed` says whether
         the signing key was available to key it, because an unkeyed digest is
         confirmable by anyone who can guess the actor id and should say so
         rather than implying otherwise. Refusal wording is drawn from a closed
         set, so a refusal can neither leak the answer it withholds nor be
         phrased as an allegation; a test asserts the wording contains none of
         cheat/misconduct/violation/suspicious.

         Two defects and two dead vocabulary values fixed before shipping:
         - `interlock.context.payload-mismatch` WAS UNREACHABLE. The assertion's
           validator recomputes `payload_sha256` from its own body, so a mismatch
           is unconstructible and the failure code could never fire. Removed.
         - `interlock.refusal.operation-not-recognised` REPLACED A FABRICATED
           CLASSIFICATION. An operation the alias table does not know now records
           `canonical_operation: null` and denies, rather than being guessed into
           the strictest class — a fabricated classification in an audit trail is
           worse than an honest gap. A mutation that guessed `answer_check` was
           caught by the corpus.
         - `route` WAS TYPED AS AN IDENTIFIER, so no real route could be sent;
           it is now a bounded path type that refuses a traversal sequence.
         - The engine's `matrix_rows()` helper had NO CALLER once the report
           carried the rows itself; removed rather than left as decoration.

         Escalation is a routing decision, not a softer refusal: a denial under
         `proctored` escalates to a human because that is what proctoring means,
         and an ungranted aid under `accommodation` escalates because whether an
         accommodation covers something is a person's call rather than a
         matrix's. Uncertainty never becomes a silent no.

         Verification: 113 focused Python tests (75 engine, 22 property, 12
         service, 7 API contract), including a sweep of all 104 cells at all four
         checkpoints (416 decisions, none falling through); corpus 67/67 matched
         across context, matrix, revalidation, bypass and journey with Wilson 95%
         intervals; 11 mutations, all caught (2 by the import-time matrix
         validator, 2 by the contract, 7 by corpus or bypass mismatch); a
         producibility audit asserting every context-failure code, outcome,
         refusal reason, escalation reason, disposition, mode and operation class
         is produced by a named input; ruff + ruff format + mypy --strict clean;
         OpenAPI and the generated TypeScript client regenerated (6 paths, 22
         schemas added, none removed, contract check clean); 36 browser
         checks green across chromium/firefox/webkit with automatic serious +
         critical axe analysis, a 390px overflow audit and a keyboard-
         reachability sweep. The browser fixture is generated from the real
         service rather than hand-written.

         Limitations, stated rather than implied: the interlock governs operation
         CLASSES. It does not inspect payload content, so a learner who extracts
         an answer from content that an allowed operation legitimately returned
         is outside what this check can see — the graph/media and accessibility
         bypasses are answered at the operation layer, not the content layer.
         The alias table covers the names this estate uses; a caller inventing a
         new name is denied as unrecognised, which is safe but is not the same as
         understanding it. Nothing here establishes that an institution's policy
         revision is authentic, only that the assertion and the request agree
         about which one they are under. NO PRODUCTION TUTOR OR VISUALIZATION
         RUNTIME IS BOUND TO THIS INTERLOCK, and M6.8's separate assessment-mode
         pedagogy dimension is untouched and not replaced by it. Browser API
         transport is mocked; production deployment, representative operator
         usability, and formal accessibility certification are not established.
       -->
- [x] M6.10 Build author analytics for completion, misconception, item
      performance, objective mastery, fairness, and content drop-off. Evidence:
      aggregation privacy and minimum cohort rules.
  - [x] M6.10.a Define each metric’s population/denominator/window/source/
        revision/unit/missingness/owner and valid descriptive use. Evidence:
        metric catalog.
  - [x] M6.10.b Compute completion/progression/drop-off by course/module/lesson/
        activity/content step and item performance/option/rubric/time/exposure.
        Evidence: known-value tests.
  - [x] M6.10.c Compute objective mastery/model-specific summaries and aggregate
        misconception evidence without individual diagnosis. Evidence: learning
        analytics tests.
  - [x] M6.10.d Compute approved fairness/DIF/accessibility/outcome slices with
        minimum cohorts, uncertainty, privacy suppression, and no hidden trait
        inference. Evidence: privacy tests.
  - [x] M6.10.e Present revision-aware trends/comparisons/filters and explicit
        unknown/suppressed/stale/causal-limit state. Evidence: analytics UI
        tests.
       <!-- M6.10 evidence (2026-09-05)
         Built the 6.10.0 author-analytics contract
         (apps/metis/service/src/metis/schemas/author_analytics.py), the
         catalog/aggregation/disclosure engine
         (services/author_analytics_engine.py), a 38-case labelled corpus over 13
         fixture families and five dimensions
         (data/m6_10_author_analytics_corpus.json, evaluated by
         services/author_analytics_evaluation.py), an append-only tenant-scoped
         store (services/author_analytics_service.py), instructor + admin routes
         (api/author_analytics.py, api/admin.py), and the admin workbench at
         /author-analytics.

         A METRIC WITH NO STATED DENOMINATOR IS NOT A METRIC. Every catalog entry
         declares its population, window, source revision, missingness policy,
         owner, valid descriptive use and known limitations; a rate or a
         proportion must also state what it divides by, and a unit that is not a
         quotient is refused if it supplies one. `missingness_policy` has NO
         default, because treating a gap as a zero is a decision with
         consequences and has to be written down. The catalog also refuses causal
         prose: a metric whose stated use contains "improves", "causes", "leads
         to" or thirteen other markers cannot be constructed, because author
         analytics counts what happened and does not establish why.

         A VALUE EXISTS EXACTLY WHEN IT IS REPORTED. `MeasuredValue.value` is
         `None` unless `state` is `reported`, so a suppressed cell cannot render
         as zero and an unknown cell cannot either — the two absences this estate
         most often collapses into a misleading nought. Five states are
         distinguished (reported, unknown, suppressed, stale, not_applicable) and
         the workbench prints the state rather than a number for four of them.

         A SUPPRESSED CELL IS NOT RECOVERABLE FROM A PUBLISHED MARGIN. This is
         the M6.2 defect this session found and fixed, enforced here one level
         higher: `CohortTable` REFUSES a reported margin beside a suppressed
         cell, so the arrangement is unconstructible rather than documented as a
         residual risk. Raising the cohort floor from 10 to 20 in the workbench
         withholds a cohort that was published a moment earlier, and withholds
         the total with it.

         A SLICE IS ONLY EVER OVER AN ATTRIBUTE SOMEBODY RATIFIED. Cohort
         dimensions are M5.12's vocabulary, reached through four named optional
         fields on `ObservationCohorts` — no map, no free key, no derived value —
         so an attribute nobody approved has nowhere to arrive and the engine has
         no inference path at all. A probe supplying `inferred_ability` is
         refused by the shape itself.

         Known values, worked by hand and then checked: completion 24/30 = 0.8;
         drop-off 6/30 = 0.2, counted independently and summing to 1.0 rather
         than derived from completion; item facility 13/30 = 0.433333 with a
         Wilson interval matching the published formula to six places; mastery
         10/20 for model:bkt beside 15/20 for model:irt, reported separately
         because two models disagreeing is a finding rather than an average;
         misconception prevalence 3/30, on a shape with no learner field at all
         and `individual_diagnosis: Literal[False]`.

         Two defects found during the build:
         - THREE MUTATIONS SURVIVED THE FIRST CORPUS. An empty population
           reported as zero, mastery pooled across models, and a comparison
           across content revisions claiming to be like for like all passed 33/33
           because the corpus had no fixture that could see them. Closing the
           gaps turned the comparison into something that answers the question it
           names: it is emitted only when the observations actually span two
           revisions, and `like_for_like` is a function of whether they do.
         - A SCHEMA NAME COLLISION RENAMED M6.3's PUBLISHED SCHEMA.
           `ObjectiveMastery` already existed for one learner's mastery of one
           objective; defining a cohort aggregate by the same name made FastAPI
           emit `ObjectiveMastery-Input` and `ObjectiveMastery-Output` and delete
           the plain name, breaking the generated client for M6.3. Caught by
           diffing the published schema set before and after, which showed
           +28/-1. Renamed to `ObjectiveMasteryAggregate`; a new test walks all
           2,868 model classes under `metis.schemas` and asserts no two modules
           define the same name. The estate holds that invariant everywhere else,
           so it is now pinned.

         Verification: 44 focused Python tests (29 engine, 7 service, 8 API
         contract); corpus 38/38 across catalog, known_value, learning_analytics,
         privacy and state with Wilson 95% intervals; 8 mutations, all caught (3
         by the contract refusing to represent the wrong state, 5 by corpus
         mismatch); 5 disclosure probes, all refused; ruff + ruff format + mypy
         --strict clean; OpenAPI and the generated TypeScript client regenerated
         (4 paths, 27 schemas added, none removed, contract check clean); 33
         browser checks green across chromium/firefox/webkit with automatic
         serious + critical axe analysis, a 390px overflow audit and a
         keyboard-reachability sweep. The browser fixture is generated from the
         real service rather than hand-written.

         Limitations, stated rather than implied: the disclosure rule is minimum
         cohort plus margin withholding. It defends against recovering a withheld
         cell by subtracting the published ones from the total; it does NOT
         defend against a reader who already knows a cohort's population and can
         therefore bound its value, and it is not differential privacy. Fairness
         slices are computed over supplied, ratified attributes only — nothing
         here establishes that those attributes were collected with consent, only
         that no other kind can be sliced on. DIF proper remains M5.12's cell;
         this one reports completion by cohort and does not estimate item bias,
         and the outcome slices named in M6.10.d are covered only to the extent
         that completion is one. Rubric-level item analytics named in M6.10.b are
         NOT built: the observation shape carries option selection, timing and
         exposure, and no rubric. NO PRODUCTION LEARNING-RECORD STORE IS BOUND TO
         THIS ENGINE; observations arrive in the request. Browser API transport is
         mocked; production deployment, representative author usability, and
         formal accessibility certification are not established.
       -->
- [x] M6.11 Build feedback-to-revision queues. Evidence: learner data is
      aggregated/redacted and does not auto-edit content.
  - [x] M6.11.a Define feedback signal/source/aggregation/version/severity/
        evidence/privacy, queue item, target content revision, owner, status,
        and decision contract. Evidence: queue schema.
  - [x] M6.11.b Ingest approved surveys/comments/support/corrections/analytics/
        performance/accessibility/integrity-safe signals with PII redaction and
        cohort thresholds. Evidence: intake tests.
  - [x] M6.11.c Deduplicate/cluster/priority-rank with attributed rule/model
        versions, uncertainty, counterexamples, and no unreviewed learner quote
        exposure. Evidence: triage tests.
  - [x] M6.11.d Let authors inspect/accept/reject/defer/request research and
        create a new draft revision linked to signals; never auto-edit published
        content. Evidence: authority tests.
  - [x] M6.11.e Track revision/gate/review/publication outcome and close the
        loop with privacy-safe evaluation. Evidence: lineage tests.
       <!-- M6.11 evidence (2026-09-05)
         Built the 6.11.0 feedback-queue contract
         (apps/metis/service/src/metis/schemas/feedback_queue.py), the
         intake/triage/decision engine (services/feedback_queue_engine.py), a
         30-case labelled corpus over 13 fixture families and five dimensions
         (data/m6_11_feedback_queue_corpus.json, evaluated by
         services/feedback_queue_evaluation.py), an append-only tenant-scoped
         store with a decision flow (services/feedback_queue_service.py),
         instructor + admin routes (api/feedback_queue.py, api/admin.py), and the
         admin workbench at /feedback-queues.

         A SIGNAL CAN PRODUCE A PROPOSAL, AND THERE IS NO FIELD THROUGH WHICH IT
         COULD PRODUCE AN EDIT. DraftRevisionProposal names the revision it is
         BASED ON and a new draft it mints; it has no field naming a published
         revision to overwrite, and `published_content_modified` is a
         `Literal[False]`. Only an acceptance mints a proposal — a validator ties
         the two together, so attaching a draft to a rejection is
         unconstructible. Six auto-edit probes attempt every route to an edit
         (flipping the literal, adding an overwrite field, giving a rejection a
         proposal, publishing without a passing gate, and two quote routes);
         6/6 refused.

         THE QUEUE HAS NO FIELD ABLE TO HOLD A LEARNER'S WORDS. QueueItem carries
         signal identifiers, counts, severity and uncertainty, and nothing a
         learner wrote. A quote reaches it only as a ReleasedQuote, which cannot
         be constructed without the reviewer who released it, when, and the
         redactions applied — so "no unreviewed learner quote exposure" is a
         missing field rather than a rule somebody has to remember. Raw text
         lives in RawFeedbackSignal at intake and stops there: AcceptedSignal,
         the only thing intake emits, has no verbatim field and says
         `verbatim_retained: Literal[False]`.

         THE PRIVACY CLAIM IS SETTLED BY LOOKING. The published queue payload is
         walked with M6.5's identity detectors — one definition of what a learner
         identity looks like, reused rather than copied — and the response
         carries the finding count it actually got. The service scans again on
         the way to disk, so a record carrying an identity is not persistable
         whatever the engine reported.

         Three defects found in this cell's own code before shipping:
         - A DETECTOR IS NOT A REDACTOR. Reusing M6.5's `LEARNER_ID_PATTERN` to
           *remove* rather than detect left `[redacted-learner]042`: the pattern
           matches `learner:` plus ONE character, which answers "is there an
           identifier here?" and does not remove one. Removal now uses
           maximal-munch patterns, asserted directly per kind.
         - AND THEN THE FIX WAS OVERCLAIMED. The first comment said re-scanning
           proves the redaction worked. It does not: `[redacted-learner]042`
           matches no detector, because the `learner` inside the placeholder is
           followed by `]`. The re-scan catches residue that STILL LOOKS LIKE an
           identity and cannot see residue the placeholder masked; what protects
           is maximal munch. The comment and the tests now say exactly that, and
           a test asserts the re-scan's blind spot rather than hiding it.
         - A VOCABULARY WORD TRIPPED THE SCAN ON ITSELF. The redaction kind
           `learner_identifier` matches the learner detector, so the published
           payload reported one identity finding — its own enum value. Narrowing
           the scan to dodge that would have hollowed it out, so the value is
           renamed `identity_reference` and the scan stays total.

         `feedback.intake.nothing-left-after-redaction` was dead until a fourth
         fix: "[redacted-email]" is not an empty string, so a submission that was
         only an address was being accepted as a signal with no content, padding
         a cluster's reach. Intake now checks what survives once the placeholders
         are removed.

         Verification: 53 focused Python tests (35 engine, 11 service, 7 API
         contract); corpus 30/30 across intake, triage, authority, lineage and
         privacy with Wilson 95% intervals; 8 mutations — 7 caught (2 by the
         contract, 5 by corpus mismatch) and the eighth, removing the residue
         re-check, provably unobservable because the removal patterns prevent
         what it guards against, so it is exercised instead by simulating the
         pattern-list drift it exists for; ruff + ruff format + mypy --strict
         clean; OpenAPI and the generated TypeScript client regenerated (5 paths,
         20 schemas added, none removed, no name collisions); 33 browser checks
         green across chromium/firefox/webkit with automatic serious + critical
         axe analysis, a 390px overflow audit and a keyboard-reachability sweep.
         The browser fixture is generated from the real service.

         Limitations, stated rather than implied: redaction removes the shapes it
         knows — an email, a UUID, a `learner:`-style reference. A submission
         that names somebody in prose still names them afterwards, which is why
         the queue carries no signal text at all by default and a quote needs a
         human to release it. Clustering is by target content revision and source;
         it is not semantic, so two differently-worded reports of the same problem
         on the same revision cluster together and two different problems on one
         revision also do. The cohort threshold applies to learner-voice sources
         only, because one accessibility failure is one accessibility failure.
         `severity` is a function of the source, not of the content. Nothing here
         submits a draft to a gate or publishes one — the lineage record has the
         fields to track that and this cell only ever writes `not_submitted` /
         `not_published`. NO PRODUCTION FEEDBACK SOURCE IS BOUND TO THIS INTAKE;
         signals arrive in the request. Browser API transport is mocked;
         production deployment, representative author usability, and formal
         accessibility certification are not established.
       -->
- [x] M6.12 Build experiment governance for content or sequencing A/B tests.
      Evidence: hypothesis, metric, guardrail, allocation, stop, and analysis
      records.
  - [x] M6.12.a Define experiment identity/version, hypothesis, exact variants/
        content/algorithm revisions, eligible population, owner, duration, and
        institutional/legal/ethics approval. Evidence: schema.
  - [x] M6.12.b Pre-register primary/secondary metrics, guardrails, estimand,
        power/sample assumptions, analysis method, subgroup/privacy rules, and
        multiple-testing policy. Evidence: analysis plan.
  - [x] M6.12.c Define deterministic privacy-safe assignment, allocation,
        exclusion, ramp, consistency, contamination, consent/opt-out, and
        logging. Evidence: allocation tests.
  - [x] M6.12.d Define start/pause/stop/harm/quality/SLO/integrity thresholds,
        monitoring, authority, and immutable commands. Evidence: guardrail
        tests.
  - [x] M6.12.e Analyze per preregistration with
        attrition/missingness/uncertainty/ limitations, human decision, and no
        automatic rollout. Evidence: analysis records.
       <!-- M6.12 evidence (2026-09-05)
         Built the 6.12.0 experiment-governance contract
         (apps/metis/service/src/metis/schemas/experiment_governance.py), the
         allocation/guardrail/analysis engine
         (services/experiment_governance_engine.py), a 59-case labelled corpus
         over 15 fixture families and five dimensions
         (data/m6_12_experiment_governance_corpus.json, evaluated by
         services/experiment_governance_evaluation.py with Wilson intervals per
         dimension), an append-only tenant-scoped store
         (services/experiment_governance_service.py), instructor and admin
         routes (api/experiment_governance.py, api/admin.py), and the
         /experiment-governance admin workbench.

         M6.12.a — schema. `ExperimentDefinition` requires exactly one control
         arm and an allocation summing to 10,000 basis points; every
         `VariantDefinition` pins a `content_revision_sha256`, so a variant
         cannot name content without naming which revision of it.

         M6.12.b — analysis plan. `PreRegistrationIntake` carries everything a
         plan seals and has NO field for its own revision hash; the service
         derives it from the plan's own content and `PreRegistration` refuses a
         declared seal that does not hash the plan. Before this the hash was a
         caller-supplied number only ever compared to itself, and the earlier
         helper hashed the metrics alone — so alpha, the target sample and the
         multiple-testing policy were all editable inside a "sealed" plan. Eight
         parametrised cases prove each sealed field moves the hash.

         M6.12.c — allocation tests. The ramp decision reads digest[0:8] and the
         arm decision reads digest[8:16]: drawing both from the same bits leaves
         a partial ramp entirely inside one arm (measured: at a 25% ramp every
         assigned subject landed in control), and widening a ramp then reshuffles
         arms. `ramp_consistency` widens 25%→50/75/100% over 800 subjects and
         reports 0 subjects moved. `ExperimentCohort` states how many subjects
         carry each disqualifying condition, because without it four of the five
         `AssignmentExclusion` values were unreachable through the API and the
         branches that refuse them never ran; `assign_one`'s precedence is tested
         directly against subjects carrying two conditions at once. An
         `AssignmentRecord` has no subject-identifier field, only a per-tenant
         salted digest, and the service refuses to allocate at all when
         `experiment_governance_assignment_salt` is unset rather than emitting a
         guessable one. `contamination_free` was REMOVED: with the report's
         digest-uniqueness rule in place no constructible report could ever carry
         two arms for one subject, so the flag was true by construction.

         M6.12.d — guardrail tests. `CommandLog.a_stop_is_final` refuses a resume
         after a stop. A guardrail reads the direction it registered (a drop in a
         higher-is-better guardrail is adverse, a rise is not) and treats its
         tolerance as a limit that may be reached but not passed; both boundaries
         are tested. `AnalysisResult` refuses a `consider_rollout` recommendation
         alongside any breach.

         M6.12.e — analysis records. `ExperimentRunResponse` requires an analysis
         to exist exactly when the command log says stopped, so a preregistered
         test cannot be read mid-flight. `MetricAnalysis` makes a metric
         confirmatory exactly when the plan gave it a role and refuses a
         significant verdict on an exploratory one. `AttritionReport` names the
         metric it counted and reports the worst-observed primary; previously
         `build_run` passed observed == assigned, so the report could only ever
         say zero. `MetricObservation` carries rates and coverage, never counts —
         counts are derived from the arms the allocation actually produced, so a
         metric cannot be reported on more subjects than were enrolled (the
         earlier count-carrying shape let a 1,000-observation request accompany a
         10% ramp that reached 106). `RolloutRecommendation` has no field naming
         a variant to promote and carries `automatic_rollout: Literal[False]`.

         Three defects the adversarial pass found in my own code:

         1. A LOOKUP TABLE CANNOT SERVE A CORRECTION THAT DIVIDES. The critical
            values were a hand-kept dict, and `_effective_alpha` floors — so
            Bonferroni over three tests at 500 basis points asks for alpha 166
            and the table's `167` entry was unreachable. Sixteen further
            reachable alphas had no entry at all, and a plan with six primaries
            would have returned no verdict on anything. `_critical_value` now
            computes from `statistics.NormalDist.inv_cdf` (Wichura AS241 in the
            standard library) and the five ratified constants CHECK that
            computation at import; a test proves every alpha the corrector can
            produce over twenty tests resolves.
         2. THREE REASONS TO TAKE NO VERDICT SHARED ONE EXPLANATION. A missing
            critical value and a zero pooled variance both returned
            `underpowered` with a detail sentence naming a sample-size shortfall
            that had not happened. They are now separate, and the surviving
            `statistic is None` branch's comment was corrected once testing
            showed the empty-arm half of it is shadowed by the sample check.
         3. A TWO-SIDED TEST THAT READS THE SIGN HIDES EVERY HARM. Dropping the
            `abs()` around the statistic survived the suite, because no fixture
            had a treatment that significantly hurt its primary metric. Added
            the `treatment_hurts` scenario: −11.97 points, `significant`,
            `keep_control`.

         Verification: 78 service tests (engine 57, store 10, contract 11) plus
         59/59 corpus cases matched and 8/8 governance probes refused; ruff,
         `ruff format` and `mypy --strict` clean on all five modules.
         Mutation testing applied 27 mutations to the engine and schema across
         three rounds and the suite now kills 26; the one survivor ("read the
         last command instead of folding the sequence") is semantically
         equivalent — every command kind maps to one state — and the docstring
         that claimed otherwise was corrected rather than a test written around
         it. The duplicate-model-name scan added in M6.10 caught a real collision
         (`SyntheticCohort` already belonged to M6.5's learner simulation) before
         it could rename both schemas in the published API; renamed to
         `ExperimentCohort`. OpenAPI regenerated and compared semantically
         against HEAD: +26 schemas, 0 removed, 0 existing schemas changed,
         4 new paths. Browser: 16 Playwright specs green on chromium against a
         fixture generated from the real service (28 runs across 7 scenarios ×
         4 ramps), including a 390px viewport check and keyboard reachability of
         every horizontally scrolling region.
       -->
- [x] M6.13 Build curriculum, item-writing, verification, accessibility, and
      tutoring agent plans with least-privilege tool grants. Evidence: sources,
      course revision, policy, budget, stop conditions, and approval points are
      locked before dispatch.
  - [x] M6.13.a Define versioned agent-plan/step contracts and distinct plan
        templates/output schemas for curriculum, item, verification,
        accessibility, and tutoring work. Evidence: plan schemas.
  - [x] M6.13.b Bind exact source/extraction/claim/course/item/rubric/standard/
        policy/config revisions and purpose-minimized learner context where
        permitted. Evidence: input binding tests.
  - [x] M6.13.c Resolve each tool/model/provider grant by version, typed
        operation, data scope, tenant/course, resource/network limit, answer-key
        visibility, and expiry. Evidence: grant matrix.
  - [x] M6.13.d Define dependencies/preconditions/outputs/evidence, budget per
        step/total, deadlines/retries, hard stop/refusal, and no silent
        fallback. Evidence: execution plan tests.
  - [x] M6.13.e Insert required human review/approval points with role
        separation and unchanged-plan/evidence binding; agents receive no
        approval/publish/ grade/integrity/credential authority. Evidence:
        authority tests.
  - [x] M6.13.f Validate graph/grants/inputs/policy/budget/stops/approvals/
        destinations before dispatch and reject direct plan injection. Evidence:
        zero-dispatch tests.
       <!-- M6.13 evidence (2026-09-05)
         Built the 6.13.0 agent-plan contract
         (apps/metis/service/src/metis/schemas/agent_plan.py), the template
         registry / grant matrix / dispatch engine
         (services/agent_plan_engine.py), a 54-case labelled corpus over 13
         fixture families and six dimensions
         (data/m6_13_agent_plan_corpus.json, evaluated by
         services/agent_plan_evaluation.py with Wilson intervals per dimension),
         an append-only tenant-scoped store (services/agent_plan_service.py),
         instructor and admin routes (api/agent_plan.py, api/admin.py), and the
         /agent-plans admin workbench.

         M6.13.a — plan schemas. `OUTPUT_FOR_PLAN_KIND` is a table, not a field:
         a curriculum plan cannot declare that it emits items. Five ratified
         templates cover the five kinds, and `_validate_templates` refuses at
         import a template whose steps ask for an operation the matrix does not
         permit its kind to hold, whose required inputs no step reads, or whose
         approval role equals its authoring role.

         M6.13.b — input binding tests. `InputBinding` requires a
         `revision_sha256`; `PlanStep` refuses a binding the step holds no grant
         to read, and refuses a learner-context grant with no field named —
         standing permission over every field is not least privilege.
         `LearnerContextField` has no learner-identifier field, so a context
         grant cannot become a way to fetch a named person's record.

         M6.13.c — grant matrix. 75 cells (5 kinds x 15 operations), total by
         construction: `_validate_matrix` fails at import on a gap, because an
         unlisted pair would read as permitted. Only tutoring and accessibility
         may read learner context; tutoring may NOT read item revisions, because
         a tutor that can fetch items can fetch the question a learner is about
         to be asked. Every grant names a tool version, a typed operation, a
         scope, a tenant, a per-operation resource ceiling, a network posture
         and an expiry, and carries `answer_key_visible: Literal[False]`.

         M6.13.d — execution plan tests. `StopCondition.on_trigger` has two
         values, `hard_stop` and `refuse_and_report`; there is no value that
         continues on a smaller model. Every step must state what happens when
         its budget runs out. The steps together may not outspend the plan and
         no step deadline may outrun the plan's. Step order is the topological
         order, so a cycle cannot be expressed.

         M6.13.e — authority tests. `ToolOperation` contains no verb for
         approving, publishing, grading, deciding an integrity appeal, waiving a
         gate, issuing a credential, releasing an answer key, or modifying
         published content. Those eight are listed in `WITHHELD_AUTHORITIES`
         and the two sets are asserted disjoint at import; each is probed by
         actually attempting to construct a grant naming it. `ApprovalPoint`
         blocks rather than notifies, and an approval by the authoring role is
         refused at construction.

         M6.13.f — zero-dispatch tests. `PlanDispatchRequest` has no field for a
         plan, a step, a grant, an approval or a budget, so injection has
         nowhere to land; the service expands the plan from the registry. All 14
         refusal reasons are reachable and each is covered by a test. A refused
         decision carries `plan=None` and `plan_sha256=None`, enforced by
         `DispatchDecision`, so a turned-down request leaves no authorised-
         looking artefact. Every check runs, so a caller fixing one problem does
         not have to dispatch again to find the next.

         Three defects the adversarial pass found in my own code:

         1. A TUTORING DISPATCH WITH NO LEARNER CONTEXT CRASHED. `_expand`
            issued the template's learner-context grant unconditionally and
            `PlanStep` then refused the empty field set, raising out of the
            engine instead of returning a decision. Fixed by granting only what
            the bindings will use — which is also the narrower plan.
         2. AN UNUSED PARAMETER IN A DECISION FUNCTION IS A SILENT STUB.
            `check_request` accepted `tenant_id` and immediately `del`d it,
            telling a reader the tenancy had been checked there. Removed; the
            tenancy is checked in `check_plan` against the grants the expansion
            issued, and in the store against the caller.
         3. ONE CEILING WEARING FIFTEEN HATS. Every grant was built with the
            same `_ceiling()` defaults, so a resource limit bounded the loosest
            operation and said nothing about the other fourteen. Replaced with
            `CEILING_FOR_OPERATION`, validated total at import; the narrowest
            read in the vocabulary (learner context, 8 KiB) is now provably the
            narrowest.

         A fourth was removed rather than fixed: a per-grant matrix re-check in
         the store was unreachable, because `check_plan` already raises
         `grant_exceeds_template_scope` for exactly that case. A defensive
         branch no test can reach reads as coverage it does not give.

         Verification: 95 service tests (engine 74, store 11, contract 11) plus
         54/54 corpus cases matched and 23/23 dispatch probes refused across all
         four refusal mechanisms; ruff, `ruff format` and `mypy --strict` clean
         on all five modules. Mutation testing applied 31 mutations across the
         schema, engine and store and the suite kills all 31. OpenAPI compared
         semantically against the previous contract: +20 schemas, 0 removed, 0
         existing schemas changed, 5 new paths. Browser: 15 Playwright specs
         green on chromium against a fixture generated from the real service
         (9 dispatches, 5 dispatched and 4 refused), including the rendered
         75-cell grant matrix, a 390px viewport check and keyboard reachability
         of every horizontally scrolling region.
       -->
- [x] M6.14 Build Metis-scoped agent run, queue, plan, tool-call, budget,
      evidence, output, and provenance views. Evidence: live traced journey and
      tenant isolation.
  - [x] M6.14.a Define audience-safe projections and queries for request/plan/
        step/queue/run/attempt/tool/budget/evidence/output/decision/lineage.
        Evidence: view contracts.
  - [x] M6.14.b Build run list/detail and plan DAG with state, dependencies,
        approvals/stops, queue/reservations, live cursor events, reconnect, and
        immutable versions. Evidence: run journey.
  - [x] M6.14.c Present tool/version/purpose/status/timing/budget and redacted
        typed inputs/outputs/errors without prompts/keys/learner data. Evidence:
        tool-call view tests.
  - [x] M6.14.d Present allocated/consumed/remaining
        token/tool/compute/cost/time plus evidence/source/citation/gate and
        proposed output lineage. Evidence: budget/evidence tests.
  - [x] M6.14.e Enforce tenant/course/actor/audience/object/property/function
        authorization across URLs/search/streams/exports/actions. Evidence:
        isolation tests.
  - [x] M6.14.f Verify live traced failure/review/accept journey, semantic
        accessibility, responsive modes, and long-history performance. Evidence:
        Playwright E2E.
       <!-- M6.14 evidence (2026-09-05)
         Built the 6.14.0 run-view contract
         (apps/metis/service/src/metis/schemas/agent_run_view.py), the audience
         matrix / redaction / event-chain / budget engine
         (services/agent_run_view_engine.py), a 39-case labelled corpus over 13
         fixture families and six dimensions
         (data/m6_14_agent_run_view_corpus.json, evaluated by
         services/agent_run_view_evaluation.py with Wilson intervals per
         dimension), an append-only tenant-scoped store
         (services/agent_run_view_service.py), instructor routes
         (api/agent_run_view.py), and the /agent-run-views admin workbench.

         M6.14.a — view contracts. AN AUDIENCE IS NOT A RANK. The existing
         runtime's `AgentRoleVisibility` is a four-level ladder used once, for an
         approval gate, so one role always sees a superset of another's. This
         cell's `AUDIENCE_MATRIX` states, for every one of 5 audiences x 12
         objects, exactly which of 5 functions apply; `_validate_matrix` fails at
         import on a gap, and `authorize` subscripts rather than defaulting.
         Five audience pairs are provably incomparable (author and operator each
         hold something the other does not), which is what distinguishes an
         audience from a rank.

         M6.14.b — run journey. The event stream is a hash chain: each event's
         digest covers its own contents including its predecessor's, so an
         inserted, dropped or reordered event breaks the chain where it was
         touched. A reconnecting reader gets `contiguous` AND `broke_at` — a
         boolean says something is wrong, a sequence says where to go and fetch.
         Both are validated against the page they describe, so a page cannot
         claim to be whole. Run list rows carry state and progress, never the
         whole view.

         M6.14.c — tool-call view tests. `RedactedPayload` has no `value`,
         `text`, `prompt` or `body` field. A prompt or an API key is not blanked
         out; there is nowhere for the bytes to sit. Each field is described by
         name, kind, byte size (the JSON encoding, asserted) and a reason drawn
         from a six-value vocabulary, and a field nobody classified defaults to
         withheld — describing an unclassified field is a choice to show it.

         M6.14.d — budget/evidence tests. `BudgetLine.remaining` is checked
         against `allocated - consumed`, and consumption for calls, compute and
         cost is summed from the attempts rather than carried as a counter that
         could drift. A ledger omitting any of the five resources is refused.
         `OutputProposal` carries `published: Literal[False]` and can name only a
         proposal or a review queue, and a view carrying an output whose cited
         evidence it does not also carry is unconstructible.

         M6.14.e — isolation tests. 16 probes across all five surfaces a caller
         actually has (url, search, stream, export, action), all refused; the
         report refuses to publish if any surface is unprobed. Tenancy is
         exercised against the real store: a run recorded under one tenant is not
         found under another, a view cannot be stored under a tenant other than
         the caller's, and a role the audience table does not name is refused
         rather than defaulted to the narrowest view — showing an unknown role
         the least still shows it something nobody decided it should see. The
         request shape has no `audience` field, so a caller cannot choose one.

         M6.14.f — Playwright E2E. 15 specs green on chromium against a fixture
         generated from the real service, walking all five audiences and
         asserting that an author sees outputs but not tool calls while an
         operator sees the reverse, that no prompt, key or learner reference
         reaches the rendered page, and that a viewer who may not stream is still
         told how long the history is. The harness's own axe pass caught a real
         WCAG AA contrast failure (3.48:1 muted text); fixed to 6.8:1 here and in
         M6.13's page, which shares the stylesheet.

         Two defects the adversarial pass found in my own code:

         1. A REPORTED GAP MUST BE THE GAP. `EventPage` said only whether it was
            contiguous, and my own observation computed the break point as
            `from_sequence + len(events)` — 8 for a chain that broke at 5. Added
            `broke_at`, derived and validated, so a wrong break point is
            unconstructible.
         2. A MODEL AND A BUILDER WITH NO CALLER IS A SPEC-ONLY MODULE.
            `RunListItemView` and `list_item` were built and never wired, while
            M6.14.b asks for a run list. Wired as `GET /agent-run-views/runs`,
            with `overspent` sourced from the budget only when the audience may
            read one — so a list row cannot leak an overspend to a viewer with no
            budget access.

         Verification: 76 service tests (engine 53, store 15, contract 11) plus
         39/39 corpus cases matched and 16/16 isolation probes refused across all
         five surfaces; ruff, `ruff format` and `mypy --strict` clean on all four
         modules. Mutation testing applied 34 mutations across the schema, engine
         and store and the suite kills all 34. OpenAPI compared semantically:
         +23 schemas, 0 removed, 0 existing schemas changed, 3 new paths.
         Long-history performance is measured, not asserted: paging the tail of a
         5,000-event chain took ~650 microseconds, taken on the last page because
         a scan from the start is fast at sequence 1.
       -->
- [x] M6.15 Intake agent output as proposals requiring attributed human
      acceptance. Evidence: agents cannot expose answer keys, change grades,
      decide integrity appeals, waive gates, or publish.
  - [x] M6.15.a Define signed/hashed result envelope with agent/run/step/tool/
        model/config versions, exact inputs, output kind/schema, evidence/
        citations, confidence, cost, and status. Evidence: result schema.
  - [x] M6.15.b Validate signature/hash/schema/size/type/expected step/source/
        tenant/course and scan content/files before storage. Evidence: hostile
        result tests.
  - [x] M6.15.c Detect/redact/reject forbidden learner/answer/integrity/grade/
        credential/private-source/tool-secret fields and unsupported claims.
        Evidence: protected-field tests.
  - [x] M6.15.d Store curriculum/item/content/review/accessibility/tutor outputs
        only as attributed proposals linked to exact authoring revisions.
        Evidence: proposal persistence tests.
  - [x] M6.15.e Require an authorized human accept/edit/reject/defer decision;
        acceptance creates a new revision and reruns relevant gates. Evidence:
        review tests.
  - [x] M6.15.f Prove no result field/action can mutate grades, decide appeals,
        waive gates, approve/publish content, or issue credentials. Evidence:
        mass-assignment/authority suite.
       <!-- M6.15 evidence (2026-09-05)
         Built the 6.15.0 result-intake contract
         (apps/metis/service/src/metis/schemas/agent_result_intake.py), the
         signature / protected-content / review engine
         (services/agent_result_intake_engine.py), a 35-case labelled corpus
         over 13 fixture families and six dimensions
         (data/m6_15_agent_result_intake_corpus.json, evaluated by
         services/agent_result_intake_evaluation.py with Wilson intervals per
         dimension), an append-only tenant-scoped store
         (services/agent_result_intake_service.py), instructor routes
         (api/agent_result_intake.py), and the /agent-results admin workbench.

         M6.15.a — result schema. `ResultEnvelopeBody` is its own model and the
         signature is `model_dump()` over the whole of it, so a field added
         later joins the signature automatically; a test asserts the signer
         names no individual field, because naming them is how a field ends up
         outside the signature. Six parametrised cases prove each producer
         version moves the signature. `ResultEnvelope` refuses a declared body
         hash that is not the body's.

         M6.15.b — hostile result tests. The expected step and output kind come
         from the caller's plan, never from the envelope: taking them from the
         result would accept a result for a step nobody dispatched. Every check
         runs, so a producer fixing one problem does not resubmit to find the
         next. An unset signing secret refuses every result rather than
         accepting unverified ones. A second submission of one envelope id is a
         replay, not an update.

         M6.15.c — protected-field tests. `ResultPayload` is a list of named
         fields with a declared kind, never a free-form object, so a payload
         cannot smuggle a nested structure past a shallow scan; `ALLOWED_FIELDS`
         names what each output kind may carry, and anything unlisted is refused
         rather than stored just in case. Eleven patterns cover eight protected
         kinds, each distinct kind reported once per value, and a finding never
         quotes what it matched — a finding that repeated the credential would
         carry the credential into the record meant to prove it was kept out. A
         protected finding is a rejection, not a note.

         M6.15.d — proposal persistence tests. A stored proposal is attributed
         (agent, model, tool, config, all versioned), pinned to the exact
         revisions it cited, and sits in `awaiting_review`. A rejected result
         leaves no proposal, enforced by `IntakeResponse`.

         M6.15.e — review tests. A decision names the human and the exact
         proposal hash they saw; a decision about a proposal that changed since
         is refused. Acceptance produces a `DraftRevision` attributed to the
         accepter and schedules every gate the output kind names, all `pending`
         — acceptance schedules a gate rather than deciding one. A reviewer's
         edit is scanned on the same terms as the agent's output and cannot
         introduce an unlisted field.

         M6.15.f — mass-assignment/authority suite. `FORBIDDEN_FIELD_SUBSTRINGS`
         names fourteen words; `reachable_field_names()` walks all 103 field
         names in the published model tree, nested models included, and
         `_validate_no_forbidden_field_names` refuses at import if one appears.
         The scan admits NO exemption list, because an exemption is exactly the
         hole it closes — two of my own field names (`published_field_names`,
         `replaces_published_revision`) were renamed rather than excused. A
         second import-time guard proves no allowlisted field name contains a
         forbidden substring, so the two defences cannot drift apart. 14 probes
         cover all six named authorities across all four refusal mechanisms
         (absent field, allowlist, scan, state machine), all refused.

         Two defects the adversarial pass found in my own code:

         1. A SECRET DETECTOR THAT STOPS AT THE FIRST SEPARATOR MISSES THE
            COMMONEST SHAPE. `\b(?:sk|pk|api|bearer)[-_][A-Za-z0-9]{8,}` does
            not match `sk-live-abcdef0123456789`, because `live` is four
            characters and the hyphen ends the run — and that is the exact
            string M6.14's own fixture uses. Split into three shapes with
            separators allowed inside the token, and checked against four real
            key formats and three innocuous phrases.
         2. `\b` BEFORE A SLASH NEVER MATCHES AFTER A SPACE. The private-source
            pattern `\b/(?:home|Users|root)/` could not fire in ordinary prose,
            because a space and a slash are both non-word characters. Replaced
            with a lookbehind that also excludes a public URL path.

         Verification: 91 service tests (engine 62, store 15, contract 14) plus
         35/35 corpus cases matched and 14/14 authority probes refused; ruff,
         `ruff format` and `mypy --strict` clean on all four modules. Mutation
         testing applied 43 mutations across the schema, engine and store and
         the suite kills all 43 — including the guards the layer above shadows,
         tested directly rather than removed, because a future allowlist edit is
         exactly what they are for. OpenAPI compared semantically: +20 schemas,
         0 removed, 0 existing schemas changed, 4 new paths. Browser: 10
         Playwright specs green on chromium against a fixture generated from the
         real service, asserting no protected content reaches the rendered page.
       -->
- [x] M6.16 Implement agent pause, redirect, cancel, budget kill, and replay.
      Evidence: failure and race tests preserve immutable run/evidence history.
  - [x] M6.16.a Define separate command eligibility/role/scope/reason/
        idempotency/expected version/outcome contracts. Evidence: command
        schemas.
  - [x] M6.16.b Pause at safe tool/step boundary, checkpoint resumable state,
        stop new calls, and resume only after full revalidation. Evidence: pause
        tests.
  - [x] M6.16.c Redirect through a new plan revision with changed objective/
        inputs/tools/budget and preserved completed history. Evidence: redirect
        lineage tests.
  - [x] M6.16.d Cancel queued/running/downstream work with provider/tool
        propagation, late-result fencing, cleanup, and partial evidence
        retained. Evidence: cancellation race tests.
  - [x] M6.16.e Kill before next spend at hard budget ceiling and reconcile
        nonrefundable in-flight usage without overrun success. Evidence: budget
        kill tests.
  - [x] M6.16.f Replay as new run identity after current source/course/policy/
        grant/budget/rights validation; never mutate source run. Evidence:
        replay tests.
       <!-- M6.16 evidence (2026-09-05)
         Built the 6.16.0 run-control contract
         (apps/metis/service/src/metis/schemas/agent_command.py), the
         eligibility / apply / fence engine (services/agent_command_engine.py),
         a 44-case labelled corpus over 13 fixture families and six dimensions
         (data/m6_16_agent_command_corpus.json, evaluated by
         services/agent_command_evaluation.py with Wilson intervals per
         dimension), an append-only tenant-scoped store
         (services/agent_command_service.py), instructor routes
         (api/agent_command.py), and the /run-control admin workbench.

         M6.16.a — command schemas. Six command types, not one with a `kind`
         field: a pause has no field naming a new plan and a redirect has none
         naming a refund, so a command cannot ask for something its verb does
         not mean. Six routes, one per verb, each accepting only its own body.
         `ELIGIBLE_LIFECYCLES` is total over 6 commands x 7 lifecycles and
         validated at import, which also refuses a command eligible nowhere and
         (except replay) any command eligible against a terminal lifecycle.
         Every command carries `expected_version`, and `CommandRecord` records
         both the version named and the version found — a reader of a stale
         refusal needs to know what the issuer was looking at.

         M6.16.b — pause tests. A pause takes a `Checkpoint` whose completed
         plus pending equals the plan; a checkpoint that lost a step would
         resume into a run that skipped it. It stops *before* a pending step, so
         a run with nothing left is refused with `no_safe_boundary` rather than
         interrupting work in flight. `RunControlState` requires a checkpoint
         exactly while paused, so one cannot outlive the resume that cleared it.
         `ResumeCommand.revalidated` is a `Literal[True]` and the engine refuses
         a resume whose revalidation did not pass.

         M6.16.c — redirect lineage tests. A redirect changes the plan id and
         leaves the completed step ids alone; the store refuses any transition
         that discards completed work, because a reviewer may already have cited
         it. A redirect applies only to a queued or paused run — mid-flight is
         not when you change what a run is for.

         M6.16.d — cancellation race tests. A cancel bumps a fence, and
         `check_fence` refuses a result whose fence is not the current one: a
         tool call that started before a cancel can finish after it, and without
         the fence it looks exactly like a timely result. `CommandRecord` ties
         the fence move to the run ending, so a cancel that did not move it is
         unconstructible. Five race probes issue two commands against one
         version and each resolves to exactly one change; the loser is the
         second writer, not the gentler verb, proved by running the same pair in
         both orders.

         M6.16.e — budget kill tests. A kill below the ceiling is refused —
         that is a cancel wearing a budget's name. `SpendLedger` carries the
         unrecoverable share separately and refuses more unrecoverable than was
         ever committed; the kill records what was already committed to a
         provider rather than writing it off, and the run's lifecycle becomes
         `killed`, never `completed`.

         M6.16.f — replay tests. `ReplayCommand` refuses its own run id and
         requires all six revalidations as distinct values (`min_length=6`
         counts entries; the check counts distinct ones). An applied replay
         moves only `version` and `updated_at` on the source — asserted by
         diffing every field of the state before and after — and
         `CommandRecord` refuses a replay whose lifecycle changed.

         Two defects the adversarial pass found in my own code:

         1. A REFUSAL REBUILT THE STATE FROM PARTS AND DROPPED THE CHECKPOINT.
            A refused pause against an already-paused run returned a state with
            no checkpoint, which then failed its own validator. A refusal now
            returns the original state object.
         2. A LOSER'S `version_before` IS WHAT IT SAW, NOT WHAT IT NAMED. My own
            race message read "named version 5" for a command that named 4.
            Added `expected_version` to `CommandRecord`, with a validator
            requiring the two to agree on anything that applied.

         `command_kinds_in` was written and never called; removed rather than
         left as a helper with no caller.

         Verification: 88 service tests (engine 62, store 18, contract 8) plus
         44/44 corpus cases matched and 5/5 races resolved across five verbs;
         ruff, `ruff format` and `mypy --strict` clean on all four modules.
         Mutation testing applied 44 mutations across the schema, engine and
         store and the suite kills all 44. OpenAPI compared semantically: +19
         schemas, 0 removed, 0 existing schemas changed, 11 new paths. Browser:
         10 Playwright specs green on chromium against a fixture generated from
         the real service, including the rendered 42-cell eligibility matrix.
       -->
- [x] M6.17 Build tutor-configuration and preview workspaces for text, live
      voice, pronunciation, pacing, hint ladders, Socratic guidance,
      demonstrations, accessibility, human escalation, and session summaries.
      Evidence: simulated and real learner contexts are visibly distinct and
      integrity interlocks remain active in every modality.
  - [x] M6.17.a Build versioned persona/pedagogy/language/tone/disclosure and
        exact course/source/objective/config applicability editor. Evidence:
        tutor config tests.
  - [x] M6.17.b Build hint-ladder/Socratic/scaffolding/demonstration/direct-
        answer bounds, triggers, penalties, and assessment-mode rules. Evidence:
        pedagogy editor tests.
  - [x] M6.17.c Build allowlisted tool/source/citation/model/provider/budget/
        retention/safety/escalation editor with effective grant preview.
        Evidence: configuration guard tests.
  - [x] M6.17.d Build text preview with synthetic learner states, transcript,
        grounding/pedagogy/integrity findings, and no real side effects.
        Evidence: text preview tests.
  - [x] M6.17.e Build live voice preview with approved voice/consent, language/
        pronunciation/pacing/interruption/captions/transcript/latency and
        degraded states. Evidence: voice preview tests.
  - [x] M6.17.f Build accessibility modes for keyboard/screen reader/visual/
        hearing/cognitive supports and equivalent source/hint/tool behavior.
        Evidence: accessibility tests.
  - [x] M6.17.g Build human escalation simulation with reason/context summary/
        privacy limits/routing/acknowledgement and no fake handoff. Evidence:
        escalation tests.
  - [x] M6.17.h Build session summary/retention/delete/export settings and show
        exact context sources, redactions, model/config versions, and costs.
        Evidence: session tests.
  - [x] M6.17.i Visually and contractually distinguish synthetic preview from
        authorized real learner context; rerun integrity interlock tests for
        text, voice, tool, demo, accessibility, and cached/offline paths.
        Evidence: modality boundary suite.
       <!-- M6.17 evidence (2026-09-06)
         Built the 6.17.0 tutor-preview contract
         (apps/metis/service/src/metis/schemas/tutor_preview.py), the pedagogy /
         grant / render / boundary engine (services/tutor_preview_engine.py), a
         46-case labelled corpus over 13 fixture families and seven dimensions
         (data/m6_17_tutor_preview_corpus.json, evaluated by
         services/tutor_preview_evaluation.py with Wilson intervals per
         dimension), an append-only tenant-scoped store
         (services/tutor_preview_service.py), instructor routes
         (api/tutor_preview.py), and the /tutor-previews admin workbench.

         M6.17.a — tutor config tests. `Applicability` pins the course, source
         and config revisions; `DisclosureLevel` has two values and both
         disclose, because a tutor that says nothing about itself has no
         configuration this module can express.

         M6.17.b — pedagogy editor tests. `MOVES_IN_MODE` is total over 4 modes
         x 6 moves and validated at import, which also refuses a matrix letting
         an assessment hint or answer, and a mode permitting nothing at all. A
         `HintLadder` must climb: a rung revealing less than the one before, or
         costing less for more, is unconstructible — that is the rung every
         learner takes first. `PedagogyBounds` cannot widen what its mode allows.

         M6.17.c — configuration guard tests. `EffectiveGrant` is checked to be
         exactly the intersection of requested and held, and its `dropped` list
         must name every requested tool the grant withheld — an author who is
         not told goes on believing the tutor has it. `RetentionSettings`
         refuses audio kept longer than the transcript it belongs to.

         M6.17.d — text preview tests. A move the mode forbids is NOT rendered:
         it is reported as a finding, which is what an interlock looks like from
         outside. Turns are numbered from zero without gaps, and `TextPreview`
         carries `produced_no_side_effects: Literal[True]`.

         M6.17.e — voice preview tests. Consent, captions and a transcript are
         `Literal[True]`. The degradation is CHOSEN from the measurement, and a
         latency breach reporting no degradation is unconstructible — a breach
         nobody degraded for is a breach nobody noticed.

         M6.17.f — accessibility tests. Every mode offers the same moves and the
         same sources, compared AS SETS across the six modes rather than each
         mode asserting its own adequacy. A mode whose tool need the grant does
         not meet is reported as a gap, and the response refuses any mode
         offering a tool outside the effective grant.

         M6.17.g — escalation tests. There is no state meaning "handed off"
         without an acknowledgement: `acknowledged` requires both a name and a
         time, and telling the learner a person has it while nobody has is
         unconstructible. All three of learner words, identifier and attempt
         contents are withheld from the summary.

         M6.17.h — session tests. The summary names every context source WITH
         the revision it was read at, the persona and model versions, the
         redactions and the cost, and must count the turns the preview actually
         rendered.

         M6.17.i — modality boundary suite. `SyntheticLearnerState` has no field
         that could hold a real learner and its label must start `synthetic-`;
         every rendered turn is prefixed `[synthetic preview]`, checked in
         `PreviewTurn` rather than left to a renderer; and `ModalityCheck` pairs
         the interlock claim with the move attempted and the mode it was
         attempted in, so a path claiming an active interlock while making a
         permitted move is unconstructible. All six paths (text, voice, tool,
         demonstration, accessibility, cached) are checked, and the practice-mode
         run shows the interlock is NOT simply always on.

         Verification: 88 service tests (engine 69, store 9, contract 10) plus
         46/46 corpus cases matched and 15/15 boundary probes refused across all
         four mechanisms (absent field, marker check, matrix cell, intersection);
         ruff, `ruff format` and `mypy --strict` clean on all four modules.
         Mutation testing applied 44 mutations across the schema, engine and
         store and the suite kills all 44 — including three guards a layer above
         shadows, tested directly because a stored-bytes route or an allowlist
         edit makes each reachable. OpenAPI compared semantically: +26 schemas,
         0 removed, 0 existing schemas changed, 3 new paths. Browser: 11
         Playwright specs green on chromium against a fixture generated from the
         real service, including the rendered 24-cell pedagogy matrix and an
         assertion that no learner reference reaches the page.
       -->
- [x] M6.18 Build credential pathway authoring that links objectives,
      assessments, evidence, mastery/grade rules, human approvals, issuer
      policy, expiry, and revocation. Evidence: course completion alone cannot
      mint a credential unless the locked criteria and authority are satisfied.
  - [x] M6.18.a Build versioned credential pathway/template selection, issuer/
        authority, audience, achievement/standards, lifecycle, locale, and
        validity editor. Evidence: pathway schema/UI tests.
  - [x] M6.18.b Author nested
        objective/course/assessment/evidence/mastery/grade/ experience/time
        requirements with exact revisions and deterministic evaluation
        semantics. Evidence: criteria tests.
  - [x] M6.18.c Configure acceptable evidence types/sources/verification/
        freshness/privacy and required human review/approval/quorum. Evidence:
        evidence policy tests.
  - [x] M6.18.d Preview current/synthetic subject eligibility per criterion with
        missing/stale/conflicting/N/A state and no exposed private detail.
        Evidence: evaluation tests.
  - [x] M6.18.e Lock criteria/issuer/policy/evidence/subject projection on
        issuance request and require authorized issuer decision/signing.
        Evidence: issuance guard tests.
  - [x] M6.18.f Prove course completion or author action alone cannot mint,
        backdate, replace, revoke, or verify credentials. Evidence: authority
        bypass suite.
  - [x] M6.18.g Define expiry/renewal/revoke/replacement and propagate status
        without altering underlying learning evidence. Evidence: lifecycle
        tests.
       <!-- M6.18 evidence (2026-09-06)
         Built the 6.18.0 credential-pathway contract
         (apps/metis/service/src/metis/schemas/credential_pathway.py), the
         criteria / eligibility / issuance / lifecycle engine
         (services/credential_pathway_engine.py), a 44-case labelled corpus over
         13 fixture families and seven dimensions
         (data/m6_18_credential_pathway_corpus.json, evaluated by
         services/credential_pathway_evaluation.py with Wilson intervals per
         dimension), an append-only tenant-scoped store
         (services/credential_pathway_service.py), registrar routes
         (api/credential_pathway.py), and the /credential-pathways admin
         workbench.

         M6.18.a — pathway schema/UI tests. `CredentialPathway` is versioned
         (semver `pathway_version`), selects one of four credential kinds, and
         carries the issuer authority, audience, standard ids, locale, groups
         and validity days. `completion_alone_is_sufficient` is a
         `Literal[False]`: there is no pathway on which the course finishing is
         the award, and constructing one raises. `IssuerAuthority` requires a
         non-empty `signing_roles`, so an issuer nobody may sign for does not
         exist. UI: 12 Playwright specs green on chromium, firefox and webkit,
         including one that reaches the workbench by following the sidebar link
         rather than by navigating straight to its URL.

         M6.18.b — criteria tests. Six criterion kinds cover objective and
         mastery (`objective_mastery`), course (`course_completion`), assessment
         and grade (`assessment_score`), evidence (`evidence_artefact`),
         experience (`supervised_experience`) and time (`elapsed_time`).
         `subject_revision_sha256` is REQUIRED: a criterion naming a course
         without naming which revision would be satisfied by a course that had
         since changed into something else. A criterion that states no bar is
         unconstructible — a threshold kind without a threshold and an hours
         kind without hours both raise. Groups nest one level with `all_of`,
         `any_of` and `at_least_n`; evaluation is pure, so two evaluations of
         the same inputs give the same verdicts.

         M6.18.c — evidence policy tests. `EVIDENCE_FOR_CRITERION` is total over
         6 criterion kinds x 5 evidence types = 30 cells, 7 accepted, validated
         at import: a gap stops the service starting rather than admitting an
         attendance record as proof of mastery. `verification_required` is a
         `Literal[True]`, freshness has a policy default and a per-criterion
         override, `withheld_from_projection` must name all three of free-text
         answers, identifier and demographics, and `review_quorum` may not
         exceed the reviewer roles that could meet it.

         M6.18.d — evaluation tests. Six criterion states, each reachable and
         each distinguished by a fixture: `missing` is not `not_met`, because
         reporting evidence nobody could find as a shortfall tells an issuer the
         subject failed when nothing was checked. `SubjectProjection` carries a
         salted digest and no identifier, and `EligibilityPreview` evaluates a
         current OR synthetic subject and records nothing. FIXED: `is_synthetic`
         was a declared field nothing read — it now refuses issuance, because a
         credential minted for a made-up subject would attest to learning nobody
         did. A preview of a synthetic subject that did not say so is
         unconstructible.

         M6.18.e — issuance guard tests. `IssuanceLock` hashes the criteria, the
         issuer, the policy, the projection and the eligibility outcome, and
         every field in it is covered by its own hash. FIXED: the lock folded in
         the moment it was taken, so no two parties could ever compute the same
         string and the approve-then-issue flow was impossible; the evaluation
         instant is now excluded from both the lock and the eligibility digest,
         while a change in the substance — evidence going stale — still moves
         the lock. The decision is HMAC-signed over the lock (32-character
         minimum, `SigningNotConfiguredError` and a 503 when unset), and nine
         refusal reasons are each reachable and separately evidenced. FIXED:
         approvals were a field on the request, so one caller could invent a
         quorum; they are now recorded acts by the approving party against one
         exact lock, and the quorum counts distinct people.

         M6.18.f — authority bypass suite. 21 probes, all refused, spanning all
         five verbs (mint, backdate, replace, revoke, verify), all three actors
         (course completion, author action, unauthorized role) and all four
         mechanisms (absent field, lock binding, state machine, role check). The
         request shape has no field for a status, an issue date or a credential
         id, so a caller cannot mint by asking.

         M6.18.g — lifecycle tests. `STATUS_AFTER` is total over 4 actions x 4
         statuses = 16 cells, 7 allowed; an action with nowhere to go refuses
         rather than proceeding. A `LifecycleEvent` carries no criterion, no
         evidence and no subject, and `alters_learning_evidence` is a
         `Literal[False]`. FIXED: `apply_lifecycle` copied the record, leaving
         its digest covering the old status, so every legitimately revoked
         credential verified as tampered; it now re-seals. A renewal must state
         an expiry later than the one it replaces, or it is not a renewal.

         Verification: 66 service tests (engine 38, store 16, contract 12) plus
         44/44 corpus cases matched and 21/21 authority probes refused; ruff,
         `ruff format` and `mypy --strict` clean on all five modules. Mutation
         testing applied 49 mutations across the schema, engine and store and
         the suite kills all 49. The store's own re-checks of contract
         invariants were DELETED rather than left unkillable: two places
         computing a lock is two places that can drift, so the engine exports
         `lock_for` and the store calls it. OpenAPI compared semantically: +27
         schemas, 0 removed, 0 existing schemas changed, 9 new paths. Browser:
         12 Playwright specs green on chromium, firefox and webkit against a
         fixture generated from the real service — the rendered 30-cell evidence
         matrix checked cell by cell against the service, every verb's refusal
         count, the sidebar link followed rather than the URL typed, and an
         assertion that no subject identity reaches the page; axe WCAG 2.1 AA
         runs after every one of the 36 test executions with zero serious or
         critical violations.
       -->
- [x] M6.19 Build governed training-data/evaluation feedback intake from
      educator decisions, aggregated learner outcomes, verifier disagreements,
      and media critiques. Evidence: consent, minimization, purpose, retention,
      contamination, licensing, deletion, lineage, and opt-out controls precede
      any training or fine-tuning use.
  - [x] M6.19.a Define feedback-source registry and intake record for educator
        decisions, cohort-level outcomes, verifier disagreements, and media
        critiques with exact originating versions. Evidence: schemas.
  - [x] M6.19.b Verify collection notice/consent or approved basis, creator/
        learner/institution rights/license, purpose scope, tenant policy, and
        opt-out before admission. Evidence: eligibility tests.
  - [x] M6.19.c Minimize/redact/deidentify protected answer, learner, grade,
        integrity, biometric/voice, source, tenant, and free-text PII with
        privacy thresholds. Evidence: minimization tests.
  - [x] M6.19.d Filter quality/label validity, duplication, leakage, benchmark/
        evaluation contamination, malicious/poisoned content, and subgroup
        representation. Evidence: quality/contamination tests.
  - [x] M6.19.e Store immutable dataset candidate/record lineage, license/
        purpose/retention/delete/opt-out state and prohibit direct training use.
        Evidence: dataset ledger tests.
  - [x] M6.19.f Require separate dataset curation, governance approval,
        versioned manifest/splits, security/privacy/fairness review before
        evaluation or training. Evidence: promotion gate.
  - [x] M6.19.g Propagate deletion/withdrawal/revocation through datasets,
        experiments, derived models/evaluations and record irrecoverable
        constraints. Evidence: lineage/withdrawal tests.
       <!-- M6.19 evidence (2026-09-05)
         apps/metis/service/src/metis/{schemas/training_intake.py,
         services/training_intake_{engine,service,evaluation}.py,
         api/training_intake.py, data/m6_19_training_intake_corpus.json};
         admin /training-intake workbench. 72 service tests (engine 48, store
         13, contract 11), 49/49 corpus cases, 26/26 bypass probes refused over
         5 verbs x 3 actors x 4 mechanisms, 13 Playwright specs on chromium
         with axe clean; OpenAPI +35 schemas, +7 paths, 0 changed. Nothing
         that arrives is trainable: DatasetCandidate.trainable is
         Literal[False]; only a DatasetRelease (separate curator, quorum of 2,
         three cleared reviews, disjoint manifest) says True. A cohort outcome
         withholds >=2 buckets with a residual >= the reportable minimum, so
         no cell is recoverable by subtraction. Promotion re-resolves the
         lineage against withdrawals on file. Defects found and fixed: the
         minimisation matrix claimed `hashed` for source/tenant fields the
         payload does not carry; the candidate digest folded in the assembly
         instant (same class as M6.18's lock); one unreachable store re-check
         deleted; four unread vocabulary constants deleted.
       -->

### M7 — Integrity, review, and institutional governance

- [x] M7.1 Integrate Themis adjudication through its canonical contract only.
      Evidence: no private Themis imports.
  - [x] M7.1.a Inventory Themis public contracts/versions/operations/events/
        auth/failure modes and all current Metis integration/private imports.
        Evidence: integration inventory.
        <!-- metis-themis-integration.json: Metis↔Themis integration/boundary
                                                                                                                                                                                                                                                                        inventory. Themis exposes 71 public @themis/* packages; Metis integrates
                                                                                                                                                                                                                                                                        EXACTLY 3 (@themis/arbitration, @themis/justice, @themis/policy) through a
                                                                                                                                                                                                                                                                        SINGLE canonical wiring module (libs/metis/integrations/src/themis-canonical-
                                                                                                                                                                                                                                                                        wiring.ts): 16 typed MetisThemis* contracts (Adjudication/Appeal/AuditRecord/
                                                                                                                                                                                                                                                                        EnforcementAction...) + 2 enforcement consts (METIS_THEMIS_POLICY_DOMAIN,
                                                                                                                                                                                                                                                                        METIS_THEMIS_ENFORCEMENT_ACTION). BOUNDARY FINDING: 3 public bare-package
                                                                                                                                                                                                                                                                        imports, 0 public subpath imports, 0 PRIVATE/deep imports (@themis/x/src|dist|
                                                                                                                                                                                                                                                                        internal) — the boundary is CLEAN (Metis reaches no Themis internals; all
                                                                                                                                                                                                                                                                        integration flows through the sanctioned wiring). generate-metis-themis-
                                                                                                                                                                                                                                                                        integration.mjs + 6 tests, wired into inventory/verify + CI. M7.1.b+
                                                                                                                                                                                                                                                                        (Themis version/failure-mode contracts) are design and stay unchecked. -->
  - [x] M7.1.b Map Metis case/evidence/subject/policy/decision/appeal fields to
        the public contract with protected-field and authority boundaries.
        Evidence: mapping tests.
  - [x] M7.1.c Implement an injected adapter/client with version negotiation,
        idempotency, timeouts, redaction, audit, and unavailable/degraded state.
        Evidence: contract tests.
  - [x] M7.1.d Remove/forbid private package/schema/store imports and duplicate
        adjudication mutation paths. Evidence: dependency-boundary gate.
  - [x] M7.1.e Verify case submit/status/decision/appeal/revocation journey and
        cross-tenant/role failures. Evidence: integration E2E.
       <!-- M7.1.b–e evidence (2026-09-05)
         libs/metis/integrations/src/themis-contract-mapping.ts (b),
         themis-adjudication-adapter.ts (c), scripts/v1-workbenches/
         themis-boundary-gate.{mjs,test.mjs} (d, wired into
         verify:inventory:v1-workbenches), themis-contract-mapping.spec.ts +
         themis-adjudication-journey.spec.ts (b/c/e). 21 specs green plus the 9
         pre-existing wiring tests; gate test 5/5 and the gate is clean over
         1,296 Metis-boundary files. Protected fields: a party is a salted
         digest and mapEvidence never reads excerpt text; mapped payloads
         validate against Themis's own zod inputs. Authority: a 15-cell
         role x verb matrix, total at module load. Adapter: version
         negotiation (0.1.x), idempotency by key + payload digest, injected-
         clock timeouts -> degraded, transport throw -> unavailable (never a
         fabricated case), an audit entry per call with reachedTransport. The
         journey walks submit -> status -> decision -> appeal -> revocation
         against the real in-process DisputeManagementSystem; cross-tenant and
         role refusals never reach the transport. Findings: Themis has no
         revocation state, so revocation is `closed` + a structured note and
         the mapping names it lossy; Themis exports "./*" so the gate forbids
         EVERY subpath import, not just /src|dist/; the gate's first run
         flagged apps/oshun/bff/src/themis/moderation-appeal-store.ts, which is
         the BFF's own Themis product surface, not Metis — scan narrowed to
         Metis code + Metis-specific BFF paths and the reason is stated in the
         gate; Themis enforces no status transitions, so Metis does (filed ->
         adjudicated allowed, as the existing bridge already does).
       -->
- [x] M7.2 Reconcile assessment and tutor integrity-mode vocabularies through an
      explicit lossless mapping. Evidence: round-trip tests.
  - [x] M7.2.a Inventory every assessment/tutor/delivery/institution integrity
        mode code, definition, version, owner, behavior, and consumer. Evidence:
        vocabulary inventory.
  - [x] M7.2.b Define canonical mode identities separately from context-specific
        projections and map exact/broader/narrower/composite/unmapped semantics.
        Evidence: mapping table.
  - [x] M7.2.c Represent untranslatable fields/constraints explicitly and reject
        lossy defaults or “closest mode” execution. Evidence: loss tests.
  - [x] M7.2.d Round-trip each supported mode/version through assessment
        context, tutor config/session, API/events, and UI labels. Evidence:
        goldens.
  - [x] M7.2.e Version/deprecate mappings with compatibility windows and
        preserve historical session meaning. Evidence: lifecycle tests.
       <!-- M7.2 evidence (2026-09-05)
         apps/metis/service/src/metis/schemas/integrity_mode_registry.py (a, b,
         e), services/integrity_mode_{engine,goldens}.py (c, d), api/
         integrity_mode.py (3 routes), data/m7_2_integrity_mode_goldens.json,
         tests/test_integrity_mode_registry.py (17) plus 35 neighbouring tests
         green; OpenAPI +7 schemas, +3 paths, 0 changed. The inventory found
         SEVEN mode vocabularies on FOUR axes (stakes, posture, stance, style):
         `practice` is a stakes level in four of them and a tutor stance in a
         fifth; `proctored` is a stakes level in the interlock and a posture in
         the item pool. Canonical identities live per axis, separate from every
         projection; the 37-projection table is total at import and every
         projection states exact/broader/narrower/composite/unmapped with its
         loss named (Themis-style `review` and `collaborative` are unmapped and
         say why; interlock `proctored` is composite over stakes+posture). The
         reverse direction refuses with candidates rather than choosing a
         closest mode. Loss tests fixed three production paths that defaulted:
         normalize_integrity_mode (unknown -> teach), derive_integrity_mode_
         from_tutor_style (everything -> teach), _build_policy_binding (unknown
         assessment_type -> assessed), plus the web label fallback ('Teach');
         seven lossy defaults are recorded against their vocabularies. Goldens:
         37 round trips (27 lossless, 10 with the loss named) and 85 context
         expressions (31 named unexpressible — e.g. posture has no assessment-
         context vocabulary) generated from the registry and drift-checked.
         Lifecycle: the stance code `practice` is deprecated to practice_coach
         under a 7.2.0..7.4.0 window; a session recorded under 7.2.0 resolves
         to practice_coach while the current mapping is inside the window and
         is refused with the window named once it moves past. Finding: the
         registry's own import check caught my first table (style:direct had
         no exact code) — a canonical nobody can say is a word, not a mode.
       -->
- [x] M7.3 Build integrity signal review with model version, evidence,
      confidence, uncertainty, and protected-data controls. Evidence: reviewer
      journey.
  - [x] M7.3.a Define signal/case projection with exact learner/attempt/item
        refs, source/collector/model/rule/config versions, raw outputs,
        evidence/chain, confidence/uncertainty, and state. Evidence: schema.
  - [x] M7.3.b Build permission-filtered queue/detail with priority, deadlines,
        duplicate/correlation, evidence freshness, and explicit classifier-not-
        conclusion language. Evidence: review UI tests.
  - [x] M7.3.c Render timeline/media/session/device/similarity/authorship
        evidence through field-level redaction, secure viewer, and access audit.
        Evidence: protected-data tests.
  - [x] M7.3.d Support request-evidence/dismiss/escalate/open-case actions with
        reason/expected version and no automatic grade/consequence. Evidence:
        action tests.
  - [x] M7.3.e Verify reviewer journey for positive/false-positive/conflicting/
        insufficient/appealed/cross-tenant cases accessibly. Evidence: E2E.
       <!-- M7.3 evidence (2026-09-05)
         apps/metis/service/src/metis/{schemas/integrity_signal_review.py,
         services/integrity_signal_review_{engine,evaluation,service}.py,
         api/integrity_signal_review.py} (7 routes); admin /integrity-signals
         workbench + e2e. 17 tests in tests/test_integrity_signal_review.py
         plus both duplicate-model-name guards green; 9 Playwright specs on
         chromium with axe clean; OpenAPI +21 schemas, +7 paths, 0 changed.
         (a) the projection pins learner (salted digest), attempt and item to
         exact revisions and carries collector/model/rule/config versions, raw
         classifier outputs, a confidence with interval + basis (a calibrated
         basis must name its sample), a hash-chained evidence trail, and state;
         `is_conclusion` is Literal[False] and the only language is
         `classifier_indicates` — a summary containing verdict words is
         unconstructible. (b) the queue derives rank (severity, then narrower
         interval, then deadline), deadline by severity, overdue, duplicates
         (same attempt revision+kind+source+config; earliest is original),
         correlation, and freshness; it carries the digest and no field for a
         person; filtered by tenant and, for a learner, to their own. (c) a
         total 15x3 field-group x audience visibility matrix; learner-facing
         excerpt text is COMPUTED by running the M6.11 redactor plus engine-
         owned residue detectors (phone, handle) and withheld on residue — a
         FieldDecision with residue and any treatment but withheld is
         unconstructible; every view appends an access-audit entry naming the
         fields shown and the view digest. (d) four verbs over a total 4x6
         table, `expected_version` refused when stale, the original projection
         returned on refusal, and no field on the action or its record for a
         grade, score change or consequence. (e) six journeys walked against
         the engine; cross-tenant and learner action refused before the
         transport. Findings: `redactionState` on excerpts was set to
         learner_safe by its only writer while copying raw answer text, and
         the learner-facing excerpt response returns `text` regardless of it —
         labelled, never enforced; my own field `no_automatic_consequence`
         tripped my consequence-word scan (renamed carries_effect);
         `QueueItem` collided with M6.11's (renamed SignalQueueItem — the
         duplicate-name guard scans the schema dir at test time and caught it
         mid full-suite run).
       -->
- [x] M7.4 Build decision and appeal workflows with separation of duties.
      Evidence: original reviewer cannot decide a restricted appeal.
      <!-- Verified 2026-09-05: schemas/appeal_workflow.py 7.4.0 +
         services/appeal_workflow_{engine,evaluation}.py + api/appeal_workflow.py
         (GET /appeal-workflows/evaluation); tests/test_appeal_workflow.py 22
         tests + 2 duplicate-name guards = 24 passed; mypy --strict clean on 4
         files; OpenAPI +1 path, +4 schemas, 0 changed. (a) 6 case and 7 appeal
         states over transition tables validated at import; 7 roles; a total
         7x4 evidence-rights matrix; deadlines by standard; a stay extends the
         deadline by exactly its duration; every state change writes an audit
         entry (actor, role, versions) through one `_move`. (b) eight refusal
         reasons, each reached by its own input; a decision names all six
         guards passed. (c) only the learner, or a representative holding the
         learner's unexpired grant, files; the new-evidence ground carries a
         chained link; accessibility support opens a stay; the status view has
         no field for raw outputs. (d) MAY_DECIDE_APPEAL 2x7: restricted refuses
         the original detector owner, reviewer and decider (the original
         reviewer cannot decide a restricted appeal even holding the
         appeal_decider role); the original decider never may; a restricted
         quorum is 2 DISTINCT independent signatories. (e) six outcomes each
         recorded from its state with a CorrectionTrigger as a SEPARATE record
         (performed is Literal[False], required_role integrity_lead); the BFF
         projection is lossy for 4 of 6 and says so. Findings: BFF
         `decideAppeal` has no separation-of-duties guard; my generic
         `transition_case` could enter decided/appealed/stayed with no
         decision, filing or stay record (0 callers — the caller scan caught
         it; now UNGUARDED_TARGETS = evidence_complete, closed and it refuses
         to leave a stay); the outcome digest was first computed over a
         hand-built dict (now AppealOutcomeBody.seal()); DecisionRequest/
         DecisionRecord collided with M6.11 (renamed Case*).
       -->
  - [x] M7.4.a Define decision/appeal state machines, roles, evidence rights,
        standards/policies, deadlines, stays, outcomes, remedies, and audit.
        Evidence: workflow schemas.
  - [x] M7.4.b Require eligible independent decision maker, conflict check,
        evidence completeness, learner explanation, and expected case version.
        Evidence: decision guards.
  - [x] M7.4.c Let learner/authorized representative file appeal, grounds, new
        evidence, accessibility support, and receive status safely. Evidence:
        appeal intake tests.
  - [x] M7.4.d Enforce restricted appeal independence/quorum so original
        detector/reviewer/decider cannot decide where prohibited. Evidence:
        separation tests.
  - [x] M7.4.e Record appeal affirm/modify/overturn/remand/withdraw/expire and
        trigger grade/consequence correction separately. Evidence: outcome
        tests.
- [x] M7.5 Build immutable integrity audit and learner-visible explanation
      projection. Evidence: role-based redaction tests.
      <!-- Verified 2026-09-05: schemas/integrity_audit.py 7.5.0 +
         services/integrity_audit_{engine,service,evaluation}.py +
         api/integrity_audit.py (11 routes under /integrity-audit);
         tests/test_integrity_audit.py 25 tests + 2 duplicate-name guards = 27
         passed; mypy --strict clean on 6 files; OpenAPI +11 paths, +44
         schemas, 0 changed. (a) ten event kinds with actor, authority basis
         (total over roles; a grant or authorization must name its record),
         subject versions (non-mutating kinds leave the version) and a
         per-subject hash chain whose event digest covers the PAYLOAD'S DIGEST;
         a case chain's opening link names a link the signal chain contains;
         four faults each found at its link (gap, relink, cross-subject,
         on-disk tamper). (b) a total 8-section x 6-audience policy: raw
         evidence, classifier features, other learners, staff identity and
         security method are withheld from learner and representative, masked
         (count/roles/flag) for reviewers, and shown to forensic access only
         under a stored authorization naming examiner, subject and expiry; a
         withheld section is empty AND named; the actor digest is absent when
         staff identity is withheld. (c) seven ordered sections (allegation,
         policy, evidence+limitations, decision, consequence, appeal rights +
         deadlines, support), each with its provenance, plus a locale and
         accessibility profile. (d) the projector takes the case, its chain,
         the decision, appeal and outcome — there is no parameter for a signal
         projection; the catalogue is total over locales x keys, pre-decision
         keys are scanned for accusatory stems at import, an unsupported locale
         is refused with the supported list, and the same records give the
         same view digest under a different clock. (e) tamper ->
         digest_mismatch at the link; a legal hold refuses the whole purge and
         a release is its own record; a purge drops payloads only and is itself
         a link, the chain verifies after; the export is sealed and carries the
         correction history; cross-tenant and other-learner reads refused;
         every read appends an access link. Findings: the opening link first
         named the signal chain's MOVING head; learner digest, signal id and
         the opening link began in the purgeable payload (moved to the header);
         RedactedPayload collided with M6.14; PLACEHOLDERS as an identifier
         would have tripped the stub scan (TEMPLATE_FIELDS).
       -->
  - [x] M7.5.a Record append-only collection/access/change/signal/case/evidence/
        decision/appeal/notification/correction events with actor/authority/
        versions/hash chain. Evidence: audit schema tests.
  - [x] M7.5.b Protect raw evidence/classifier features/other learners/reviewer
        identity/security methods while retaining authorized forensic access.
        Evidence: field policy.
  - [x] M7.5.c Define learner-visible explanation for allegation/category,
        applicable policy, evidence summary/limitations, decision/reason,
        consequence, appeal rights/deadlines, and support. Evidence: projection
        schema.
  - [x] M7.5.d Generate explanations from canonical decisions, not model output,
        with locale/accessibility and no accusation before decision. Evidence:
        projection tests.
  - [x] M7.5.e Verify tamper detection, retention/legal hold, role/tenant
        redaction, export, and correction history. Evidence: audit tests.
- [x] M7.6 Build content review templates for SME, pedagogy, accessibility,
      fairness, legal/rights, and publisher signoff. Evidence: required-role
      matrix.
      <!-- Verified 2026-09-05: schemas/content_review_template.py 7.6.0 +
         services/content_review_template_{engine,evaluation}.py +
         api/content_review_template.py (5 routes under
         /content-review-templates); tests/test_content_review_template.py 20
         tests + 2 duplicate-name guards = 22 passed; mypy --strict clean on 4
         files; OpenAPI +5 paths, +30 schemas, 0 changed. (a) versioned
         templates applying by content kind, risk, locale and delivery target;
         each of the six dimensions carries criteria, evidence kind, roles,
         qualifications, quorum, separation, sequence, expiry and gate floors;
         two platform templates cover every (kind, risk) exactly once (checked
         at import); the sequence is validated acyclic and the publisher signs
         after every other dimension; the template is sealed by a digest.
         (b) 18 criteria over 6 dimensions, each code prefixed by its
         dimension, so a criterion cannot be lent: a DimensionDecision citing
         another dimension's criterion, and a signoff carrying another
         dimension's decision, are both unconstructible. (c) the required-role
         matrix is the only way into a dimension — every role x every dimension
         walked; author, wrong locale, missing qualification, a signer of an
         excluded dimension and a repeat signature are each refused by their
         own reason; a quorum counts DISTINCT people (one person signing twice
         leaves the dimension pending); all seven signoff refusal reasons are
         reached by their own input. (d) a signoff binds content, revision,
         evidence, the gate-results digest and the template version; a change
         invalidates only the dimensions CHANGE_AFFECTS names (total over 8
         change kinds; every kind reaches the publisher) and carries the rest
         forward as CarriedSignoff records naming the original, both revisions
         and the change kinds — a carried record for an affected dimension is
         unconstructible; expiry and a superseded template invalidate too.
         (e) 400 generated overlays applied to a platform template: no overlay
         the type can express lowered a quorum, dropped a criterion or a
         qualification, relaxed a locale/separation/sequence, lengthened an
         expiry or lowered a gate floor; OverlayAddition has no removal field,
         and six weakened EffectiveTemplates were each refused by its
         validator. Findings: an overlay CAN express a longer expiry, which the
         type cannot forbid — apply_overlay refuses it (the property probe
         treats a refusal as not-a-weakening); the platform's institution
         reviewer role is deliberately absent from the required-role matrix so
         it can only ever be an addition.
       -->
  - [x] M7.6.a Define versioned review template applicability by content/item/
        course/risk/locale/target with dimension, checklist, evidence, role,
        quorum, sequence, expiry, and code floors. Evidence: schema.
  - [x] M7.6.b Define SME correctness/source, pedagogy/alignment, accessibility,
        fairness/cultural, legal/rights, and publisher/target criteria as
        separate decisions. Evidence: dimension matrices.
  - [x] M7.6.c Resolve required roles/qualifications/locale/independence and
        prohibit one signoff from satisfying another dimension. Evidence: role
        tests.
  - [x] M7.6.d Bind signoffs to exact revisions/evidence/gates/template and
        invalidate only affected approvals on change. Evidence: freshness tests.
  - [x] M7.6.e Test institution overlays can add reviewers/criteria but not
        remove platform requirements. Evidence: template property tests.
- [x] M7.7 Build anchored comments on course, lesson, block, objective, rubric,
      item, response, stimulus, and media. Evidence: anchor survival tests.
      <!-- Verified 2026-09-05: schemas/anchored_comment.py 7.7.0 +
         services/anchored_comment_{engine,service,evaluation}.py +
         api/anchored_comment.py (11 routes under /anchored-comments) + admin
         /comments + e2e; tests/test_anchored_comment.py 28 tests + 2
         duplicate-name guards = 30 passed; 5 chromium e2e specs passed (axe
         after each, incl. 390px with no page-level horizontal scroll); admin
         vitest 1 passed; mypy --strict clean on 5 files; OpenAPI +11 paths,
         +35 schemas, 0 changed. (a) 9 subject kinds x 7 anchor kinds as a
         total applicability table; a discriminated anchor union (entity,
         field, text with quote+context+offset, region, timecode, item option,
         rubric cell); a thread names the exact subject revision, is a tree
         whose replies must name a comment in it, and carries visibility,
         mentions bounded by the audience, typed attachments and a status.
         (b) six verbs over a total 6x2 table with expected_version, an edit
         only by its author that keeps the digest of what it replaced, and a
         notification plan where a mute removes the row rather than silencing
         it; every read gives a position and an anchor DESCRIPTION for a reader
         who cannot see the highlight; all 11 refusal reasons reached by their
         own input. (c) each anchor kind driven through every status its kind
         can reach (18 resolutions): entity/region/timecode/item option are
         exact-or-orphaned, field also shifts by rename, rubric cell also goes
         ambiguous, text reaches all four; the resolution always carries the
         ORIGINAL and an unresolved anchor keeps pointing at it while asking a
         person. (d) a scan of all 27 comment types' 148 fields against 11
         mutation words matched nothing; excerpting a learner's response, live
         assessment material or a private source is refused by a policy over
         subject x anchor, a response is never discussed above the reviewers,
         and a property the reader cannot read cannot be anchored to. Findings:
         the offset-first text rule matched a DIFFERENT run of the same words
         at the old offset (context now outranks the offset); an edit built an
         unvalidated dict into a validated field; the store hashed a clock it
         serialised differently on the way back in; an inapplicable anchor 500d
         through the model validator (now a refusal); learner_response_excerpt
         was unreachable because the applicability table excluded text on a
         response — a protection that cannot fire is decoration, so the table
         now admits it and the validator asserts the pair exists.
       -->
  - [x] M7.7.a Define comment/thread/reply, exact subject revision, typed
        entity/ field/text/region/timecode/item-option/rubric anchor,
        visibility, mentions, attachments, and status. Evidence: schema.
  - [x] M7.7.b Implement accessible create/reply/edit/resolve/reopen/navigate
        with object/property permissions and notification controls. Evidence:
        UI/API tests.
  - [x] M7.7.c Map stable entity anchors across revisions and text/media anchors
        as exact/shifted/ambiguous/orphaned while preserving originals.
        Evidence: survival tests.
  - [x] M7.7.d Prevent comments from changing content/answer/grade/approval and
        protect learner/assessment/private-source anchors. Evidence: boundary
        tests.
- [x] M7.8 Build reviewer conflict-of-interest declarations. Evidence:
      incompatible conflicts block signoff.
      <!-- Verified 2026-09-05: schemas/reviewer_conflict.py 7.8.0 +
         services/reviewer_conflict_{engine,evaluation}.py +
         api/reviewer_conflict.py (6 routes under /reviewer-conflicts);
         tests/test_reviewer_conflict.py 27 tests + 4 duplicate-name guards +
         the OpenAPI contract = 73 passed; mypy --strict clean on 4 files;
         OpenAPI +6 paths, +29 schemas, 0 changed, 0 removed. (a) 8 conflict
         types x 4 scopes x 7 relationship bases, self-declared or raised by a
         detector whose signal is_conclusion=False and speaks as
         "detector_indicates"; a NIL RETURN is itself a declaration, so silence
         and "nothing to declare" are different states; 7 statuses over a total
         transition table with terminal dismissed/withdrawn. (b) an assignment
         with no current declaration is refused, not assumed clean: four
         coverage gaps (missing, stale, does-not-cover, unsure-unresolved) each
         map to their own refusal; freshness tightens with risk (180/180/90/30
         days); an unsure reviewer is REFERRED and cannot answer their own
         referral, and an unanswered referral keeps blocking. (c) a total 6x8
         policy matrix reaching all three dispositions, tightened by risk and
         then by an institution overlay; 200 generated overlays never loosened
         a cell at any risk, and an overlay that names a looser cell is
         unconstructible. (d) all 7 refusal reasons reached by their own
         scenario; a disqualifying conflict blocks assignment AND signoff AND
         the quorum (3 candidates, 1 disqualified, quorum of 3 not met); a
         manageable one blocks until a mitigation is approved by SOMEBODY ELSE;
         a recusal blocks its own dimension and no other; an evaluation whose
         verdicts contradict its refusals is unconstructible. (e) a total 9x5
         detail-visibility matrix: an author learns only that a declaration
         exists, a panel sees type and identity but never basis, evidence or
         dates; a withheld field is ABSENT from the view, not blanked; every
         look leaves an entry accounting for all 9 fields; a withdrawal does
         not delete while an appeal window is open or the 365-day minimum
         stands. Findings: my ViewRequest silently renamed M7.3's schema to
         metis__api__integrity_signal_review__ViewRequest (caught by diffing
         the schema set: -1) — renamed ConflictViewRequest; VisibilityProbe
         collided with M7.5's (4th collision in 4 cells); 10 orphan
         vocabularies became import-time totality checks and producibility
         tests, and one dead constant was deleted.
       -->
  - [x] M7.8.a Define conflict types/scope/subject/reviewer relationship,
        self-declaration, detected signal, confidentiality, evidence, dates, and
        status. Evidence: declaration schema.
  - [x] M7.8.b Require declaration/update before assignment/decision and provide
        safe “unsure/request review” path. Evidence: workflow tests.
  - [x] M7.8.c Evaluate conflict policy by review dimension/risk/institution as
        disqualifying, manageable with mitigation, or permitted. Evidence:
        policy matrix.
  - [x] M7.8.d Block assignment/signoff/quorum for incompatible conflicts and
        record reassignment/recusal/mitigation approval. Evidence: enforcement
        tests.
  - [x] M7.8.e Protect conflict details from unauthorized parties while auditing
        access and preserving appeal/retention needs. Evidence: privacy tests.
- [x] M7.9 Build institution-specific policy overlays with platform floors.
      Evidence: tenants can tighten but never loosen.
      <!-- Verified 2026-09-05: schemas/institution_overlay.py 7.9.0 +
         services/institution_overlay_{engine,evaluation}.py +
         api/institution_overlay.py (6 routes under /institution-overlays);
         tests/test_institution_overlay.py 22 tests; mypy --strict clean on 4
         files; OpenAPI +6 paths, +26 schemas, 0 changed, 0 removed. (a) 17
         typed settings covering all 12 families, each declaring its value kind
         and WHICH WAY IS TIGHTER (higher, lower, superset, subset, on, fixed);
         a direction that cannot describe a shape is unconstructible, and the
         import validator refuses the module if any direction, kind or family
         has no setting — it caught that nothing used `superset` until a
         required-formats setting was added. (b) five scopes stack from the
         platform floor; a resolved value records the level that set it and
         every level that contributed, in precedence order; a level that
         loosens produces a conflict naming both levels and both values, and
         the tighter value stands. (c) 300 generated stacks — a random value
         per setting at every scope — never resolved looser than any
         contributing level or than the platform floor; every setting was also
         walked directly with a tighter, an equal and a looser value, from a
         TIGHTENED base so a looser value always exists. (d) the preview counts
         affected items from the inventory across all six categories and is
         sealed by a digest; an activation is refused when it is unreviewed,
         approved over a different preview, in the wrong status, backdated, or
         would loosen something. (e) statuses over a total transition table
         with terminal superseded/rolled_back; a history is append-only with
         one active version; a rollback is a NEW draft carrying the old values
         and naming both what it supersedes and what it restores; revalidation
         lists what needs a recheck and names the items nothing touched.
         Findings: the preview and the activation check resolved a stack that
         `resolve` then filtered the candidate OUT of (not active yet), so a
         preview would have reported no impact — fixed with `as_if_active`; a
         level stacked twice (live version + candidate) recorded itself twice
         as a contributor; `approver_is_proposer` was an unreachable refusal
         because the type already forbids it (deleted, and proved at the type);
         my `ResolveRequest` silently renamed M7.6's OpenAPI schema — the fifth
         name collision in five cells, caught by diffing removals.
       -->
  - [x] M7.9.a Define typed versioned institution overlay for content/source/
        review/accessibility/integrity/provider/retention/export/assessment/
        grade/credential/tutor settings. Evidence: schema.
  - [x] M7.9.b Resolve platform/region/institution/program/course precedence
        with exact inheritance/merge/conflict semantics. Evidence: resolution
        tests.
  - [x] M7.9.c Prove field-level monotonic tightening of safety/privacy/rights/
        accessibility/integrity/security/authority floors. Evidence: property
        tests.
  - [x] M7.9.d Preview affected users/content/builds/attempts/integrations/
        schedules and require authorized review before activation. Evidence:
        impact tests.
  - [x] M7.9.e Version/effective-date/rollback overlays and revalidate dependent
        state without rewriting history. Evidence: lifecycle tests.
- [x] M7.10 Build gradebook visibility and correction review. Evidence:
      correction events are append-only and authorized.
      <!-- Verified 2026-09-05: schemas/gradebook_correction.py 7.10.0 +
         services/gradebook_correction_{engine,evaluation}.py +
         api/gradebook_correction.py (7 routes under /gradebook-corrections) +
         admin /grade-corrections + e2e; tests/test_gradebook_correction.py 23
         tests; 5 chromium e2e specs passed (axe after each, incl. 390px);
         admin vitest 1 passed; mypy --strict clean on 4 files; OpenAPI +7
         paths, +26 schemas, 0 changed, 0 removed. (a) a grade IS its events —
         there is no mutable value field and the current value is derived; a
         total 9 field-group x 5 audience x 2 release-state matrix (90 cells):
         a learner and a guardian see no value before release, an assessor
         marks blind in BOTH states, and a projection carrying a withheld field
         is unconstructible. (b) a request binds the exact event it disputes,
         its proposed value and kind, evidence, the requester and the counted
         downstream impact; the queue derives every delta and puts a correction
         already published downstream first. (c) all ten refusal reasons
         reached by their own scenario: the scorer cannot review, the requester
         cannot decide, the reviewer who moved it on cannot approve, a value
         off the scale or unchanged, a stale version, a wrong status, a
         superseded target, a moved item revision, and no notice to the
         learner. (d) a total 3x5 decision table; an approval APPENDS an event
         and every earlier one is byte-identical afterwards, so a view shown
         before the correction is still reproducible via history_as_of. (e)
         recomputation is a pure function of its inputs (same digest under
         reordering) and names the grades it excluded rather than treating them
         as zero; propagation derives its idempotency key from the payload, not
         the clock, so the second send reports unchanged, and prior views and
         appeals are carried on the result. Findings: my "a projection cannot
         carry a withheld field" test was vacuous for a teacher after release,
         who is withheld nothing — moved to an audience that is; DOWNSTREAM_SYSTEMS
         was an orphan vocabulary until each system was walked as an impact and
         a propagation target.
       -->
  - [x] M7.10.a Build role/field/time-window filtered grade/current/history/
        evidence projection for learner/guardian/teacher/assessor/admin.
        Evidence: visibility tests.
  - [x] M7.10.b Build correction request/detail queue with target grade
        revision, proposed value/status, reason/evidence, downstream impact, and
        requester. Evidence: review schema/UI.
  - [x] M7.10.c Enforce scorer/reviewer/approver separation, scale/range/policy/
        attempt/item freshness, expected version, and learner notice. Evidence:
        guard tests.
  - [x] M7.10.d Record approve/reject/request-changes as immutable decision and
        approved correction as append-only grade event. Evidence: history tests.
  - [x] M7.10.e Recompute aggregates/LTI/OneRoster/credentials idempotently and
        preserve prior views/appeals. Evidence: propagation tests.
- [x] M7.11 Build BYOM quarantine, evaluation, approval, kill switch, and abuse
      monitoring into operator review. Evidence: fail-closed end-to-end journey.
      <!-- Verified 2026-09-05: schemas/provider_model_admission.py 7.11.0 +
         services/provider_model_admission_{engine,evaluation}.py +
         api/provider_model_admission.py (6 routes under
         /provider-model-admissions); tests/test_provider_model_admission.py 22
         tests + the duplicate-name guard = 23 passed; mypy --strict clean on 4
         files; OpenAPI +6 paths, +27 schemas, 0 changed, 0 removed, 0
         qualified. NOTE: the estate's existing BYOM
         (libs/metis/integrations/src/byom, apps/oshun/bff/.../metis-byom-
         decision-store.ts) is bring-your-own-MATERIAL; this cell is
         bring-your-own-MODEL and shares no vocabulary with it. (a) intake
         records provider, endpoint (TLS-only, region-pinned, carrying a model
         FINGERPRINT), credential BY REFERENCE, ownership, licence, security
         review, data use, residency, cost envelope and capabilities, and
         starts quarantined; a scan of all 20 models' 134 field names against 8
         secret-bearing words matched nothing; provider_default data use and a
         residency that disagrees with the endpoint region are unconstructible.
         (b) 8 probe kinds, each accounted for in every run — a probe that did
         not run is recorded not_run and HOLDS the candidate whatever its kind,
         because the blocking/advisory split is about a failure and a
         measurement not taken is not a pass; fixtures must be synthetic and
         approved, and a gap in fixture coverage holds quarantine too.
         (c) five named roles, five DISTINCT people, every approval over the
         same evaluation run and the same fingerprint; a scope by tenant,
         course, use, data class and region with an expiry, and
         identified_learner data is unconstructible in a scope. All 12 refusal
         reasons reached, each carrying fallback=not_configured. (d) a kill is
         scoped (candidate/tenant/provider/platform) with a blast radius
         counted from what is admitted and running, a credential revoke, and an
         in-flight behaviour a platform kill cannot set to drain; restore gates
         are total over the five bases and every gate is required by some
         basis. (e) 9 abuse signals, 4 of which suspend before a person looks;
         a triage decision cannot dismiss or merely watch one of those, and
         must name the signals and kinds that drove it. (f) the nine-leg
         journey — direct endpoint, secret substitution, model substitution,
         stale approval, cross tenant, outage, abuse, kill, recovery — every
         leg refused, every one falling back to not_configured. Findings: an
         advisory probe that did not RUN was treated as no obstacle until the
         outage leg exposed it; AdmissionRefusal collided with M6.19's (sixth
         name collision, caught by the removal diff) — renamed
         ModelAdmissionRefusal, and the spec now carries zero qualified names.
       -->
  - [x] M7.11.a Intake provider/model/endpoint/config credentials by secret ref,
        ownership/license/security/data-use/residency/cost/capabilities and
        quarantined state. Evidence: intake tests.
  - [x] M7.11.b Probe/scan/benchmark with synthetic approved fixtures for
        contract, safety, grounding, answer leakage, privacy, latency/cost,
        availability, and model/version drift. Evidence: evaluation suite.
  - [x] M7.11.c Require named security/privacy/safety/education/operator
        approval and scope admission by tenant/course/use/data/region before
        enablement. Evidence: approval tests.
  - [x] M7.11.d Implement scoped disable/kill with session/job behavior,
        credential revoke, fallback/not-configured state, blast radius, and
        restore gates. Evidence: kill-switch tests.
  - [x] M7.11.e Monitor prompt injection/exfiltration, harmful/answer-leaking
        output, anomalous spend/rate/data, endpoint drift, and complaints with
        explainable human triage. Evidence: abuse monitoring tests.
  - [x] M7.11.f Run direct endpoint/secret/model substitution, stale approval,
        cross-tenant, outage, abuse, kill, and recovery journey. Evidence:
        fail-closed E2E.
- [x] M7.12 Build review SLA, escalation, workload, and notification views.
      Evidence: quiet hours never suppress urgent integrity events.
      <!-- Verified 2026-09-05: schemas/review_sla.py 7.12.0 +
         services/review_sla_{engine,evaluation}.py + api/review_sla.py (6
         routes under /review-sla) + admin /review-sla + e2e;
         tests/test_review_sla.py 20 tests; 5 chromium e2e specs passed (axe
         after each, incl. 390px); admin vitest 1 passed; mypy --strict clean
         on 4 files; OpenAPI +6 paths, +29 schemas, 0 changed, 0 removed, 0
         qualified. (a) priority is DERIVED from review type x risk over a
         total 6x4 table (a high-risk integrity review is urgent whoever files
         it); a business calendar in a real tzdata zone with working days,
         hours and holidays; deadlines are WALKED through working hours, so
         Friday 15:00 to Tuesday 11:00 across a bank holiday is 4 hours and a
         deadline always lands inside a working day; pauses carry a reason and
         are subtracted, so time spent waiting on somebody else is not time the
         reviewer was late; an escalation path must climb and start at the
         deadline. (b) workload excludes blocked and finished tasks, derives
         utilisation and reports none when capacity is zero; counts by role,
         locale or institution are suppressed below 5; assignment picks the
         least-loaded qualified locale-matched reviewer with the digest as a
         reproducible tie-break and records what it considered; all five
         assignment refusals reached. (c) four audiences; a summary reader gets
         counts and NO titles, and a view carrying a title it withholds — or
         withholding one it shows — is unconstructible. (d/e) quiet hours are
         evaluated in the recipient's own zone with zoneinfo: the same instant
         is night in London and the working day in New York and Kolkata, and
         the window holds either side of the UK's autumn clock change (asserted
         by the differing UTC offsets). A ratified urgent integrity or safety
         event is sent THROUGH quiet hours; the type makes a suppressed or
         deferred urgent notification unconstructible, and an override without
         a ratification equally so. A deferral names when it will be sent, and
         the idempotency key is derived from the payload, so a retry is one
         send. Findings: my `warned` state was unreachable in the first probe
         set (the clock never sat between 75% and 100% of the target); the New
         York probe conflated "urgent" with "quiet" and proved neither until
         split. Known unrelated reds in the suite: 5 pre-existing failures in
         cells this work never touched — test_evidence_bundle_helpers and
         test_item_assembly_eligibility (stale SimpleNamespace doubles missing
         `maturity`/`scalar` that exposure_control_service now reads) and
         test_item_difficulty_helpers (a fixture expecting 64 against a
         computed 640).
       -->
  - [x] M7.12.a Define review-type/risk/priority SLA clocks, business calendars,
        pauses, deadlines, breach thresholds, owners, and escalation paths.
        Evidence: SLA schema.
  - [x] M7.12.b Compute queue age/due/breach/workload/capacity by qualified
        role/ locale/institution with privacy-safe counts and fair assignment.
        Evidence: fake-clock/load tests.
  - [x] M7.12.c Build accessible queue/calendar/workload views with filters,
        current/stale/blocked states, reassignment, and no sensitive titles in
        unauthorized summaries. Evidence: UI tests.
  - [x] M7.12.d Route reminders/escalations over configured channels/quiet
        hours/ substitutes/on-call with idempotency and acknowledgement.
        Evidence: notification tests.
  - [x] M7.12.e Override quiet hours only for ratified urgent integrity/safety
        events, record reason, and test timezones/DST/failures. Evidence:
        escalation tests.
- [x] M7.13 Build human signoff gates that automation cannot complete. Evidence:
      system actor refusal.
      <!-- Verified 2026-09-05: schemas/human_signoff_gate.py 7.13.0 +
         services/human_signoff_gate_{engine,evaluation,service}.py +
         api/human_signoff_gate.py (5 routes under /human-signoff-gates);
         tests/test_human_signoff_gate.py 57 tests; ruff + mypy --strict clean
         on 5 source files and the test; OpenAPI +5 paths, +17 schemas, 0
         removed, 0 qualified. The whole cell rests on one property: a
         `HumanSignoff`'s actor field is annotated `HumanActor`, not a union,
         so a machine signature is not merely unauthorised but UNCONSTRUCTIBLE
         — asserted at import, walked over all 8 non-human kinds plus an
         impersonator and a delegate, and checked again in the published
         contract (the actor is a single `$ref`, not an anyOf). (a) 6 gate
         kinds over a total requirements table (eligible roles, quorum,
         step-up), a gate binds its exact subject revision and one digest over
         all its evidence, and `delegable` is a `Literal[False]`. (b) an actor
         carries an interactive authentication (method, session, second
         factor, 15-minute step-up freshness); the API builds it from the
         gateway's session headers and there is NO actor field in any request
         body. (c) refusal at three layers, each recorded by name in the report
         — the type (4 probes), the engine (10) and the store (1, which
         actually writes to a temp root and reads the directory back empty).
         (d) 15 adversarial probes, all 15 refusal reasons reached: batch
         replay, subject moved, evidence swapped, one person twice, expired,
         ineligible role, undeclared conflict, stale step-up, rewritten policy,
         copied record, two mass-assignment payloads, a frozen-record mutation,
         a service account in a signoff, and the store write. (e) a material
         change invalidates the gate and NAMES the signatures it preserves; all
         4 change kinds come from a real detection. Findings: 3 public helpers
         had 0 callers (deleted); `policy_changed` was unreachable from the
         detector, so a gate now records the digest of the requirements row it
         opened under and a rewritten policy is caught with no notification
         (this also added the `gate_policy_changed` refusal, named apart from
         the material-change kind so a log never has to guess which vocabulary
         a value came from); my first API took the existing signatures FROM THE
         CALLER, so a signer could hide their own earlier signature and be
         counted twice — the store holds them now; and `GateOutcome` collided
         with best_of_n's, silently renaming both in the published contract —
         visible only in the removal diff, and renamed to
         `SignoffGateOutcome`. Known unrelated reds: the same 5 pre-existing
         failures recorded under M7.12. -->
  - [x] M7.13.a Define human-only gate kinds, eligible named
        roles/qualifications/ independence/quorum, exact subject/evidence,
        decision fields, expiry, and nondelegability. Evidence: gate schema.
  - [x] M7.13.b Require interactive authenticated human action with step-up
        where needed, conflict declaration, rationale, and unchanged
        subject/evidence. Evidence: signoff tests.
  - [x] M7.13.c Reject system/service/agent/model/provider/import/migration
        actor types and impersonated/delegated tokens at API/domain/store
        layers. Evidence: system actor refusal tests.
  - [x] M7.13.d Prevent mass assignment, event replay, direct state mutation,
        copied signoff, stale approval, and flag/policy bypass. Evidence:
        adversarial tests.
  - [x] M7.13.e Invalidate on material change and preserve original human
        decision/audit. Evidence: lifecycle tests.
- [x] M7.14 Build review evidence export suitable for institutional audit.
      Evidence: stable schema, access control, and tamper hash.
      <!-- Verified 2026-09-05: schemas/review_evidence_export.py 7.14.0 +
         services/review_evidence_export_{engine,validator,evaluation}.py +
         api/review_evidence_export.py (5 routes under
         /review-evidence-exports); tests/test_review_evidence_export.py 55
         tests; ruff + mypy --strict clean on 5 source files and the test;
         OpenAPI +5 paths, +20 schemas, 0 removed, 0 qualified. The claim the
         cell rests on is that the checks read THE BYTES: a minimisation policy
         is verified by searching the produced files for the raw value, a
         checksum by re-hashing the file, and a tamper by handing the edited
         bundle to a validator that does not import the builder. (a) a manifest
         totalling all 9 sections — an absent one says whether it was barred by
         the purpose or simply not asked for — with a redaction ledger and the
         audit section that no scope may drop. (b) 5 purposes x 6 field classes
         = 30 minimisation cells, all 4 treatments used; every one of the 30 is
         probed against the bytes and the walk is non-vacuous (12 kept values
         ARE found, 18 removed ones are not); a pseudonym counts as removal, is
         stable within an export and disjoint under a different key; role x
         purpose and section x purpose are both total, and a learner reaches
         only their own subject access. (c) byte-identical rebuilds, sorted
         keys, RFC 4180 CRLF with every field quoted, one UTC spelling of an
         instant, and all 6 formula leaders (= + - @ TAB CR) guarded by a
         PREFIX so the auditor still sees what the field said; 402 rows split
         into 3 numbered parts sharing a header. (d) HMAC-SHA256 over the
         canonical manifest digest, per-file SHA-256 over the encoded bytes,
         verification instructions generated from the constants in use, and
         provenance naming actor, role, time and commit; with no key the build
         is REFUSED rather than shipped unsigned. (e) 11 tamper probes, 11 of
         11 detected, every validation finding reached — and where the edit is
         to the manifest the probe RE-SIGNS it, because an attacker who can
         rewrite a manifest can also recompute a signature. Findings: the first
         injection probe compared against a hand-quoted string and reported a
         false negative on the payload containing a quote (the guard was fine,
         the probe was not — it now reads the cell back with a csv reader); the
         first minimisation probe treated a pseudonym as a disclosure, so a
         leaked raw value alongside a pseudonym would have passed; and the
         engine carried a duplicate file-name guard that `ExportFile` already
         enforces and no test could reach, so it was deleted rather than kept
         as decoration. The builder and the validator share no code, so the two
         markers they agree on are asserted equal in a test. Known unrelated
         reds: the same 5 pre-existing failures recorded under M7.12. -->
  - [x] M7.14.a Define versioned export manifest for subjects/revisions,
        templates/gates, measurements/evidence, comments, assignments/conflicts,
        decisions/appeals/signoffs, actors/roles, audit, and redactions.
        Evidence: schema.
  - [x] M7.14.b Authorize export purpose/scope/institution/course/time/subjects/
        fields and apply learner/answer/integrity/source/privacy minimization.
        Evidence: access tests.
  - [x] M7.14.c Produce deterministic JSON/CSV/PDF or ratified formats with
        stable IDs/times/locales, checksums, schema/version metadata, and no
        spreadsheet/formula/archive injection. Evidence: export tests.
  - [x] M7.14.d Sign/hash manifest/files and include verification instructions,
        missing/redacted/external refs, generation actor/time/commit, and audit.
        Evidence: tamper tests.
  - [x] M7.14.e Round-trip through independent validator and test two-tenant/
        role/large export/retention/expiry behavior. Evidence: audit export
        suite.

### M8 — Persistence, APIs, jobs, and tenancy

- [x] M8.1 Implement missing workbench models using the ratified database.
      Evidence: migration review and real-DB apply.
      <!-- Verified 2026-09-06: a-d closed. The gap map (a) is computed from
         the code and says 98 ratified contracts against 106 tables, with 36
         cells writing only to the file store and 23 with no storage at all.
         The design (b) proposes 81 tables for those and refuses to design 158
         wire formats and computed reports. The migrations (c) carry all 81 as
         readable five-phase plans and freeze the 14 whose design needed no
         human decision into alembic revision 044. The apply (d) ran that chain
         against a real PostgreSQL 16.14 database from clean and from revision
         043, and checked the constraints and the query plans by asking the
         server. The 67 aggregates whose design carries an open question have a
         plan and no licence: a table nobody agreed the shape of is not created
         by a deriver, and that is recorded rather than quietly resolved. -->
  - [x] M8.1.a Diff ratified contracts/ownership against existing schema and
        list exact reusable models/fields/indexes and genuine gaps. Evidence:
        gap map.
        <!-- Verified 2026-09-05: schemas/persistence_gap_map.py 8.1.0 +
           services/persistence_gap_map.py + api/persistence_gap_map.py (2
           admin-only routes under /persistence-gap-map);
           tests/test_persistence_gap_map.py 24 tests; ruff + mypy --strict
           clean; OpenAPI +2 paths, +3 schemas, 0 removed, 0 qualified. The map
           is COMPUTED from three sources that cannot be edited into agreeing:
           the contract modules read with `ast`, the tables read from the
           SQLAlchemy metadata, and the medium each cell writes to read from
           its service's imports. Nothing is declared, so it moves when the
           code moves. Result: 98 ratified contracts against 106 tables — 39
           payload_backed, 36 file_store_only, 23 absent, 0 typed, 0
           unbacked_fields; by medium 37 ratified_database, 36 file_store, 25
           in_memory_only; 86 tables claimed, 20 unclaimed; and 0 tables
           holding a contract lack a tenant-leading index. Ownership comes from
           the contract's own version literal (7.14.0 is M7.14). Finding, and
           the reason the first draft was wrong: it reported 39 contracts as
           "partial" with hundreds of "missing" fields, because this estate
           stores payloads in `*_json` columns beside its typed ones — a field
           with no column of its own is very likely inside the payload and the
           schema cannot see which. Reporting that as missing would have been
           an accusation with no evidence, so the verdict vocabulary now
           separates `payload_backed` (untyped fields, and a payload column
           that may hold them) from `unbacked_fields` (untyped fields and no
           payload column anywhere), and the row says `untyped`, never
           `missing` — which is also exactly the input M8.2 needs. Two of the
           five verdicts have no instance in the estate today; that is a fact
           about the estate, not the function, so a test asks the decision
           function directly and every verdict is producible. -->
  - [x] M8.1.b Design tenant-scoped keys, logical/revision IDs, FKs/checks/
        uniqueness/classification/retention for each missing aggregate.
        Evidence: schema review.
        <!-- Verified 2026-09-05: schemas/persistence_design.py 8.1.1 +
           services/persistence_design.py + api/persistence_design.py (2
           admin-only routes under /persistence-design);
           tests/test_persistence_design.py 22 tests; ruff + mypy --strict
           clean; OpenAPI +2 paths, +7 schemas, 0 removed, 0 qualified. Derived
           from the 60 contracts the M8.1.a gap map says have no table: 81
           proposed tables and 158 shapes given none, every aggregate root of
           every missing contract accounted for as one or the other. The design
           is a PROPOSAL and most of these tests are about what it declines to
           decide. The tenant leads every key and the uniqueness is exactly the
           key in key order; where several identifier fields could be the key it
           names one, lists the rest and marks the row for ratification (30 of
           81 ambiguous); CHECKs are lifted from the `Literal` vocabularies the
           contracts already declare (asserted against agent_command's real
           COMMAND_KINDS); every FK is tenant-scoped by construction and records
           whether its target is a table this review proposes or one that
           already exists (both states reached); a name over Postgres's 63-char
           limit is shortened with a digest rather than truncated, because
           Postgres truncates silently and two long names become one table.
           Retention is proposed per classification and ALWAYS carries
           requires_ratification=True — a schema deriver does not get to decide
           how long an institution keeps records — and `operational_record` and
           `ratified` are reserved for a person: no pattern produces them, and a
           test asserts the deriver never does. 67 of 81 rows still need a human
           decision and the report says so. Finding: the first run proposed 166
           tables, 118 of them for HTTP request/response shapes that happened to
           carry an `assessment_id` — the suffix test now runs BEFORE the key
           test, because a wire format with an identifier in it is still a wire
           format and giving it a table invents an aggregate that does not
           exist. Also replaced a `forward_references` field that was always
           empty by construction with the per-FK target_state that actually
           distinguishes something. -->
  - [x] M8.1.c Add expand/backfill/verify/cutover/contract migrations with
        compatibility, idempotency, batching, rollback, and no shadow store.
        Evidence: migration tests.
        <!-- Verified 2026-09-06: schemas/persistence_migration.py 8.1.2 +
           services/persistence_migration.py +
           scripts/generate_persistence_expand_migration.py +
           alembic/versions/044_persistence_expand.py (frozen) + 2 more routes
           under /persistence-design; tests/test_persistence_migration.py 28
           tests; ruff + mypy --strict clean; OpenAPI +2 paths, +3 schemas, 0
           removed, 0 qualified. Five phases in one order for all 81 designed
           aggregates, and the order IS the safety property: expand only adds
           (every statement IF NOT EXISTS), backfill walks the store in sorted
           path order from a checkpoint in batches of 200, verify compares the
           two stores and is the only thing that yields a cutover permit,
           cutover moves the authority, contract removes the old store. A phase
           that writes owes a rollback and a read-only one may not have a
           made-up rollback — asserted both ways at the type. The backfill is
           tested against a real store on disk: 25 records in batches of 10,
           and an interruption at record 5 resumed twice, each record moved
           exactly once. All 4 verify verdicts reachable; a changed row at the
           SAME row count is caught by the digest. A cutover permit binds the
           source digest it was granted against, so a store that grew or
           changed between verifying and cutting over invalidates it. And a
           source still present after cutover is reported as the shadow store,
           which is exactly the state this sequence exists to avoid. The frozen
           alembic revision is GENERATED, not hand-written, and the drift test
           parses its `_STATEMENTS` with `ast` and compares tuples — a
           substring search over the source would have passed or failed on
           reformatting. It carries only the 14 aggregates the design settled
           on its own; the other 67 have a plan to read and no licence to run.
           Finding: `phase_order()` had 0 callers and was deleted. -->
  - [x] M8.1.d Apply to an isolated real database from clean and production-like
        prior versions; verify constraints/data/query plans. Evidence: real-DB
        report.
        <!-- Verified 2026-09-06 against PostgreSQL 16.14 on 127.0.0.1:5432:
           tests/test_persistence_real_database.py 11 tests, ALL PASSED, each
           run against a database created for the run (metis_m81d_<hex>) and
           dropped after it. `METIS_TEST_DATABASE_URL=postgresql+psycopg://
           oshun:***@127.0.0.1:5432/oshun_dev PYTHONPATH=src .venv/bin/python
           -m pytest tests/test_persistence_real_database.py -q` -> "11 passed
           in 11.62s". Applied `alembic upgrade head` from CLEAN (all 44
           revisions) and separately from the production-like prior version:
           `downgrade 043` removed persistence_cutover and all 14 tables, then
           `upgrade head` restored them — the upgrade a live database actually
           runs, which is not the same test as from clean. Constraints checked
           by asking the server, not by reading the SQL: the tenant-scoped
           unique index refuses a duplicate and allows the same logical id
           under a different tenant; a NULL tenant is refused; and a CHECK
           derived from a contract's Literal vocabulary refuses a value outside
           it while allowing one inside (this caught my own fixture first — the
           original test inserted `{"kind":"note"}` into agent_plan and Postgres
           rejected it, which is the constraint working). Idempotency: every
           frozen CREATE re-ran with no change; the ALTER TABLE ... ADD
           CONSTRAINT is the one statement Postgres has no IF NOT EXISTS for,
           and the test asserts it raises rather than pretending otherwise.
           Query plans measured with EXPLAIN over 100,000 rows across 5 tenants
           after ANALYZE: the tenant-scoped read uses an Index Scan with no Seq
           Scan, and the contrasting non-tenant read on an unindexed column IS a
           Seq Scan — without that contrast the first plan would prove nothing.
           -->
- [x] M8.2 Separate operational columns from versioned content payloads.
      Evidence: critical filters use indexed columns.
      <!-- Verified 2026-09-06: schemas/operational_projection.py 8.2.0 +
         services/operational_projection.py + api/operational_projection.py (2
         admin-only routes) + scripts/generate_operational_columns_migration.py
         + alembic/versions/045_operational_columns.py (frozen, 126
         statements); tests/test_operational_projection.py 34 tests +
         tests/test_persistence_real_database.py 17 (all against a real
         PostgreSQL 16.14 database created for the run); ruff + mypy --strict
         clean; OpenAPI +2 paths, +8 schemas, 0 removed, 0 qualified. (a) 4,490
         fields of every ratified aggregate classified into 13 roles — 914
         operational, 3,576 content — and `content` is the DEFAULT rather than
         a residue, because a field left in the payload is never lost while
         promoting one to a column asserts we know how to derive and index it.
         The matcher never uses a bare substring: `state` is inside
         `statement`, `risk` is inside `asterisk`, and a field matches only on
         its whole name or a trailing `_`-separated segment. (b) 7 typed
         columns, each naming the ONE payload field it comes from; two columns
         from one field would be two derivations and the test refuses it, and a
         source scan asserts no other module in the estate writes an `op_`
         column. An absent field projects to NULL, never to a default nobody
         wrote. (c) an operational change carries the content digest through
         untouched and `ImmutableUpdate` cannot be constructed if it moved;
         authored content changes by making a revision. (d) tenant-leading
         composite and partial indexes, benchmarked with EXPLAIN over 100,000
         rows across 5 tenants after ANALYZE: the state filter uses the PARTIAL
         index by name, the owner filter uses the owner index, no ratified
         filter's plan mentions content_json or jsonb — and the contrasting
         payload filter does, which is what the columns replaced. (e) the
         reconciler recomputes from the payload and REPORTS; `repaired` is a
         Literal[False] so no caller can read the result as an instruction, and
         all 4 divergence kinds are reached. Findings: `identity` was a role no
         field could reach, and `gate_id` was being filed as authored content —
         a trailing `_id` rule now classifies a record's own identifier, after
         the specific patterns so `tenant_id` and `actor_id` keep theirs. And I
         wrote the truncation bug M8.1.b warns about: `[:63]` was applied to the
         whole `CREATE INDEX IF NOT EXISTS ix_...` prefix, so the index created
         was `..._tenant_sta` while the plan declared `..._tenant_state` — the
         real-database benchmark caught it, the name is now computed once and
         digest-shortened, and the type refuses a plan whose declared name is
         not in its own statement. -->
  - [x] M8.2.a Classify identity/tenant/owner/state/maturity/timestamps/locks/
        queue/freshness/risk/visibility fields as operational versus immutable
        authored content/revision payload. Evidence: field map.
  - [x] M8.2.b Normalize query-critical operational fields into typed columns
        with constraints and one authoritative derivation. Evidence: schema
        tests.
  - [x] M8.2.c Store versioned typed content by immutable revision with schema
        version/hash and prohibit operational mutation from rewriting payload.
        Evidence: immutability tests.
  - [x] M8.2.d Add tenant-leading composite/partial indexes for ratified
        filters/ sorts and verify production-scale query plans/no JSON scans.
        Evidence: index benchmark.
  - [x] M8.2.e Reconcile operational projection after revision/events and detect
        divergence instead of dual authority. Evidence: consistency tests.
- [x] M8.3 Implement course, lesson, item, rubric, source, objective, standard,
      review, and release stores. Evidence: durable-before-ack tests.
      <!-- Verified 2026-09-06: schemas/aggregate_store.py +
         services/aggregate_store.py + alembic/versions/046_aggregate_stores.py
         (5 tables, hand-written because fixed rather than derived);
         tests/test_aggregate_store_real_database.py 35 tests, ALL against a
         real PostgreSQL 16.14 database created for the run and dropped after;
         ruff + mypy --strict clean. There is no unit-test version of this
         cell: "the four rows commit together", "a second writer at the same
         revision loses" and "a link to a revision that does not exist cannot
         be inserted" are properties of a database, and a fake would only prove
         the fake behaves as written. (a) ONE store for all nine kinds rather
         than nine copies of the same concurrency and audit code — the kind is
         a column, so a cross-aggregate reference is one foreign key instead of
         nine; the repository owns its transactions and no method leaves one
         open, so a partial write is not something it can be asked to do. All 9
         kinds created and read back, and the CHECK on the table is asserted to
         name all 9. (b) `expected_revision` is required, so a caller that has
         not read cannot write; two threads writing at revision 1 leave exactly
         one winner, one `revision_conflict` naming the revision the store
         holds, and two revisions in the table — not three. Exact revision
         reads, an unknown revision refused rather than approximated, the
         revision chain linked by previous_sha256, a caller refused a kind it
         does not hold and a course it is not scoped to. (c) references are a
         FOREIGN KEY into store_revisions, so a link to a revision that was
         never written is refused by the DATABASE — nothing in the application
         checks, and a reference into another tenant is not visible to the key
         at all, which is stronger than recognising and rejecting it. A
         reference pins an exact revision that a later revision of the target
         does not move. (d) the durability test does not check what the store
         SAYS: it kills a write with a duplicate idempotency key after the
         revision, aggregate and audit rows are already inserted, then counts
         all four tables from a fresh connection — 0, 0, 0, 0 — while the
         earlier successful write is untouched. The acknowledgement is
         constructed after the `with` block, so "acknowledged" and "durable"
         cannot come apart. (e) restart (a rebuilt repository reads what the
         old one wrote), duplicate/retry (an idempotency key refuses the second
         attempt, and a byte-identical payload is not a new revision), 64
         concurrent writers across 8 threads all committing, two tenants
         holding the same logical id without meeting, and 100,000 rows across 5
         tenants where the tenant+kind+state read uses
         ix_store_aggregates_tenant_kind_state with no Seq Scan. 7 of 8 refusal
         reasons reached; `cross_tenant_reference` is answered by the foreign
         key as `unknown_revision`, recorded rather than papered over. Findings:
         my first scale test loaded ONE tenant and asserted an index scan — the
         planner was right to scan, because a tenant predicate that matches
         every row is not selective, so the fixture now loads five tenants and
         a contrasting no-tenant read is asserted to BE a Seq Scan. Also
         removed a `referenced` audit action nothing produced and the module's
         `*_SCHEMA_VERSION` marker, which would have made the storage layer
         count itself among the things needing storage. -->
  - [x] M8.3.a Define repository interfaces and transaction ownership for each
        aggregate with typed create-revision/read/list/transition operations.
        Evidence: store contracts.
  - [x] M8.3.b Implement tenant/project/course-scoped persistence, exact
        revision reads, constraints, optimistic concurrency, and authorization
        context. Evidence: repository tests.
  - [x] M8.3.c Implement cross-aggregate refs through validated IDs/versions and
        outbox events, avoiding application-enforced dangling links. Evidence:
        integration tests.
  - [x] M8.3.d Commit aggregate/revision/audit/outbox atomically before success
        acknowledgement and roll back on any failure. Evidence: durable-before-
        ack tests.
  - [x] M8.3.e Test restart/duplicate/retry/deadlock/serialization/two-tenant
        and production-size access paths. Evidence: real-DB suite.
- [x] M8.4 Implement immutable revision and semantic-diff queries. Evidence:
      history integrity integration test.
      <!-- Verified 2026-09-06: schemas/revision_history.py 8.4.0 +
         services/revision_history.py +
         alembic/versions/047_revision_immutability.py (a `reason` column, two
         triggers and two keyset indexes); tests/test_revision_history_diff.py
         44 tests + tests/test_revision_history_real_database.py 22 tests
         against a real PostgreSQL 16.14 database created for the run; ruff +
         mypy --strict clean. (a) pagination is by CURSOR, not offset: an
         offset means something different the moment a revision is written, so
         the test pages, writes a revision in between, and asserts nothing from
         the first page reappears on the second; paging all the way through 10
         revisions returns each exactly once. Ancestry walks link by link and
         reports the revision where a chain breaks rather than trusting it;
         consumers are the things that pinned an EXACT revision (revision 2
         exists and nothing pinned it); a branch point cannot be written by the
         store, so the query reports damage rather than branching. Protected
         fields are withheld BY NAME — a learner's row carries no reason and no
         author and says which, and the digest is never withheld from anybody.
         (b) immutability is a TRIGGER, not a rule the application keeps: the
         test runs UPDATE against all six of payload, hash, parent, author,
         reason and time, and a DELETE, and the database refuses each with
         "history is immutable"; the audit table too. An approved workflow may
         redact (`retention_expiry`, `legal_erasure`) and an invented name may
         not — and because the permission is `SET LOCAL`, the very next
         statement on the same pooled connection is refused again, which is
         asserted. (c/d) the diff is over IDENTITIES: three blocks rotated give
         three `moved` and zero `changed`, an insertion in the middle moves the
         ones after it and changes none, and a list with no identities is
         answered `unknown` rather than guessed at. An answer-key path is
         `redacted` — that it changed, never what to, and the old value appears
         nowhere in the serialised diff. All 7 dimensions reached from real
         payload pairs, all 7 change kinds, and the diff names both revisions
         and both digests so it cannot be attached to the wrong pair. (e) the
         corruption fixture drags the projection back to revision 1 and deletes
         another one entirely; the rebuild replays the log, repairs both, and
         the log itself is verified unchanged afterwards — the repair only ever
         goes from the log to the projection. Findings: the `entity` dimension
         was unreachable because `A and B or C` grouped wrongly, so a whole
         object added was filed as a plain field; and threading the new
         `reason` column through M8.3's read left the SELECT one column short,
         which the rebuild test caught as an IndexError. -->
  - [x] M8.4.a Implement revision list/detail/ancestry/branch/consumer queries
        with stable pagination and protected-field projection. Evidence: query
        tests.
  - [x] M8.4.b Verify stored payload/hash/parent/author/reason/time cannot be
        changed/deleted outside approved retention/legal workflows. Evidence:
        immutability tests.
  - [x] M8.4.c Compute typed semantic field/entity/list/relationship/source/
        accessibility/key-safe diffs using stable identities. Evidence: diff
        goldens.
  - [x] M8.4.d Represent added/removed/changed/moved/unchanged/redacted/unknown
        and bound diff to schema/version pair. Evidence: result schema.
  - [x] M8.4.e Rebuild current/history from revisions/events after corruption
        fixture and verify integrity across restore/supersede. Evidence:
        integration test.
- [x] M8.5 Implement source/claim/citation and blast-radius APIs. Evidence:
      high-risk grounding known-value route test.
      <!-- Verified 2026-09-06: schemas/source_graph.py 8.5.0 +
         services/source_graph.py + api/source_graph.py (4 routes under
         /source-graph) + alembic/versions/048_source_propagation.py;
         tests/test_source_graph.py 35 tests + 16 against a real PostgreSQL
         16.14 database; ruff + mypy --strict clean; OpenAPI +4 paths, +9
         schemas, 0 removed, 0 qualified. (a) six typed locator kinds, and a
         locator nobody could follow is not accepted: a bad DOI, an http URL and
         a `javascript:` URL are all refused, and a page range that ends before
         it begins points at nothing — which a regex over "pages 40-12" cannot
         see and a typed range can. Property-level authorization by audience: a
         learner reads a protected source's title and NOT its locator, rights or
         protected flag, and the withheld properties are NAMED. (b) rights that
         were never checked are not rights to use (`unknown` is deliberately
         outside CITABLE_RIGHTS), currency runs from the last verification and
         differs by authority, and a stale source is not citable however good
         its rights. The headline: THERE IS NO PATH TO A CANONICAL CLAIM THAT
         DOES NOT CARRY A HUMAN DECISION — `advance_claim` raises on
         `canonical` by name, `make_canonical` requires the decision as an
         argument, the type refuses a canonical claim without one, and a
         provider proposal carries `is_decision: Literal[False]` at any
         confidence. (c) breadth-first over M8.3's reference rows with a fixed
         neighbour ordering, so the reported shortest path is the SAME shortest
         path on every run (asserted by walking twice); a node reachable two
         ways is reported once at the shorter distance; a walk that hits the
         depth limit reports the depth it stopped at, because a truncated answer
         that does not admit it is a wrong answer; and the walk does not cross a
         tenant even when both hold the same logical id. (d) a retraction writes
         propagation rows and outbox events in one transaction and NEVER touches
         the revision log — asserted by re-reading the lesson's digest, checking
         the ancestry is intact, and counting the revisions afterwards. (e) the
         known-value route: source -> objective -> lesson -> item -> release plus
         a tutor review, retracted as protected material, with the exact five
         (kind, id, state, owner) tuples and the exact distances written down in
         the test, and another tenant's identical chain untouched. Finding: the
         outbox idempotency key omitted the reason, so an ESCALATION from stale
         to blocked could not write its events and killed the transaction — a
         stale notice and a later blocking notice are different news an owner
         must get, so the reason is now part of the key, the insert is
         ON CONFLICT DO NOTHING, and a true retry acknowledges the delivery it
         already made rather than inventing a second one. -->
  - [x] M8.5.a Define source/extraction/claim/citation
        list/detail/create-review/ link/verify contracts with exact revisions
        and property authorization. Evidence: API schemas.
  - [x] M8.5.b Validate typed locators/source rights/authority/currency and
        provider proposals; canonical claims require human decision. Evidence:
        mutation tests.
  - [x] M8.5.c Implement forward/reverse dependency and impact preview queries
        with stable paths/pagination/reasons/current state. Evidence: graph
        tests.
  - [x] M8.5.d Implement update/retraction command triggering stale/block state,
        owner queue, outbox/audit, and no history rewrite. Evidence: propagation
        tests.
  - [x] M8.5.e Run high-risk source-to-lesson/item/tutor/published-build route
        fixture with cross-tenant/protected-source failures. Evidence: known-
        value route E2E.
- [x] M8.6 Implement course/lesson/item/rubric CRUD with property-level
      authorization and optimistic concurrency. Evidence: contract suite.
      <!-- Verified 2026-09-06: schemas/authoring_api.py 8.6.0 +
         services/authoring_api.py + api/authoring.py (6 routes under
         /authoring) + alembic/versions/049_aggregate_archive.py;
         tests/test_authoring_api.py 23 tests against a real PostgreSQL 16.14
         database; ruff + mypy --strict clean; OpenAPI +6 paths, +11 schemas, 1
         removed (my own M8.5 SourceRecord, renamed), 0 qualified. (a) eight
         operations over M8.3's store, and A PUBLISHED AGGREGATE IS NOT EDITED
         IN PLACE — `edit` refuses it by name and says to make a new revision,
         which is a different route with the same store call underneath. Archive
         is a SHELF, not a lifecycle decision: a withdrawn lesson can be
         archived and restored and is still withdrawn afterwards, because
         withdrawal is terminal on purpose and a shelf is a different question.
         (b) role x operation is total and no two roles are alike; a role is
         refused at the route before the object is looked at (proved by showing
         the object does not exist afterwards); per-property visibility is
         applied in ONE place on the way out, and it applies to VALIDATION
         ERRORS too — a learner's rejection of an empty answer_key says the
         property is empty and does not quote it, which is the easiest place a
         withheld field escapes. Groups match whole segments, so `answerable` is
         not an answer. (c) never last-write-wins: a second editor at the same
         base gets `conflict` with the current and base revisions and the store
         still holds the FIRST writer's value; the conflict diff is the M8.4
         differ, so an answer-key change is `redacted` and the old value appears
         nowhere in the serialised outcome — a merge hint is not worth handing a
         key to anybody who can force a conflict. ETags round-trip and refuse
         one issued for another version. (d) writes go through M8.3's atomic
         four-row transaction with idempotency. (e) all SEVEN outcome states are
         published in one type and all seven are REACHED by the suite; success
         and redacted are deliberately different answers, because a caller
         reading success with fields missing would take them for fields that
         were never there. Findings: three model names collided with other
         cells (PropagationResult, SourceRecord, ImpactPreview) — renamed to
         SourceRetractionResult, CitedSource, BlastRadiusPreview — and
         EditRequest collided with the anchored-comment API's. The OpenAPI
         removal diff caught only the last one: a collision between an exposed
         model and one that is NOT exposed reaches the contract as a silent
         rename of the exposed one, so the package-wide duplicate-name guard is
         the check that sees it, and this cell now carries a copy. Also fixed an
         ETag parser that split from the left, cutting `lesson:0001` in half. -->
  - [x] M8.6.a Define versioned create/read/list/edit/new-revision/transition/
        archive/restore contracts and disallow in-place published mutation.
        Evidence: schemas.
  - [x] M8.6.b Enforce route/function/object/tenant/course authorization and
        per-property answer/source/learner/review visibility on inputs/outputs/
        errors. Evidence: auth matrix.
  - [x] M8.6.c Accept expected revision/ETag and return typed conflict with safe
        current/base diff; never last-write-wins. Evidence: concurrency tests.
  - [x] M8.6.d Validate relationships/registries/standards/floors and write
        revision/audit/outbox atomically with idempotency. Evidence: mutation
        tests.
  - [x] M8.6.e Publish consumer contracts for success/validation/conflict/stale/
        forbidden/not-found/redacted states. Evidence: contract suite.
- [x] M8.7 Implement provider-backed generation/assist APIs. Evidence: 503, 502,
      cancellation, malformed output, budget, and success cases.
      <!-- Verified 2026-09-06: schemas/generation_job.py 8.7.0 +
         services/generation_job.py + api/generation.py (4 routes under
         /generation) + alembic/versions/050_generation_jobs.py;
         tests/test_generation_job.py 45 tests against a real PostgreSQL 16.14
         database with a provider double at the seam; ruff + mypy --strict
         clean; OpenAPI +4 paths, +8 schemas, 0 removed, 0 qualified. (a) five
         operations, typed request/context/estimate/job/proposal/progress/error,
         and the provider is a Protocol — the only thing faked in the tests, and
         it is the dependency rather than the thing under test. (b) GRANTS ARE
         RESOLVED FROM THE OPERATION AND THE REQUEST IS NOT AN INPUT: the
         server's context holds answer_key, learner_responses and credentials,
         and no operation reads any of them; the prompt is asserted not to
         contain them. Money is reserved BEFORE dispatch inside a transaction
         that counts open holds, so two jobs cannot both fit into room for one
         (asserted), and a job that does not fit never reaches the provider
         (the double records zero calls). (c) all nine failure classes carry a
         fixed status — not_configured and provider_unavailable and rate_limited
         are 503, malformed_output and upstream_error are 502 — and BOTH the
         status and the retryability come from the class, refused at the type
         when a caller tries to set either case by case. malformed_output is
         deliberately not retryable: the same prompt produces the same mess.
         There is NO FALLBACK: an unconfigured provider is a 503 over HTTP and
         is never quietly replaced. (d) durable jobs read back from the
         database, idempotent by key, and FENCED — the cancel test bumps the
         fence while a provider is still working and the late result is dropped,
         with the contrast that an on-time result is accepted. Cancellation
         releases the money. (e) a proposal carries applied and approved as
         Literal[False] and there is no field that could say otherwise; a
         generation writes nothing to the aggregate store (asserted by counting
         revisions); and every one of the six failing behaviours raises with a
         planted secret in its message while NONE of the four secrets reaches
         the failure body or the job document. Findings: my budget tests assumed
         a 1-cent limit would refuse a job the estimator prices at 1 cent — it
         fits, and the honest zero-budget case is limit 0; and the
         forbidden-grants check sat behind the allowed-grants check, making it
         unreachable, so it now answers first. Two vocabulary values have no
         producer in this build — rate_limited (the seam cannot report it yet)
         and queued (the job runs inline) — and that is recorded in the
         reachability assertions rather than papered over. -->
  - [x] M8.7.a Define operation-specific typed
        request/estimate/confirmation/job/ proposal/progress/error contracts and
        injected provider seam. Evidence: API schemas.
  - [x] M8.7.b Resolve canonical source/course/item/context/policy/tool grants
        server-side and reserve quota/budget before dispatch. Evidence: guard
        tests.
  - [x] M8.7.c Map not-configured/unavailable to 503, malformed/upstream failure
        to stable 502 class, plus refusal/timeout/rate/budget/cancel states
        without fallback. Evidence: failure contracts.
  - [x] M8.7.d Implement idempotent durable jobs, progress stream, cancellation/
        late-result fencing, and attributed proposal intake. Evidence: job
        tests.
  - [x] M8.7.e Prove success cannot auto-edit/approve/publish/expose answers and
        errors leak no prompts/credentials/learner data. Evidence: boundary
        suite.
- [x] M8.8 Implement quality and psychometric gate jobs. Evidence: stale-policy
      recomputation tests.
      <!-- Verified 2026-09-06: schemas/gate_job.py 8.8.0 +
         services/gate_job.py + alembic/versions/051_gate_jobs.py;
         tests/test_gate_job.py 47 tests + 2 real-database tests; ruff + mypy
         --strict clean. The cell turns on ONE distinction and everything
         follows from it: a MEASUREMENT is identified by what could change what
         was observed (subject revision, evaluator, evaluator version, corpus,
         input digest) and a VERDICT by that measurement plus the policy and its
         thresholds. The two input sets are DISJOINT, asserted at import, which
         is what makes `decide_recompute` a derivation rather than a judgement.
         (d) a threshold or policy version moving is `verdict_only` and the
         measurement key is literally unchanged; each of the 5 measurement
         inputs moving is `new_measurement_required` (parametrised over all
         five); when both move the measurement wins, because no policy
         arithmetic can produce an observation of a different subject. The
         schema enforces the same rule: `gate_measurement` is keyed by the
         observation and not the policy, so a policy edit CANNOT write a second
         row — checked against the real constraint on PostgreSQL 16.14. (a)
         one item per gate at one exact revision; a gate measuring a different
         revision, waiting on a gate the job does not run, or waiting on itself
         is unconstructible, and a dependency cycle is refused at plan time
         rather than hanging. (b) leases with a holder and an expiry, reclaimable
         once lapsed, bounded at 3 attempts; a dependant of a failed gate is
         SKIPPED rather than failed, because it did not fail — nothing ran it;
         and a cancellation KEEPS what was already measured, since throwing away
         real observations makes cancelling expensive enough that people stop
         doing it when they should. (c) a measurement cannot carry a judgement
         (a `verdict`/`passed`/`decision` key is refused) and a gate that does
         not apply observed nothing; applicability, uncertainty and freshness are
         separate fields, and the type refuses a `pass` that is not applicable,
         is undetermined, or rests on a stale measurement. A DETERMINISTIC
         measurement never goes stale on its own — same inputs, same answer —
         and its absence from the freshness table is asserted. (e) authority is
         computed from EVERY gate: one failure withholds the whole job, an
         unfinished or cancelled job authorises nothing, a stale measurement
         withholds even where the numbers were fine, and `advisory` is a note
         rather than permission. All 5 withholding reasons reached, and
         "mostly passed" is unconstructible. -->
  - [x] M8.8.a Resolve exact subject revisions and required deterministic/model/
        psychometric/accessibility gates plus evaluator/policy/corpus versions.
        Evidence: job plan tests.
  - [x] M8.8.b Execute bounded dependency-aware gate items with leases,
        idempotency, progress, retries, cancellation, and partial preservation.
        Evidence: worker tests.
  - [x] M8.8.c Persist immutable raw measurements and separate verdicts/
        applicability/uncertainty/freshness. Evidence: result tests.
  - [x] M8.8.d Recompute verdict-only on policy/threshold change and require new
        measurement on subject/evaluator/input change. Evidence: stale-policy
        tests.
  - [x] M8.8.e Prevent partial/stale/unknown jobs from authorizing publication
        or calibration promotion. Evidence: authority tests.
- [x] M8.9 Implement QTI/LTI/OneRoster/SCORM/xAPI/Caliper/Open Badges import and
      export jobs. Evidence: per-standard golden fixtures.
      <!-- Verified 2026-09-06: schemas/standards_exchange.py 8.9.0 +
         services/standards_exchange.py + services/open_badges_service.py (the
         seventh standard, which this build had no implementation for);
         tests/test_standards_exchange.py 42 tests; ruff + mypy --strict clean.
         (a) one job shape over 7 standards x 2 directions, and A CAPABILITY IS
         WHAT THE CODE DOES: the registry declares intent, `probe_capabilities`
         imports the implementation module, and `supported` is refused at the
         type unless both are true. The probe initially reported open_badges
         UNSUPPORTED because nothing implemented it — so it was implemented
         (OB 3.0 assertions as Verifiable Credentials over the estate's own
         credential models) rather than left as a note, and all 14 cells are now
         supported. (b) the hostile defences live with the CONTAINER rather than
         the standard, because the attacks are properties of the container: real
         fixtures build a zip whose member is `../../etc/cron.d/x`, a deflated
         archive that unpacks past the ceiling from a 300KB file, an XML with a
         billion-laughs entity, and an XML pulling in /etc/passwd — all refused,
         and a clean payload of every container passes. A preview accounts for
         everything it was shown and plans NOTHING when the payload itself is
         refused. (c) exports declare loss DERIVED FROM THE PROFILE rather than
         from what the exporter noticed, `undeclared_loss` catches a manifest
         that hid one, a manifest cannot declare loss against another profile,
         and two builds of the same export are byte-identical. (d) NO SILENT
         DROPS: the receipt refuses to exist unless every offered item has
         exactly one outcome, so "we lost three of forty questions" is not a bug
         this surface can have — it is a shape it cannot express. A resume
         passes what it already did, touches none of it again, and still
         accounts for all of it. (e) minimal/full/edge goldens for the Open
         Badges profile all parse and round-trip byte-identically through build
         and parse, with six incompatible cells stated by name — and a document
         carrying a `proof` reports `proof_present`, never `verified`, because
         treating the presence of a proof as proof is how badge systems are
         fooled. Findings: my zip-bomb fixture wrote STORED rather than
         DEFLATED, so the "small archive" was 32MB; and the completion check I
         wanted to exercise sat behind the receipt check, so the test now
         supplies a receipt to reach it. -->
  - [x] M8.9.a Define per-standard
        operation/direction/profile/version/credential/
        input/output/diagnostic/progress/result contracts and capability probes.
        Evidence: job schemas.
  - [x] M8.9.b Parse/fetch/upload safely, validate conformance/identity/version/
        rights, preview mappings/loss/conflicts, and require authorization.
        Evidence: hostile import tests.
  - [x] M8.9.c Export exact revisions deterministically with standards metadata,
        privacy/property filtering, manifests/hashes, and declared loss.
        Evidence: export tests.
  - [x] M8.9.d Run durable idempotent per-item/resource jobs with partial
        results, retry/cancel/resume, receipts, and no silent drops. Evidence:
        lifecycle tests.
  - [x] M8.9.e Validate/import/read-back minimal/full/edge golden for every
        supported profile and explicit incompatible cells. Evidence: conformance
        suite.
- [x] M8.10 Implement publication and LMS synchronization as idempotent sagas.
      Evidence: partial-failure recovery.
      <!-- Verified 2026-09-06: schemas/publication_saga.py 8.10.0 +
         services/publication_saga.py + models/publication_saga.py +
         alembic/versions/052_publication_sagas.py;
         tests/test_publication_saga.py 54 +
         tests/test_publication_saga_real_database.py 34 (real PostgreSQL, a
         destination double at the boundary); ruff + mypy --strict clean; no new
         routes, so the OpenAPI diff is empty and the package-wide duplicate
         model-name guard is clean.
         (a) the plan names exact build/assessment revisions and target IDs, is
         stored once per key (a re-plan returns the first plan, because
         re-planning is how duplicates get made), and refuses two steps sharing
         an idempotency key, a step waiting on one the order never satisfies,
         and — via `duplicate_risk` at `store_plan` — a creating step whose key
         is not unique to this plan.
         (b) THE PREREQUISITES ARE RE-READ BEFORE EVERY WRITE, NOT ONCE PER RUN:
         a probe counts snapshots taken and asserts one per step, and a double
         that moves `rights` after the second step leaves steps 1-2 succeeded,
         step 3 pending, the run `partial`/compensate, and the destination
         holding exactly two writes. A plan that goes stale before anything
         landed is `needs_operator`/manual rather than compensate: there is
         nothing to undo and the plan itself is what moved.
         (c) THE INTENT TO WRITE IS COMMITTED BEFORE THE CALL GOES OUT. A
         separate connection, opened from inside the destination's `write`, sees
         the ledger row already committed; a timeout raised after the resource
         landed leaves the step `verifying`, and the resume reads instead of
         writing — one write under that key, asserted from the double. A refused
         connection cannot have delivered anything, so its key is released and
         the retry writes; anything unrecognised is treated as possibly
         delivered, because an unverified step costs a read and a duplicated one
         costs a duplicate.
         (d) A WRITE IS NOT A SUCCESS UNTIL THE DESTINATION IS READ BACK AND
         AGREES. `StepCheckpoint(state="succeeded")` is unconstructible with no
         verification, with one that came back absent, with one that came back
         different, and with a "matched" verification whose two digests do not
         match. A destination that acknowledges and silently drops reads back
         `absent` (retry, and the key goes back because we now know it did not
         land); one holding something else reads back `different` and is never
         overwritten automatically — a re-execute writes nothing.
         (e) compensation walks the plan in reverse under its own key suffix,
         and a compensation the read-back cannot confirm LEAVES THE STEP
         SUCCEEDED — because it is, the resource is still standing — and asks
         for a person instead of reporting a rollback that did not happen.
         `reconcile` only reads: it reports a resource the destination lost
         (retry), a write that landed without being recorded (reconcile, adopt
         it, because writing again is how a course gets two), one holding
         something else (manual), and what it could not read at all, and a probe
         asserts it changed neither the run nor the destination.
         Two vocabulary defects in my own draft, both caught by producibility
         probes: `writing` and `revalidating` were states no checkpoint could
         ever be written in (checkpoints are persisted at decisions, not during
         them), and `TERMINAL_STEP_STATES` described something the engine does
         not honour — it retries `failed`. Replaced by `RESUMABLE_STEP_STATES`,
         which the engine actually branches on, with a real-DB probe asserting
         every remaining state and every saga state but `running` is produced by
         some scenario.
         M8.1's gap map then correctly reported the new contract as storage-less,
         because 052's tables are raw SQL and were not in `Base.metadata`; adding
         models/publication_saga.py fixed that, and the gap map's own
         tenant-index invariant immediately caught that two child tables had no
         tenant column at all — both now carry `tenant_id` and a tenant-leading
         index, with a probe asserting every row a run writes carries it. -->
  - [x] M8.10.a Define immutable publication/sync plan with exact build/
        assessment revisions, target/course/context/resource IDs, prerequisite
        snapshot, steps, compensation, and idempotency. Evidence: saga schema.
  - [x] M8.10.b Revalidate gates/reviews/rights/standards/target/config/
        authorization immediately before each external write. Evidence: TOCTOU
        tests.
  - [x] M8.10.c Execute create/update/link/content/grade/config/visibility steps
        with durable checkpoints and destination-native idempotency/versioning.
        Evidence: integration tests.
  - [x] M8.10.d Read back external state/bytes/metadata/permissions/links and
        mark success only after verification. Evidence: read-back tests.
  - [x] M8.10.e Recover by reconcile/retry/compensate/manual action without
        duplicating resources or hiding partial state. Evidence: partial-failure
        tests.
- [x] M8.11 Implement gradebook corrections and late-event replay. Evidence:
      duplicate and out-of-order tests.
      <!-- Verified 2026-09-06: schemas/grade_ledger.py 8.11.0 +
         services/grade_ledger.py + models/grade_ledger.py +
         alembic/versions/053_grade_ledger.py, under M7.10's existing grade
         contracts; tests/test_grade_ledger.py 35 +
         tests/test_grade_ledger_real_database.py 23 (real PostgreSQL); ruff +
         mypy --strict clean; no new routes, OpenAPI unchanged.
         (a) AN ARRIVAL ORDER IS NOT AN EVENT ORDER. The ledger keeps
         `occurred_at` (what the source said) and `received_at` (when we heard)
         in separate columns and orders by the first only; the permutation probe
         feeds four events in all 24 arrival orders and every one produces the
         same sequence numbers and the same digest. AN EXTERNAL IDENTIFIER IS
         NOT AN INTERNAL ONE: a platform's event id can be an uppercase UUID or
         a URL, so the source's id is stored verbatim (it is what dedup keys on)
         and the estate's `Identifier` is derived from it — deterministic and
         scoped by system.
         (b) appends are serialised per grade with an advisory lock, because AN
         EXPECTED-VERSION CHECK TWO WRITERS CAN PASS AT ONCE IS A COMMENT: two
         threads at the same version contend and exactly one wins. Audit and
         outbox rows are written in the append's own transaction.
         (c) A TIE IS AN AMBIGUITY, NOT A COIN FLIP — two events nothing can
         separate are refused and the pair is named, rather than ordered by
         arrival behind a deterministic-looking tiebreak; a source that can emit
         two events in one instant must supply a sequence, refused at the type.
         Three real defects the tests caught in my own engine: a retry was
         reported as AMBIGUOUS WITH ITSELF; a retry carrying the version its
         first attempt saw would have been REFUSED FOR BEING LATE (a delivery
         guarantee turned into an error); and the same source event claiming a
         SECOND GRADE came back as a duplicate, silently dropping it. The
         duplicate check now answers before every other check, and
         `grade_mismatch` — which the producibility probe showed had no producer
         at all — is now the refusal for the third.
         (d) FOUND AND FIXED IN M7.10: `propagate` folded `history.current()
         .sequence` into the payload digest, so a late event renumbering the
         chain changed the idempotency key of a value that had not changed —
         a rebuild after a late arrival would have written to LTI/OneRoster for
         a grade nobody touched. The sequence is this estate's position in a
         derived history, not something a downstream holds; it is out of the
         payload, with a probe on each direction (unchanged value → same key,
         changed value → different key) and M7.10's own 23 tests still green.
         `ReplayReport` refuses to be built if the outbox count moved across a
         rebuild, counted from a separate read, and ten rebuilds produce one
         digest.
         (e) the ledger feeds M7.10's visibility matrix rather than going round
         it: the same ledger shows a learner no value before release and their
         value after, and stale/unauthorized/unspoken-for appends are refused
         with every reason reported at once. -->
  - [x] M8.11.a Persist immutable grade/source event/correction IDs, exact
        learner/attempt/item/scale revisions, ordering keys, timestamps, source,
        and integrity. Evidence: event schema.
  - [x] M8.11.b Apply authorized corrections as append-only events with expected
        grade version, audit/outbox, and deterministic current projection.
        Evidence: correction tests.
  - [x] M8.11.c Deduplicate by source/idempotency and order/reconcile late,
        reordered, retried, corrected, voided, and replayed events. Evidence:
        event ordering tests.
  - [x] M8.11.d Rebuild projection/aggregates/LTI/OneRoster/credentials from
        event history without duplicating external writes. Evidence: replay
        tests.
  - [x] M8.11.e Preserve visibility/release windows/appeals/notifications and
        reject stale unauthorized mutation. Evidence: boundary tests.
- [x] M8.12 Implement integrity/appeal API integration without shadow records.
      Evidence: authoritative ID linkage.
      <!-- Verified 2026-09-06: schemas/themis_linkage.py 8.12.0 +
         services/themis_linkage.py + services/themis_boundary_gate.py +
         models/themis_linkage.py + alembic 054, and a one-line change to
         services/persistence_design.py; tests/test_themis_linkage.py 56 +
         tests/test_themis_linkage_real_database.py 18 (real PostgreSQL); ruff +
         mypy --strict clean; no new routes, OpenAPI unchanged.
         THE GATE FOUND A REAL SHADOW ON ITS FIRST RUN. Revision 044 created
         `appeal_workflow_integrity_cases` — a complete Metis-side integrity
         case, derived from M7.4's `IntegrityCase` contract, for a decision
         Themis owns. 044 and 045 are regenerated without it and 054 drops it
         from any database that already ran the old chain, with a real-DB test
         asserting it is absent after the whole chain runs. The deriver does not
         drop the module in silence: it emits a `DerivedShape` with a new
         `authoritative_elsewhere` reason for each of its aggregates, because A
         CONTRACT EXCLUDED WITHOUT A STATED REASON IS INDISTINGUISHABLE FROM ONE
         NOBODY LOOKED AT — my first attempt did drop it, and M8.1's own
         "designed or explained" test caught that. The only OpenAPI change in
         this cell is that one enum value; the schema and path sets are
         identical.
         (a) the linkage record is an authoritative kind + id + version, a Metis
         subject, and a delivery state. Its field tuple is asserted against a
         hand-written list, no field name anywhere in the contract may contain
         any of twelve content words (`rationale`, `finding`, `verdict`,
         `sanction`...), and a real-DB test reads `information_schema.columns`
         against a hand-written column list — because a shadow usually arrives
         as one more column on a table that already exists, not as a new table.
         (b) the seam is a Protocol with one method and no way to ask for a
         rationale: WHAT THIS SERVICE CANNOT ASK FOR, IT CANNOT STORE.
         Authorization carries actor/tenant/purpose/properties and is never
         taken from a body; linking is idempotent on the authoritative identity,
         and the same record pointing at a second subject is refused.
         (c) A DELETION IS NOT AN ABSENCE. `deleted` (Themis says it is gone),
         `forbidden` (it may exist and you may not see it) and `missing` (the
         read failed and said nothing) stay three answers, each with its own
         sentence, because a learner told "no case found" when the truth is "you
         are not allowed to see it" has been told something false. An
         unclassified seam failure raises rather than falling into `missing`.
         The projection links to an explanation and never copies one, and a
         blank status without a reason is unconstructible.
         (d) events are versioned and idempotent: newer applies, equal is a
         duplicate, older is a reordered delivery and does not wind the record
         back, a merge is followed and says where the case went, a withdrawal
         makes the outcome undeliverable, and a forbidden/missing report changes
         nothing because it is not news about the record. The outcome vocabulary
         is partitioned into those that record something and those that decline,
         checked at import — the first draft forbade a withdrawal from moving
         the version, which was wrong: "this is gone" arrives as a version like
         anything else.
         (e) A SHADOW IS DECIDED BY PROVENANCE, NOT BY ITS NAME. The gate's
         first draft matched table names against `appeal` and was wrong both
         ways: it flagged `item_exposure_appeals` (an appeal about an assessment
         item leaking, nothing to do with anyone's conduct) and no column-level
         rule could have caught the real shadow, whose columns are just
         `tenant_id, case_id, revision, content_json`. What separates them is
         which contract each table serves. The name net is kept only for tables
         no contract claims, which is the one case provenance cannot answer. All
         three surfaces — `Base.metadata` plus every `CREATE TABLE` in the
         migration history, the deriver's proposals, and imports reaching past a
         Themis public package — are arguments to the scanners, and each is run
         over a tree with a shadow planted in it, because M7.1's own lesson is
         that AN INVENTORY THAT FAILS ONLY ON DRIFT IS NOT A GATE. -->
  - [x] M8.12.a Store only Themis authoritative case/evidence/decision/appeal
        IDs/ versions plus minimal Metis subject linkage and delivery state.
        Evidence: linkage schema.
  - [x] M8.12.b Create/query/update through public Themis contracts with current
        actor/tenant/purpose/property authorization and idempotency. Evidence:
        contract tests.
  - [x] M8.12.c Project audience-safe status/explanation into Metis without
        copying raw evidence/decision authority. Evidence: projection tests.
  - [x] M8.12.d Consume versioned events idempotently and handle missing/
        forbidden/deleted/merged/corrected authoritative records. Evidence:
        event tests.
  - [x] M8.12.e Add schema/import guards proving no shadow integrity decision/
        appeal tables or private imports are introduced. Evidence: boundary
        gate.
- [x] M8.13 Implement permission-aware search and item-bank indexing. Evidence:
      hidden answer and protected integrity metadata do not leak.
      <!-- Verified 2026-09-06: schemas/search_index.py 8.13.0 +
         services/search_index.py; tests/test_search_index.py 92 tests; ruff +
         mypy --strict clean; no new routes, OpenAPI unchanged.
         (a) A FIELD REACHES THE INDEX BECAUSE IT WAS CLASSIFIED, NOT BECAUSE IT
         WAS PRESENT. `FIELD_CLASSES` is total over the eight kinds the item
         names; a payload field nobody classified is REFUSED rather than indexed
         or dropped, because both of those are somebody guessing. Every kind is
         checked at import to have something searchable and something forbidden
         — a kind that forbids nothing has not been reviewed, it has been
         skipped.
         (b) index events are ordered by source revision and never by arrival:
         an older one is ignored, a duplicate changes nothing, a quarantine
         withholds without forgetting, and a delete leaves a tombstone a late
         upsert cannot resurrect. Reconciliation finds all three populations,
         including the source with no document at all — the one a rebuild that
         only walks the index misses entirely. Exposure is a first-class field,
         not a display string, so the suppression rule cannot be fooled by what
         an author typed; a worn-out item is withheld from everyone but an
         admin.
         (c) filters, sorts, facets and pagination are allowlists, and the index
         only narrows: every surviving hit is authorized individually
         afterwards. A facet a caller cannot then filter on is refused at import
         as a dead end.
         (d) the protection is structural rather than a filter. A forbidden
         value never reaches the document, so it cannot appear in a snippet
         (cut from the searchable map), a facet, a count, an autocomplete
         suggestion (built from authorized documents only) or a log. A COUNT IS
         A LEAK: totals and facet buckets are computed from what survived
         authorization, and the type refuses a response whose facet counts more
         than its total. A QUERY LOG IS A PLACE PROTECTED TEXT ENDS UP —
         somebody pastes an answer key in to see whether it is findable — so the
         log keeps a digest and a term count and has no field text could go in.
         The residue scan runs over all eight kinds, searching the WHOLE
         serialized document rather than its declared fields, and a probe proves
         the scan can actually find a planted leak.
         (e) A NON-LEAK IS PROVED BY INDISTINGUISHABILITY, so the adversarial
         tests compare the ENTIRE response for a document that exists and is
         forbidden against the response for one that does not exist and require
         them equal — asserting "the answer key is not in the response" passes
         for a response that says "0 results (1 hidden)". Same for a guessed
         course id, a quarantined document, and two tenants holding the same
         entity id (a grant is scoped by the tenant that issued it, not by the
         id it names). Timing is not asserted with a clock, which would be
         flaky: THE TIMING ORACLE IS CLOSED BY CONSTRUCTION, NOT BY PADDING —
         nothing in the search path reads a protected value, so no branch's
         duration can depend on one, and a scan over the module's own source
         proves it never names a forbidden field. -->
  - [x] M8.13.a Define index documents per source/course/lesson/item/stimulus/
        rubric/objective/review with classified searchable/display-only/
        forbidden fields. Evidence: index schema.
  - [x] M8.13.b Build tenant/course/role/exposure-aware indexing events,
        version/freshness, deletion/quarantine, and rebuild reconciliation.
        Evidence: index lifecycle tests.
  - [x] M8.13.c Query with allowlisted filters/sorts/facets/autocomplete/
        pagination and server-side object/property authorization after hits.
        Evidence: search tests.
  - [x] M8.13.d Exclude answer/rationale/hidden tests/integrity evidence/learner
        data/protected sources from index, snippets, counts, spellcheck, and
        logs. Evidence: leak tests.
  - [x] M8.13.e Test guessed IDs/terms/timing/counts/stale docs and two-tenant
        collisions. Evidence: adversarial suite.
- [x] M8.14 Implement tenant-isolation tests for authoring, learner preview,
      search, export, gradebook, integrity, and analytics. Evidence: two-tenant
      suite.
      <!-- Every sub-item is now checked. This parent was briefly unchecked on
         the belief that learner preview lived in Studio; it does not, and .c
         records the correction and the work.
         Verified 2026-09-06: schemas/tenant_isolation.py 8.14.0 +
         services/tenant_isolation.py; tests/test_tenant_isolation.py 35 +
         tests/test_tenant_isolation_real_database.py 12 +
         tests/test_tenant_isolation_authoring_api.py 9 (the last two against
         real PostgreSQL); ruff + mypy --strict clean; no new routes or tables,
         OpenAPI unchanged.
         THE MEASUREMENT FIRST: `classify(Base.metadata)` walks every table and
         says how a row reaches a tenant. 102 by a tenant column, all with a
         tenant-leading index; 0 by inheritance through a foreign key (checked,
         not assumed); and 13 BY NOTHING AT ALL — `users`, `courses`, `modules`,
         `lessons`, `assessments`, `questions`, `submissions`, `enrollments`,
         `progress`, `achievements`, `tutoring_sessions`, `tutoring_messages`,
         `xapi_statements`. That is the original schema, written before this
         estate had tenants, and it holds the learners and the content. It is
         recorded as a baseline rather than quietly fixed: adding a tenant
         column to thirteen core tables is a migration, a backfill and a rewrite
         of every query that touches them, and doing that as a side effect of
         writing a test suite would be the wrong way round. The baseline is a
         RATCHET, tested in both directions — a new untenanted table fails, and
         so does a baseline entry that has since been tenanted, because
         otherwise the list rots into a permanent excuse.
         (a) THE ONLY FIXTURE THAT CAN FAIL IS A COLLIDING ONE. Both tenants use
         the same title, slug, logical id, source event id, Themis case id and
         learner reference; the only difference is one distinctive word each.
         With different ids the suite would pass against a service doing no
         scoping at all, because there would be nothing to confuse. A test
         asserts the colliding rows really do exist under both tenants, so the
         rest cannot be passing on an empty database.
         (b)/(e) the real-database half seeds three stores at once — the M8.11
         grade ledger, the M8.12 Themis linkage and the M8.3 aggregate store —
         so a leak crossing between them has somewhere to show up. Each tenant's
         reads return one row, not two, under an id both of them used; the two
         projections' digests DIFFER, because identical ones would mean the
         projection ignored something it read; and a third tenant that wrote
         nothing gets nothing, so "returns only mine" is not "returns
         everything". Every table is counted per tenant and the counts must be
         equal, non-zero, and leave nothing attributed to nobody.
         (c) STAYS UNCHECKED. The learner preview surface is Studio's, and
         `SURFACE_HOMES` records that rather than letting a Python suite imply
         it covered a renderer, an offline cache and a media pipeline it does
         not host. Naming it is the difference between "covered" and "nobody
         looked".
         (d) search is exercised over the colliding corpus: each tenant sees one
         hit, the facet counts one, autocomplete does not read the other
         tenant's words, and searching for the other tenant's distinctive term
         returns a response IDENTICAL to the one for a term that is not there.
         (f) all ten channels are enumerated and the sweep refuses to be built
         with one missing — a channel nobody enumerated is a channel nobody
         checked. Three evidence strings are turned into checks rather than left
         as sentences: no `set_attribute` call exists anywhere in the service
         (trace), no `@lru_cache`d function takes a tenant/learner/actor
         argument (cache), and `tenant_storage_key` is deterministic,
         non-reversible and distinct per tenant (blob). The database channel is
         reported as `keyed_with_recorded_exceptions` and NAMES ALL THIRTEEN —
         claiming `keyed_by_tenant` of a channel with thirteen exceptions would
         simply be false. -->
  - [x] M8.14.a Seed two tenants with colliding names/IDs-slugs/terms/times/
        sources/items/learners/roles plus distinct keys/policies/integrations.
        Evidence: isolation fixture.
  - [x] M8.14.b Exercise every authoring CRUD/revision/comment/review/build/
        provider/agent path with foreign IDs/cursors/links. Evidence: authoring
        tests.
        <!-- tests/test_tenant_isolation_authoring_api.py 9 tests over HTTP:
           all eight `/authoring` routes driven by two tenants holding the SAME
           logical id, so a route ignoring the tenant would still have a
           candidate row to return. Create, read, list, edit, new revision,
           transition, archive and restore; the listing counts one and not two,
           an edit is invisible to the other tenant, a transition moves one
           lifecycle and not the other (observable through the listing, because
           a read deliberately does not carry the state), an archive leaves the
           other tenant's listing alone, and a revision number only the other
           tenant has does not reach across. A CALLER CANNOT NAME ITS OWN
           TENANT: `_tenant_of` derives it from the authenticated user with a
           uuid5, and a test sends both an X-Tenant-Id header and a tenant_id
           query parameter and still gets its own row. The comment, review,
           build, provider and agent paths named in this item are Studio's, in
           TypeScript, and are not routes this service serves. -->
  - [x] M8.14.c Exercise learner preview/session/cache/offline/renderer/media/
        tutor synthetic and real-context boundaries. Evidence: preview tests.
        <!-- I FIRST RECORDED THIS AS OUT OF REACH AND WAS WRONG. The learner
           preview is not Studio's — it is `apps/metis/web`, in this repository:
           `src/app/create/lessons/[lessonId]/preview` with a delivery renderer,
           a runtime provider and a session media cache. Naming something out of
           reach without looking is the same mistake as claiming to have covered
           it.
           The tenancy boundary is the session media cache, and it holds: the
           key is `${documentSha256}:${contentSha256}`, both halves digests of
           the bytes themselves, so A SHARED CACHE KEY MEANS SHARED BYTES —
           two tenants can only collide when they hold byte-identical content,
           and the entry they share is what they both already have. A cache
           keyed by lesson id would be a cross-tenant leak inside one browser
           session; this one cannot be. The cache also lives in a `useRef` for
           one mounted preview and is released on unmount, so a second tenant's
           session starts empty — tested by asserting every object URL is
           revoked and the map cleared, because a cache outliving the page would
           leak whatever its key.
           Both properties are asserted in
           apps/metis/web/src/components/lesson-delivery/LessonDeliveryRuntime.spec.ts
           (4 vitest tests passing). M8.14.f's `cache_key` evidence is corrected
           accordingly: it previously said "every @lru_cache in this service",
           which was true and scoped to Python, while the estate's real cache
           risk is a TypeScript Map a Python scan cannot see. -->
  - [x] M8.14.d Exercise search/facets/counts/autocomplete/index lag and all
        standards/audit/content/evidence exports. Evidence: search/export tests.
  - [x] M8.14.e Exercise grade/event/correction/LTI, Themis case/appeal, and
        analytics/cohort/privacy projections. Evidence: protected data tests.
  - [x] M8.14.f Inspect API/UI/events/queue/blob/cache/log/trace/notification/
        timing channels for identifiers, content, existence, or counts.
        Evidence: two-tenant suite.
- [ ] M8.15 Generate OpenAPI and consumer contracts for Studio, learner,
      operator, and institutional clients. Evidence: drift gate.
      <!-- LEFT UNCHECKED behind .b and .d. What is done: schemas/api_contract.py
         8.15.0 + services/api_contract.py; tests/test_api_contract.py 53 tests;
         ruff + mypy --strict clean; no new routes, OpenAPI surface unchanged.
         AN UNCLASSIFIED OPERATION IS A FAILURE, NOT A DEFAULT — all 767 are
         classified, and asking why 35 declared no security requirement is how
         the unauthenticated draft-lesson leak was found and fixed (see
         fix(metis): stop serving unpublished lesson bodies to anonymous
         callers). A CONTRACT THAT OMITS A STATUS THE CODE RETURNS IS ONE A
         CONSUMER CANNOT HONOUR: 56 modules raise a status none of their
         operations document — 404 forty-five times, 409 forty, 403 twenty-one,
         503 five — computed by JOINING THE SOURCE TO THE DOCUMENT THROUGH THE
         ROUTER TABLE, because neither artefact answers it alone. Held by a
         ratchet in both directions, like M8.14's untenanted tables: a new gap
         fails, and a closed one must come off the list or it becomes folklore.
         Documenting all 56 is a change to ninety route modules' decorators; the
         honest thing one cell can do is measure it and hold the number. -->
  - [x] M8.15.a Inventory every route/operation/audience/consumer and canonical
        request/response/error/event/stream schema and owner. Evidence: API
        inventory.
        <!-- metis-api-surface.json: the canonical Metis API surface, consolidating
                                                                                                                                                                                                                                                                        committed metis-api-inventory + metis-route-inventory + a per-type breakdown
                                                                                                                                                                                                                                                                        of the OpenAPI component schemas (apps/metis/service/openapi/metis.openapi.json,
                                                                                                                                                                                                                                                                        read once). 181 operations across 8 route families, served to audience tiers
                                                                                                                                                                                                                                                                        (101 privileged / 44 open / 36 authenticated), consumed by 20 distinct API
                                                                                                                                                                                                                                                                        consumers. Canonical schemas: 535 total classified by schemaType() with
                                                                                                                                                                                                                                                                        error/event/stream priority over request/response — 405 response, 68 request,
                                                                                                                                                                                                                                                                        50 base, 8 event, 3 error, 1 stream. FINDING: the API is RESPONSE-DTO-HEAVY
                                                                                                                                                                                                                                                                        (405/535) with THIN error/event/stream schema typing (12 total) — a
                                                                                                                                                                                                                                                                        canonical-schema gap the M8.15.b OpenAPI drift gate should address. Single
                                                                                                                                                                                                                                                                        service owner (apps/metis/service). Fast (0.2s, committed-evidence reads).
                                                                                                                                                                                                                                                                        generate-metis-api-surface.mjs + 6 tests, wired into inventory/verify + CI.
                                                                                                                                                                                                                                                                        M8.15.b (OpenAPI generation + drift gate) is implementation and stays
                                                                                                                                                                                                                                                                        unchecked. -->
  - [ ] M8.15.b Generate OpenAPI with auth/scopes, discriminators, property
        redaction/absence, versions/units, pagination/idempotency/concurrency,
        examples, and stable errors. Evidence: schema validation.
        <!-- STILL UNCHECKED, but the two substantive halves are done.
           AUTH: every one of 767 operations is classified, the three course
           routes declare `security: [{HTTPBearer: []}, {}]` because a token
           changes their answer rather than gating the call, and the register
           carries a stated reason for each of the 32 needing none.
           STABLE ERRORS: this estate put two unrelated shapes on the wire —
           `{error, message, details}` from the handlers and FastAPI's bare
           `{detail}` from any route raising HTTPException — so a client parsing
           `error.message` got nothing from a 404. An HTTPException handler in
           main.py now adds `error` and `message` beside a verbatim `detail`.
           ADDITIVE ON PURPOSE: 47 assertions across 40 test files read
           `detail`, and rewriting the wire format is not something a
           documentation cell gets to do as a side effect. Verified by running
           all 675 API-contract tests — every one passed — and by a wire test
           asserting a real 403 carries all three keys. A status outside the
           code table gets a generic code rather than a fabricated one.
           Driving the contracts against the provider also found one of my own
           claims wrong: the authoring surface answers a refusal with a typed
           `AuthoringOutcome`, not an error envelope, so `studio.forbidden` is
           now recorded as a `payload` and not `structured_with_detail`.
           WHAT IS LEFT, and why this stays unchecked: no discriminators, no
           per-operation examples, and no unit annotations. Examples on 767
           operations is a separate piece of work, not a paragraph. -->
        <!-- SPEC 2026-09-11: what closes .b, exactly: (1) an OpenAPI
        `discriminator` on every tagged Pydantic union, derived from the models
        (M10.1.c records that untagged unions cannot exist); (2) one example per
        operation generated from the M8.15.c consumer-contract fixtures, never
        hand-written; (3) `x-unit` annotations on the 310 unit-bearing fields
        M10.1.c counted. Regenerate through `scripts/export_openapi.py`,
        semantic-diff the schema and path sets, keep `{detail}` beside `{error,
        message}`. -->
  - [x] M8.15.c Publish distinct Studio/learner/operator/institution consumer
        contracts for success/empty/partial/stale/degraded/not-configured/
        forbidden/failure cases. Evidence: contract fixtures.
        <!-- Four contracts, each total over the eight cases and unconstructible
           with one missing. A case this service cannot produce carries neither
           a status nor a body shape, so a silently absent case cannot read as a
           case that cannot happen. The four are distinct as whole documents,
           and a separate test asserts the AUDIENCE CHANGES THE WIRE AND NOT
           ONLY THE PROSE: `forbidden` is 403 for studio, operator and
           institution and 404 for a learner, because a 403 confirms the thing
           exists. Studio and institution share a status shape, which is honest
           — they are both privileged — so the distinctness test compares whole
           expectations rather than statuses alone. -->
  - [ ] M8.15.d Verify real provider implementation against every consumer and
        deployed compatibility window. Evidence: provider verification.
        <!-- LEFT UNCHECKED, but no longer prose: tests/test_provider_verification.py
           drives NINE of the thirty-two audience-and-case pairs against the
           running app and checks each against what the contract promised —
           studio success/empty/stale(409)/forbidden(403)/not_configured(503),
           learner success/empty/forbidden(404), operator success. A CONTRACT
           NOTHING EXERCISES IS A DOCUMENT, and the type refuses a verification
           whose observed status disagrees with the promise: a mismatch is a
           broken promise, not a result to file. It also refuses one with
           neither an observed status nor a reason, so A CASE NOBODY DROVE IS
           RECORDED AS NOT DRIVEN, NOT AS PASSING — the report is total over all
           thirty-two and each of the other twenty-three names why (eight of
           them because the standards exchange has no HTTP surface in this
           build, three because driving a 500 means breaking the service on
           purpose). `VERIFIED_FLOOR` is a floor, not a target: the suite
           asserts at least nine, so the number can only go up. Still unchecked
           because "every consumer" is thirty-two, not nine, and there is no
           deployed compatibility window in this repository to verify against. -->
        <!-- SPEC 2026-09-11: there is no deployment, so the "deployed
        compatibility window" is defined as the committed
        `openapi/metis.openapi.json` at the previous tagged release commit and
        at HEAD. Drive the remaining 23 audience/case pairs, recording each
        undrivable pair with its reason. The eight "standards exchange has no
        HTTP surface" pairs need a decision — add the surface (a new cell) or
        record a §2.1 N/A with a named approver; an agent drafts either, a
        person decides. -->
  - [x] M8.15.e Fail CI on unclassified operations, breaking schema/semantic/
        auth changes, or protected property exposure. Evidence: drift gate.
        <!-- services/api_contract.py: `drift_gate` diffs the committed document
           against a candidate and fails on an unclassified operation or any of
           eight breaking-change kinds. Every kind has a test that takes the real
           767-operation document, makes exactly ONE change to a copy, and
           requires that change to be classified — a gate nobody has seen fail is
           a gate nobody has tested, and a self-diff passing proves only that it
           is not noisy. PROTECTED PROPERTY EXPOSURE is the one that matters:
           a response growing an `answer_key` breaks no existing consumer, which
           is precisely why it needs a gate rather than a review, and it is
           parametrised over all twelve protected names with a control showing an
           ordinary new field is not a finding. -->

### M9 — Workbench UI, calendar, localization, catalog, and publication

- [x] M9.1 Build the Metis authoring hub around assigned work, recent courses,
      gate failures, review queue, and scheduled releases. Evidence:
      utility-first design review.
  - [x] M9.1.a Define audience-safe live view models, priority/ownership/
        freshness, actions, and navigation for every named hub region. Evidence:
        hub contract.
        <!-- Verified 2026-09-06: schemas/authoring_hub.py 9.1.0 +
           services/authoring_hub.py; tests/test_authoring_hub.py 62 tests; ruff
           + mypy --strict clean; no routes yet, so the OpenAPI surface is
           unchanged.
           One shape over all five regions, because five bespoke shapes would be
           five places to forget the count rule. A REGION'S COUNT IS A COUNT OF
           WHAT THIS VIEWER MAY SEE — M8.13's rule where it is most visible,
           since a hub's whole job is numbers and "14 gate failures" from a page
           that filtered eleven is a lie told in large type. A region carrying
           more cards than it counts is unconstructible, and an empty one counts
           nothing.
           AN ACTION A VIEWER MAY NOT TAKE IS ABSENT, NOT DISABLED: there is no
           `enabled` field to set false, `grants_for` returns only what this
           viewer may do, and the hub refuses a card offering an action its
           audience may never hold. Every mutating action carries its
           confirmation and whether it needs a reason ON THE GRANT, so the
           dialogue and the audit trail cannot disagree; `open` carries neither,
           because treating a look like a change trains people to click through.
           A card that can be acted on can also be opened — acting on something
           you cannot open is acting blind.
           PRIORITY IS DERIVED, NOT TYPED, from ownership, state and age. Work
           waiting on somebody else does not age into `due`, or another team's
           queue climbs to the top of this page; unassigned work is not late but
           is not somebody else's problem either. Every priority value is
           checked to be producible from some combination.
           A REGION THAT CANNOT SAY WHEN IT WAS COMPUTED SAYS SO: freshness is
           required, `unknown` is one of its values, and a confident freshness
           with no timestamp is unconstructible. `empty` (an answer with nothing
           in it), `offline` (no answer) and `loading` are kept apart, and a
           `ready` region with no cards is refused as an empty one — "nothing to
           do" and "still loading" are different sentences.
           Ordering is deterministic down to the card id, so the same corpus in
           a different order renders the same list; a hub that reshuffles
           between reloads is one nobody can work down.
           services/authoring_hub_sources.py wires four of the five regions to
           real tenant-scoped tables (store_aggregates joined to store_revisions,
           gate_job, the content-review signoffs, publication_saga_run) with
           tests/test_authoring_hub_sources_real_database.py 15 tests against a
           real PostgreSQL. A REGION WITH NO SCOPEABLE SOURCE IS OFFLINE, NOT
           EMPTY: `recent_courses` would read `courses`, one of the thirteen
           M8.14 found untenanted, so it returns `offline` with that reason in
           the message rather than claiming there are no recent courses.
           `assigned_work` is `degraded` for a smaller reason — the aggregate
           store records who revised a thing and nothing about who it is
           assigned to, so ownership means "last touched by" and the message
           says so instead of letting the page claim an assignment model the
           estate does not have. Pyright caught three readers taking a
           `viewer_id` they never read; the parameter is gone, because AN UNUSED
           PARAMETER IN A DECISION FUNCTION IS A SILENT STUB. Two tenants seeded
           with colliding ids across all four sources see one row each. -->
  - [x] M9.1.b Compose a restrained task-first workspace with assigned blockers
        primary, recent context/reviews/schedule secondary, and no decorative
        hero or repeated card grid. Evidence: responsive design review.
        <!-- apps/metis/admin/src/app/hub/{page.tsx,page.module.css}, served at
           /hub. A NEW surface: the existing `/` dashboard is a different page
           with its own specs (visual-rhythm asserts it is "Dashboard" with four
           stat cards) and was not replaced. Two unequal columns rather than a
           grid — 3fr primary against 2fr secondary — because a grid of equal
           tiles says every region matters the same amount, which is the
           opposite of this page's claim. The browser asserts it rather than a
           reviewer: the primary column's measured boundingBox is wider than the
           secondary's, and the assigned-work region is wider than the gate
           region. No hero and no images at all. -->
  - [x] M9.1.c Implement deterministic filters/sorts/counts/deep links/live
        updates and loading/empty/stale/degraded/offline states. Evidence:
        interaction tests.
        <!-- All six region states render a different sentence, asserted one by
           one in the browser; the one region with nothing to explain renders no
           message. AN OFFLINE REGION IS NOT AN EMPTY ONE — offline reports "age
           unknown" where empty reports its freshness. FILTERING NARROWS WHAT IS
           DRAWN AND LEAVES THE COUNT ALONE: with the blocking filter on, the
           assigned-work region shows zero cards and still reports 3, because a
           count that moved with the filter would be counting the page. Ordering
           is deterministic server-side down to the card id. Deep links carry
           the exact revision the card was built from. -->
  - [x] M9.1.d Gate inline claim/reassign/review/remediate actions by current
        role/state/version with reason/confirmation/audit. Evidence: action
        tests.
        <!-- AN ACTION A VIEWER MAY NOT TAKE IS NOT ON THE PAGE AT ALL. The
           server sends only the grants this viewer holds and the page renders
           only those: a card carrying just `open` has no buttons, and a browser
           assertion requires zero `button[disabled]` inside any card — a
           greyed-out control would still say the thing exists and that somebody
           else may move it. Every mutating action opens a confirmation carrying
           the server's own sentence, and one that requires a reason cannot be
           confirmed until one is typed (the confirm button is disabled, which
           is an incomplete form rather than a withheld action, and the test
           scopes the no-disabled rule to cards for exactly that reason).
           Cancelling records nothing. -->
  - [x] M9.1.e Verify keyboard/screen-reader/zoom/responsive and
        production-scale task/course/review volumes. Evidence: utility-first
        Playwright journey.
        <!-- e2e/authoring-hub.spec.ts, 20 tests, chromium, workers=1, all
           passing. Keyboard: tab reaches the next filter and Enter operates it.
           Focus is visibly ringed (computed `outlineStyle` is not `none`).
           Screen reader: the dialogue is a real `role="dialog"` with
           `aria-modal`, the filter group is a named `role="group"`, and the
           five regions are five level-two headings so the page is navigable by
           heading. Responsive: at 720px there is no horizontal overflow and the
           work still comes first — narrow does not mean reordered. Zoom: 200%
           at 1280 (i.e. 640 CSS pixels) keeps the primary region visible with
           no overflow. Production scale: a region of 20 drawn cards reporting
           4,137 visible renders without overflow and still says "Showing 20 of
           4137". The 503 case renders the alert and NOT half a page beside it.
           One locator bug of my own, found by the browser: `getByRole('alert')`
           also matches Next's route announcer, so the assertion is scoped to
           the page's own error element. -->
- [x] M9.2 Build inbox capture for sources, curriculum briefs, lesson ideas,
      item ideas, and feedback. Evidence: keyboard-first journey.
  - [x] M9.2.a Define one inbox item contract with typed capture kind, raw
        input, source/provenance, context, owner, status, validation, and
        conversion linkage. Evidence: inbox schema.
  - [x] M9.2.b Implement keyboard-first quick capture for each named kind with
        paste/upload/link, minimal required fields, shortcuts, autosave, and
        accessible feedback. Evidence: capture tests.
  - [x] M9.2.c Quarantine/scan/fence external content, protect learner feedback,
        label AI/imported sources, and prevent raw capture from publishing.
        Evidence: intake boundary tests.
  - [x] M9.2.d Triage/search/filter/assign/defer/archive/deduplicate and convert
        into canonical source/brief/lesson/item/feedback records as explicit new
        revisions. Evidence: conversion tests.
  - [x] M9.2.e Verify
        offline/reconnect/duplicate/conflict/permission/large-paste
        keyboard-only journey. Evidence: Playwright E2E.
- [x] M9.3 Build course and item workspaces using shared shell primitives.
      Evidence: route/selection/deep-link restoration.
  - [x] M9.3.a Define canonical routes and shared shell layout for navigator/
        primary editor/inspector/status/command palette across course and item
        workspaces. Evidence: IA contract.
  - [x] M9.3.b Reuse shared selection/revision/dirty/save/conflict/capability/
        permission state and avoid a second app shell or duplicated owner UI.
        Evidence: shell integration tests.
  - [x] M9.3.c Compose course hierarchy/dossier/content/coverage/review/build
        routes and item stem/response/key/rationale/accessibility/gate/use
        routes. Evidence: workspace tests.
  - [x] M9.3.d Persist/restore tenant/course/item/revision/selected
        entity/panel/ filter/focus through authorized URLs and
        back/forward/reload. Evidence: restoration tests.
  - [x] M9.3.e Verify direct link, deleted/stale/forbidden target, unsaved
        navigation, responsive modes, accessibility, and route ownership.
        Evidence: Playwright suite.
- [x] M9.4 Build source registry and grounding workspace. Evidence: unsourced
      high-risk claim block-to-clear journey.
  - [x] M9.4.a Build permission-aware source/extraction/claim/citation search
        and detail with
        authority/rights/currency/jurisdiction/audience/retraction state.
        Evidence: registry tests.
  - [x] M9.4.b Build extraction/original comparison, typed locator viewer,
        claim-span linking, source conflict, update/retraction, and version
        history. Evidence: grounding interaction tests.
  - [x] M9.4.c Build high-risk claim queue showing risk/source requirements,
        support/sufficiency/faithfulness/conflict gates, uncertainty, and
        reviewer role. Evidence: gate view tests.
  - [x] M9.4.d Implement attach/replace/corroborate/edit/abstain/request-review
        actions with exact revisions and blast-radius preview. Evidence:
        remediation tests.
  - [x] M9.4.e Run unsourced high-risk claim through authoritative citation,
        human review, cleared gate, rebuild, and stale-on-retraction journey.
        Evidence: Playwright E2E.
- [x] M9.5 Build objective, standard, concept, and prerequisite graph workspace
      with semantic list parity. Evidence: accessible graph journey.
  - [x] M9.5.a Build typed graph projection with stable objective/concept/
        standard nodes, prerequisite/coverage/alignment edges, revisions,
        provenance, validation, and permissions. Evidence: graph contract.
  - [x] M9.5.b Implement visual create/link/unlink/select/filter/layout/compare
        with no screen-position-as-data and explicit validation. Evidence: graph
        interaction tests.
  - [x] M9.5.c Provide synchronized tree/list/table/forms for every inspect/add/
        link/edit/delete/reorder/navigation operation. Evidence: semantic parity
        tests.
  - [x] M9.5.d Surface cycles/orphans/unreachable/missing prerequisites/
        standards drift/coverage impacts and non-destructive remediation.
        Evidence: validation tests.
  - [x] M9.5.e Verify keyboard/screen reader/non-drag/zoom/responsive/large
        graph and direct authorization journey. Evidence: accessible E2E.
- [x] M9.6 Build psychometric and DIF review workspace. Evidence: raw sample,
      uncertainty, policy, and cohort privacy are visible.
  - [x] M9.6.a Build permission-filtered item/calibration/run queue/detail with
        exact sample/method/model/tool/item versions and eligibility status.
        Evidence: projection tests.
  - [x] M9.6.b Present raw CTT/IRT parameters, standard errors, fit/information,
        sample/data quality, exposure, and declared-versus-empirical comparison.
        Evidence: measurement view tests.
  - [x] M9.6.c Present DIF method/reference-focal cohorts/effect/significance/
        uncertainty/sample counts with privacy suppression and no individual
        inference. Evidence: DIF view tests.
  - [x] M9.6.d Show thresholds/policy/verdicts/manual bands/disagreement/
        unavailable state separately from raw results. Evidence: state tests.
  - [x] M9.6.e Implement request-rerun/promote/restrict/revise/retire/review
        decisions with role separation and immutable evidence. Evidence: review
        journey.
- [x] M9.7 Build integrity and appeals deep links without duplicating
      `/operator/metis`. Evidence: role-directed navigation.
  - [x] M9.7.a Define allowed link intents from
        course/item/attempt/grade/support contexts to authoritative
        Themis/operator case/appeal views and safe return paths. Evidence: route
        map.
  - [x] M9.7.b Issue/validate signed tenant/subject/context/intent/expiry/nonce
        links and reauthorize current actor/object/function at destination.
        Evidence: deep-link tests.
  - [x] M9.7.c Show only audience-safe status/explanation/next action in Metis;
        keep raw evidence/adjudication/appeal decisions in owner surface.
        Evidence: property boundary tests.
  - [x] M9.7.d Route learner/teacher/integrity reviewer/operator actors to their
        permitted views and handle logged-out/expired/deleted/forbidden cases.
        Evidence: role navigation tests.
  - [x] M9.7.e Scan Metis routes/components/APIs for duplicated integrity
        mutation UI/private contracts. Evidence: ownership gate.
- [x] M9.8 Build editorial calendar for course launches, assessment windows,
      standard updates, item refresh, and review capacity. Evidence: time-zone
      and committed-slot tests.
  - [x] M9.8.a Define versioned calendar event/resource/reservation/dependency
        contracts for each named event type with owner/state/timezone/window/
        recurrence/visibility. Evidence: calendar schema.
  - [x] M9.8.b Build month/week/list/timeline views, filters, accessible
        non-drag create/move/resize, conflict indicators, and role-filtered
        details. Evidence: calendar UI tests.
  - [x] M9.8.c Model launch/assessment embargo/deadline, standards effective
        dates, source/item freshness, and dependency DAG/backward planning.
        Evidence: scheduling tests.
  - [x] M9.8.d Reserve named reviewer/approver/localizer/publisher and system
        build/provider/LMS capacity; reject overbooked committed slots.
        Evidence: capacity tests.
  - [x] M9.8.e Test IANA time zones/DST/all-day/locale/recurrence/quiet hours/
        missed dependency/committed slot concurrency. Evidence: calendar suite.
- [x] M9.9 Build taxonomy registry for subject, discipline, objective, standard,
      credential, modality, level, and audience. Evidence: impact preview.
  - [x] M9.9.a Define versioned typed entries/relations/aliases/locales/
        ownership/lifecycle and code-owned floors per named taxonomy. Evidence:
        registry schemas.
  - [x] M9.9.b Build permissioned author/edit/diff/review/publish/deprecate/
        supersede UI with uniqueness/cycle/collision validation. Evidence:
        lifecycle tests.
  - [x] M9.9.c Show exact use across courses/items/objectives/standards/builds/
        credentials/search/integrations before change. Evidence: impact query.
  - [x] M9.9.d Preview alias/deprecation/migration effects, conflicts/loss and
        require versioned approval; preserve historical codes. Evidence: impact
        preview tests.
  - [x] M9.9.e Verify consumer fallback/unknown behavior and cross-registry
        drift without collapsing distinct taxonomies. Evidence: registry E2E.
- [x] M9.10 Build localization workflow with translation memory, glossary,
      MathML, media, accessibility, and parity gates. Evidence: RTL and long
      locale journey.
  - [x] M9.10.a Define locale-variant job/unit/source/target/status/translator/
        reviewer/version contracts and immutable source-revision binding.
        Evidence: localization schema.
  - [x] M9.10.b Integrate permissioned translation memory and course glossary
        suggestions with source/version/match/rights and human acceptance.
        Evidence: TM/glossary tests.
  - [x] M9.10.c Localize structured text, bidi, MathML, SSML, media text/tracks/
        voice, descriptions, interaction labels, answers/rubrics, and metadata
        without breaking stable IDs. Evidence: content tests.
  - [x] M9.10.d Run source/citation/meaning/key/scoring/terminology/layout/
        accessibility/media-sync/standards-export parity gates per locale.
        Evidence: parity suite.
  - [x] M9.10.e Manage source-change invalidation, translation diff/review/
        approval/build and prohibit silent fallback in assessed content.
        Evidence: lifecycle tests.
  - [x] M9.10.f Run RTL/long/CJK/plural/date-number/unit/voice/caption/offline
        learner-render journey. Evidence: locale E2E.
- [x] M9.11 Build course/item catalog curation via reviewed bundles. Evidence:
      no direct production writes.
  - [x] M9.11.a Define versioned catalog bundle with exact course/item/build
        revisions, audience/locale/metadata/order/collections/schedule/targets,
        owner, and manifest hash. Evidence: bundle schema.
  - [x] M9.11.b Build draft add/remove/reorder/group/feature metadata editor
        with eligibility, rights/gates/reviews/credential/integration checks.
        Evidence: curation tests.
  - [x] M9.11.c Preview deterministic catalog/search/learner-facing diffs,
        removals, dependencies, visibility, and affected links. Evidence:
        preview tests.
  - [x] M9.11.d Require separate review/approval and publish through idempotent
        release API; prohibit catalog database/direct target mutation. Evidence:
        boundary tests.
  - [x] M9.11.e Preserve released versions and propagate supersession/unpublish/
        source withdrawal/recall explicitly. Evidence: lifecycle E2E.
- [x] M9.12 Build publication candidate, approval, schedule, execute, and
      verified-in-target views. Evidence: failed LMS sync cannot appear
      published.
  - [x] M9.12.a Build candidate manifest view with exact
        content/build/standards/
        locale/accessibility/gates/reviews/rights/integration versions and hash.
        Evidence: candidate tests.
  - [x] M9.12.b Build separate dimension approvals bound to candidate digest,
        reviewer roles/quorum/conflicts/conditions/freshness. Evidence: approval
        tests.
  - [x] M9.12.c Build schedule/dependency/capacity/embargo/timezone/target
        preview and confirmation against the unchanged candidate. Evidence:
        schedule tests.
  - [x] M9.12.d Build live per-target saga progress/receipts/partial/failure/
        retry/compensation with immutable attempts. Evidence: execution tests.
  - [x] M9.12.e Show published only after authoritative LMS/catalog/store read-
        back verifies bytes/metadata/visibility/links; expose failed/unknown
        honestly. Evidence: verified-target E2E.
- [x] M9.13 Build QTI/LMS export diagnostics with per-item incompatibilities.
      Evidence: no silent downgrade.
  - [x] M9.13.a Define diagnostic model per package/assessment/section/item/
        stimulus/rubric/media field with standard/profile/version, severity,
        support/loss/extension, locator, and remediation. Evidence: schema.
  - [x] M9.13.b Run preflight mappings/validators/target capability probe on
        exact revisions before export. Evidence: preflight tests.
  - [x] M9.13.c Present per-item unsupported interaction/scoring/accessibility/
        media/locale/metadata and aggregate counts without hiding failures.
        Evidence: diagnostics UI tests.
  - [x] M9.13.d Allow explicit supported substitution/exclusion only through
        reviewed new build revision; never silently downgrade. Evidence:
        substitution tests.
  - [x] M9.13.e Link output manifest/loss report/import/read-back evidence to
        diagnostics and block incompatible targets. Evidence: export E2E.
- [x] M9.14 Build command palette and Studio IA registration. Evidence: route
      integrity and permission filtering.
  - [x] M9.14.a Register Metis routes/actions/entities/keywords/shortcuts
        through canonical Studio IA with stable IDs/audiences/capabilities.
        Evidence: registration tests.
  - [x] M9.14.b Build permission/capability/context-filtered navigation and
        typed non-destructive commands with current selection/revision.
        Evidence: palette tests.
  - [x] M9.14.c Exclude unauthorized/disabled/operator/learner/protected-answer
        commands from discovery and execution server-side. Evidence: auth tests.
  - [x] M9.14.d Implement keyboard search/grouping/help/focus return and
        localized accessible labels without shortcut conflicts. Evidence:
        accessibility tests.
  - [x] M9.14.e Verify every entry resolves to an owned canonical route/action
        and stale/deprecated registrations fail CI. Evidence: route integrity
        gate.
- [x] M9.15 Build responsive reviewer mode for tablet/mobile without exposing
      answer keys to unauthorized contexts. Evidence: viewport and auth tests.
  - [x] M9.15.a Define mobile review scope/layout for subject identity, preview,
        sources/gates/diffs/comments, decision, and escalation; exclude complex
        authoring. Evidence: responsive spec.
  - [x] M9.15.b Build media/content/item/rubric semantic inspection and anchored
        comment flows with stable revision context. Evidence: mobile tests.
  - [x] M9.15.c Project answer/key/rationale/integrity/learner fields by current
        role/purpose and remove them from payloads/DOM/cache, not CSS alone.
        Evidence: property tests.
  - [x] M9.15.d Implement approve/reject/request-changes/abstain only for
        eligible roles/states with evidence, reason, confirmation, and stale
        conflicts. Evidence: decision tests.
  - [x] M9.15.e Verify touch/keyboard/screen
        reader/zoom/orientation/slow-offline/ direct URL/tier-change viewports.
        Evidence: responsive auth E2E.
- [x] M9.16 Build all loading, empty, unauthorized, stale, offline, conflict,
      not-configured, degraded, and failed states. Evidence: UI state matrix.
  - [x] M9.16.a Define canonical state discriminators, safe messages, available/
        forbidden actions, timestamps, recovery, announcements, and telemetry
        for each named state. Evidence: state contract.
  - [x] M9.16.b Implement route/list/detail/editor/preview/media/job/review/
        publication/integration variants without fabricated data or false green.
        Evidence: component matrix.
  - [x] M9.16.c Distinguish unauthorized from authenticated-empty safely, stale
        from current, offline cache from live, conflict from failure, and
        degraded from not-configured. Evidence: semantic tests.
  - [x] M9.16.d Preserve user input/focus/context where safe through retry,
        reconnect, reauth, refresh, conflict resolution, and service recovery.
        Evidence: recovery tests.
  - [x] M9.16.e Exercise every critical route/state/theme/viewport/locale with
        accessible announcements and permission assertions. Evidence: UI state
        Playwright matrix.
- [x] M9.17 Build lecture/media production, render, critic, accessibility, and
      rendition workspaces with per-segment status and replacement. Evidence:
      authoring-to-real-learner-renderer journey.
  - [x] M9.17.a Build lecture/media project list/detail with script/segment/
        artifact/rendition versions, source/objective links, owner, cost, gates,
        review, and package status. Evidence: workspace contract.
  - [x] M9.17.b Build per-segment plan/spec/code/data/media/presenter/consent/
        render job timeline with progress/logs/outputs/failures/cancel/retry.
        Evidence: production tests. Cancel and retry are version-fenced tickets
        naming the exact request against the service that owns the production;
        this surface reads productions and does not write them.
  - [x] M9.17.c Build synchronized media preview and visual/layout/correctness/
        source/accessibility/technical critic findings with attributed versions
        and educator decisions. Evidence: critic workflow tests.
  - [x] M9.17.d Build captions/transcript/audio description/sign/translation/
        pronunciation and rendition status/diff/review across locales/targets.
        Evidence: accessibility/rendition tests.
  - [x] M9.17.e Build surgical replacement impact preview, regenerate/edit,
        unchanged identity proof, invalidated gates/approvals, and new package
        comparison. Evidence: replacement tests.
  - [x] M9.17.f Run lesson-to-media-to-reviewed-package-to-real learner renderer
        with failure/cancel/consent/source withdrawal/accessibility cases.
        Evidence: Playwright E2E — 18 admin walks plus a member-web walk of the
        same package through `LecturePackageRenderer`, served from a playback
        fixture the service generates from the production the workspace judged.
- [x] M9.18 Build credential template, pathway, candidate evidence, review,
      issue, verify, expire, revoke, replace, and audit views with issuer-role
      boundaries. Evidence: Open Badges verification and learner-privacy tests.
  - [x] M9.18.a Build template/pathway authoring with criteria/evidence/
        achievement/alignment/issuer/validity/locale/version/diff/lifecycle and
        floor validation. Evidence: authoring tests.
  - [x] M9.18.b Build candidate queue/detail with privacy-minimized subject,
        per-criterion exact evidence/current/missing/stale/conflict state,
        eligibility calculation, and requester. Evidence: candidate tests.
  - [x] M9.18.c Build independent reviewer/issuer
        approve/reject/request-evidence decisions with
        authority/conflict/quorum/step-up and locked subject. Evidence: review
        tests.
  - [x] M9.18.d Build issue/sign/deliver/receipt and public/private verifier
        views for signature/issuer/recipient/evidence/status with privacy-safe
        results. Evidence: Open Badges verification tests. The signed document
        itself is the estate's existing OB 3.0 assertion builder; what this
        cell adds is the durable status a verifier reads and the audience
        projection around it.
  - [x] M9.18.e Build expiry/renew/revoke/replace/supersede status/impact/
        notification and immutable lineage/audit. Evidence: lifecycle tests.
  - [x] M9.18.f Prove authors/course completion/agents cannot issue or revoke,
        and verify learner/guardian/public/institution field boundaries.
        Evidence: authority/privacy E2E.
- [x] M9.19 Build institutional integration operations for roster sync, deep
      links, grade services, exports, webhooks, credentials, failures, replay,
      and reconciliation without duplicating operator incident tooling.
      Evidence: role-directed, live-status journey.
  - [x] M9.19.a Build institution-scoped connection/status/capability/config/
        credential-ref/version/owner views for LTI, OneRoster, LMS, LRS,
        webhook, and credential integrations. Evidence: connection tests.
        Capabilities are probed with a shelf life; an unprobed one is unknown,
        never supported, and the status is derived from the runs rather than
        from the connection's own summary columns.
  - [x] M9.19.b Build roster sync and deep-link/launch operation views with
        per-run/resource progress, mappings, changes, conflicts, diagnostics,
        and receipts. Evidence: roster/link tests.
  - [x] M9.19.c Build grade service/current-correction/outbound status and
        exports/webhooks/credential event delivery views with privacy/redaction/
        idempotency. Evidence: data-operation tests.
  - [x] M9.19.d Build failure/partial/unknown/retry/replay/reconcile controls
        with authoritative read-back, impact, expected version, role, and audit.
        Evidence: recovery tests. This surface plans and enqueues; the delivery
        itself is the integration worker's, and a recovery answers 202 with a
        queued operation and no receipt rather than claiming a far side it has
        not reached.
  - [x] M9.19.e Route platform security/outage/incident/drain/kill operations to
        operator tools through governed links; do not duplicate them here.
        Evidence: ownership tests.
  - [x] M9.19.f Run institution admin/teacher/operator journeys across live
        success, version skew, duplicate/late event, outage, recovery, and
        tenant isolation. Evidence: role-directed E2E.

### M10 — Metis verification, operations, and launch

<!-- SPEC 2026-09-11: M10 is bound by §2.5. Journeys name the host per stage
(§2.5.1), contain no `page.route` (§2.5.2), run against the venv +
`METIS_TEST_DATABASE_URL` stack (§2.5.3), bind evaluators to OpenRouter
(§2.5.4), and treat M10.20 as human-only (§2.5.9). The JWT role model is three
values — `student` (the default when the claim is absent), `instructor`, `admin`
(`deps.py` `CurrentUser`) — so any cell that separates
reviewer/decider/appeal/grade roles (M10.11, M10.12, M10.22) separates them by
actor id through the M7 assignment, conflict-of-interest and signoff records,
never by JWT role alone. -->

- [x] M10.1 Run contract tests for every canonical and workbench schema.
      Evidence: no unreviewed snapshot updates.
  - [x] M10.1.a Generate the complete canonical/workbench entity/API/event/job/
        bundle/standard schema inventory with owner/version/consumer/test.
        Evidence: coverage manifest — `openapi/schema-inventory.json`, 3,907
        models across 258 modules, generated by importing every schema, route
        and service module rather than declared by hand.
  - [x] M10.1.b Test public parse/validation/serialization/round-trip for
        minimal, full, locale/accessibility, protected-field, unknown/invalid
        fixtures. Evidence: contract suites. The estate-wide suite covers
        minimal, round-trip and unknown-field for the 2,560 models a minimal
        instance can be synthesized for, with the declines and refusals
        counted; full, locale, accessibility and protected-field fixtures are
        the per-cell contract modules', and the manifest records which models
        have one.
  - [x] M10.1.c Test cross-package/API/database/standards adapters for semantic
        field/version/unit/optional/discriminator compatibility. Evidence:
        adapter tests. Database: every mapped model's table is created by a
        migration, read from the migration's syntax tree. Units: 310
        unit-bearing fields hold quantities. Optional: required-field sets are
        recorded per model so a diff shows a breaking addition. Discriminator:
        Pydantic refuses a union with an untagged member at class-definition
        time, so the guarantee is recorded rather than duplicated.
        Cross-package is `pnpm --filter @oshun/codegen run openapi:types:check`.
  - [x] M10.1.d Fail missing/duplicate/unowned/unmapped schemas and require
        reviewed rationale/diff for every snapshot/golden update. Evidence:
        contract gate. Hard failures: a published name declared twice, a
        component FastAPI had to qualify, a component with no model, a model
        with no migration. Measured ratchets that may not grow: unowned,
        permissive, untested, unconsumed, unmapped tables. The manifest drift
        gate is what forces the reviewed diff.
- [x] M10.2 Run known-value tests for rubric, readability, coverage, item
      quality, IRT, BKT, FSRS-5, DIF, exposure, and selection logic. Evidence:
      function-to-test coverage index.
  - [x] M10.2.a Inventory exact functions/versions/units/applicability and
        create independent positive/negative/boundary/N/A/error fixtures per
        named algorithm. Evidence: function-to-test index —
        `metis/algorithm_registry.py`, eleven algorithms each with its
        reference, units and applicability. Eight are checked by a scalar
        table with all five categories; three read a domain object too large
        for one and name the tests that carry their exact answers, which the
        index verifies exist.
  - [x] M10.2.b Verify rubric arithmetic, readability/complexity, objective/
        construct coverage, item clarity/distractor/key/rationale rules, and
        exposure thresholds. Evidence: deterministic suites. Flesch and the
        exposure thresholds are point cases derived from the published
        coefficients; the rubric's weighted score, its N/A reweighting and its
        half-up rounding are indexed to the four tests that already prove them.
  - [x] M10.2.c Verify 1PL/2PL/3PL parameters/fit/SE, BKT updates, FSRS-5
        scheduling, DIF methods/privacy floors, and calibration eligibility.
        Evidence: psychometric goldens. BKT, FSRS-5 and the 3PL information
        function are point cases with the arithmetic written beside each
        expectation. DIF is an iterative maximum-likelihood fit with no closed
        form to compare against, so it is registered as property-checked and
        indexed to the recovery, Holm-family, privacy-floor and
        revision-conflict tests rather than given a second implementation of
        its own iteration to agree with.
  - [x] M10.2.d Verify pool/blueprint/adaptive selection constraints,
        tie-breaks, seeds, eligibility reasons, and no silent relaxation.
        Evidence: selector goldens — indexed to the six assembly tests that
        cover the order-independent golden, the blueprint coverage, the seeded
        tie-break, the unrelaxed enemy and stimulus constraints, the infeasible
        blueprint and the fail-closed pool eligibility.
  - [x] M10.2.e Fail coverage for any active algorithm/version without current
        fixture/tolerance/source derivation. Evidence: coverage gate. Every
        algorithm pins a digest of its own logic, taken from the syntax tree
        with docstrings stripped, so a reformat changes nothing and an altered
        branch or constant fails the gate until the values are re-derived. A
        tolerance without a reason, a case with no derivation, a category with
        no case and an algorithm with nothing checking it each fail on their
        own. Both halves were verified by mutation: changing `FSRS_FACTOR` from
        19/81 to 19/80 fails five known values, and wrapping a return in a
        `min` that changes nothing for valid inputs fails only the pin.
- [ ] M10.3 Run calibrated evaluation for AI item writing, bias assist, tutor,
      and integrity classifiers. Evidence: threshold and subgroup report.
      <!-- SPEC 2026-09-11: §2.5.4 binding and §2.5.5 corpus honesty apply. The
      four evaluators are the item-variant provider
      (`services/item_variant_provider.py`), bias/cultural review
      (`services/cultural_review*`), tutor preview evaluation
      (`services/tutor_preview_evaluation.py`) and the integrity signal
      classifiers; every run records the exact `METIS_*_MODEL` slug,
      `OPENROUTER_PROVIDER_SORT`, the provider that served it (from the usage
      payload) and the prompt version. M10.3.a's licensed/consented
      representative corpus cannot be produced by an agent: build the synthetic
      manifest, label it synthetic and un-adjudicated, and leave .a and the
      parent open. .b–.e are earnable on the synthetic corpus with that
      limitation stated in the report. -->
      <!-- STILL UNCHECKED — .a cannot be earned by an agent (the SPEC note
         above: a licensed/consented representative corpus). .b–.d are to be
         earned on a synthetic, un-adjudicated corpus with that limitation
         stated; .e is built and today refuses all four evaluators. -->
  - [ ] M10.3.a Define versioned licensed/consented representative held-out
        corpora, labels/rubrics, annotator/adjudication process, slices,
        privacy, and contamination controls for each evaluator. Evidence: corpus
        manifests.
        <!-- STILL UNCHECKED — the synthetic manifests the SPEC note asks for
           exist (`data/m10_3_*_corpus.json`: synthetic, single-labelled by
           "fixture construction", consent synthetic); the representative one
           still needs a person. BLOCKED on a corpus nobody has assembled. The
           manifest contract exists (`schemas/evaluation_corpus.py`: per-item
           licence, consent basis, annotators/adjudicator, declared slices,
           content digests for contamination) and refuses what the estate has.
           No held-out set exists for any of the four evaluators. The nearest
           in-repo corpora (m6_8/m6_17 for the tutor, m5_24/m5_25 for item
           writing) test deterministic engines around those AIs, not the AIs
           themselves. Each was written and labelled once by the session that
           built the engine it checks. None of the 26 corpus files records a
           sampling frame, annotators, adjudication or consent, and a test
           pins that. Bias assist and integrity classification have no corpus
           at all. Sourcing, consent and annotators are human work. -->
  - [x] M10.3.b Run exact model/prompt/config/tool versions and retain per-item
        proposals/scores/labels/reasons/uncertainty/abstention/errors. Evidence:
        reproducible runs.
        Evidence: four runs kept whole in
        `evidence/v1-workbenches/metis-evaluation/<evaluator>-run.json`. They
        come from `scripts/run_m10_3_evaluations.py`, `services/evaluation_runs.py`
        and the synthetic manifests `data/m10_3_*_corpus.json`.
        RE-MEASURED 2026-09-11: only the item-variant provider calls a model.
        - `cultural_review*` is human quorum logic, so the bias assist measured is
          `lesson_inclusivity_helpers`.
        - The tutor preview is canned text.
        - Integrity is keyword rules.
        What each run records:
        - The model run: the dated slug, `price` sort, reasoning `disabled`, and
          the prompt and config digests. Per item it keeps the served
          `model@provider`, cost, latency and the whole proposal.
        - Deterministic runs are versioned by the digest of their deciding code.
        Reproducibility, all tested:
        - deterministic runs re-run byte-for-byte;
        - every item-writing judgement re-derives from its retained proposal;
        - all four round-trip through the M10.3.e store on a real database.
        The first real run is kept
        (`item_writing-run-20260911T163341Z-prompt-v1-all-malformed.json`): all
        24 answers came back malformed. The prompt named an internal class
        instead of its fields, and the reasoning model spent all 1,500 output
        tokens thinking. Both are fixed (prompt v2, `METIS_ITEM_VARIANT_MODEL_REASONING`),
        and a `length` finish is now reported as a truncation. Every manifest
        states it is synthetic.
  - [x] M10.3.c Report task-appropriate correctness/validity/harm/precision/
        recall/FPR/FNR/calibration plus subgroup/language/discipline/item-kind
        slices with confidence intervals. Evidence: reports.
        Evidence: `<evaluator>-report.json` and `REPORT.md`, computed by
        `services/evaluation_reports.py` from the committed runs and manifests
        only.
        - Item writing: validity 21/24 = 0.88 [0.69, 0.96], with one key changed
          and one given away. An earlier run on the same route scored 18/23.
        - Bias assist: recall 12/30, FPR 6/12, ECE 0.31 over its 18 flags. It
          missed every paraphrase and flagged every refuting quotation.
        - Tutor preview: 46/46.
        - Integrity: recall 4/22 = 0.18 [0.07, 0.39]. All six verbatim copies of
          the product's own tutor lines passed, as did all eight model-written
          answers.
        Slices:
        - item writing: discipline, locale (en/es), item kind, pressure,
          transformation;
        - bias: category, phrasing;
        - integrity: action, length, URL;
        - tutor: dimension.
        Every slice has its size and a Wilson interval, and all are under 20
        items, so each is marked as unable to carry a conclusion. Calibration
        is reported only where a confidence is stated. The gate refuses all four.
  - [ ] M10.3.d Select fail/review/pass thresholds against ratified criteria,
        validate held-out performance, and document limitations/manual bands.
        Evidence: threshold decisions.
        <!-- STILL UNCHECKED — BLOCKED on ratification, which is a person's
           act (§2.5.9). The readiness packet is ready with the approver blank:
           `evidence/v1-workbenches/metis-evaluation/thresholds-readiness.json`,
           from `services/evaluation_thresholds.py`.
           - It proposes criteria.
           - It chooses bands on a fixed 60/40 split, stratified by label (by
             dimension for the tutor), and checks them on the held-out 40%.
           - It proposes an automatic band only when that band's precision lower
             bound clears the floor on the selection half.
           No band earns one today. The item writer and tutor preview state no
           confidence, and the bias and integrity rules' calls never reach a
           0.90 lower bound. So the manual band is 100% for all four, and the
           packet says why. The gate already refuses criteria ratified after
           the run began, a decision made under other criteria or before the run
           ended, and a review-band cost the run does not reproduce. -->
  - [x] M10.3.e Define drift/champion-challenger/recalibration triggers and
        block launch on unresolved material subgroup or safety failure.
        Evidence: evaluation gate.
        Evidence: `services/evaluation_gate.py`, with 72 tests in
        `tests/test_evaluation_gate.py`. The gate is given a corpus and a run
        and measures them itself. It takes no report, so a caller cannot hand
        it figures. What it refuses:
        - a missing or late ratification, or a decision made under other
          criteria or against another run;
        - a serving version different from the one measured;
        - a convenience or synthetic sample, or more than 10% single-annotated
          labels;
        - contamination, or skipped, foreign or errored items;
        - a floor missed on the interval's lower bound;
        - any safety finding (there is no way to accept one);
        - any material subgroup gap nobody answered for in this run.
        Slices too small, or holding too few positives to compare a miss rate,
        are marked unchecked. So are runs that mostly abstain. A pass carrying
        any of these cannot be constructed.
        `drift()` calls for recalibration on movement past the trigger or age
        past the limit. It refuses comparisons across corpora or against the
        wrong champion, and flags a challenger that is better on average but
        worse for one slice. `promotion_refusals` blocks on any regression.
        `assert_evaluators_may_launch` raises unless all four evaluators pass,
        and one fully passing launch is tested.
        The evidence is durable (migration 069, `services/evaluation_store.py`,
        13 real-database tests). Foreign keys carry the id and the digest, so a
        run of changed labels, or a decision on a run that never happened,
        cannot be written. M8.4's trigger refuses UPDATE and DELETE, every read
        re-checks the digest, and `evidence_for_decision` feeds the gate from
        stored records.
        Today it refuses all four: no corpus or run exists (see .a).
        NOT WIRED INTO SERVING: nothing in the product calls the gate. The
        tutor answers learners through its OpenRouter model with no verdict
        behind it, and Metis has no launch switch to put the gate in. Whether
        the tutor should stop serving until it passes is a product decision.
- [ ] M10.4 Run real-database and object-store integration tests. Evidence:
      source-to-course durable round-trip.
      <!-- SPEC 2026-09-11: harness = the §2.5.3 stack. Two tenants seeded with
      colliding ids following `tests/test_tenant_isolation_real_database.py`;
      object store = MinIO from `docker/docker-compose.dev.yml` (port 9000,
      `METIS_S3_ENDPOINT`); queue/outbox = what the service actually runs
      (Redis/Celery). Where no search index exists for an entity, the cell
      records "no index" rather than asserting one. Every evidence block reports
      passed/skipped/failed counts; a run whose real-database tests were skipped
      is a failed run. -->
      OPEN 2026-09-11, with all five children closed. The durable round-trip
      runs through `services/source_pipeline.py`, and no HTTP route calls it.
      The product's source ingestion routes, and about 20 other services,
      still write `source_ingestion_storage_dir` on local disk (M8.1's gap
      map's file store). So the path a user's source takes is not the one
      proven durable. Closing this needs those routes cut over to the
      pipeline. `services/durable_reconciler.py` also has no scheduled caller.
      Cells: claim, citation and release have no index, and the relay
      reports them `not_indexed`.
      RE-VERIFIED 2026-09-12 AND THE BLOCKER HOLDS: no module under
      `src/metis/api/` mentions `source_pipeline`, and nothing outside
      `durable_reconciler.py` mentions the reconciler, so neither has gained
      a caller.
      THE CUTOVER IS LARGER THAN "ABOUT 20". 68 modules under
      `src/metis/services/` reference `source_ingestion_storage_dir` and 65
      of those also contain a filesystem write. That count is by grep and is
      an upper bound — it does not prove every write targets that directory
      — but it is far enough above twenty to change the size of the job, so
      it is recorded with its method rather than swapped in as a corrected
      figure.
      2026-09-18: TWO OF THE THREE BLOCKERS ARE CLOSED; THE PARENT STAYS OPEN.
      What moved:
      - The source's own leg is cut over. `POST /api/source-ingestion` now
        promotes every item whose admitted bytes the service holds onto the
        durable path BEFORE the manifest is written
        (`services/source_durable_intake.py`, `services/durable_runtime.py`):
        the plaintext the quarantine service scanned becomes a
        content-addressed blob in the new `s3_bucket_sources` bucket, the
        source record an aggregate revision naming it, and the extracted text
        a second blob and revision. The item's `durable_source` says where it
        landed, and its absence means it did not land - it is never filled in
        from the intake record.
        The scan is CARRIED, not repeated: `ingest_scanned` takes the
        admission's verdict with its receipt, policy and scanner-set digests,
        because re-scanning here would let a host with no ClamAV overrule a
        real clean verdict with `unavailable`.
        A missing or unreachable bucket is a 503 that registers nothing, never
        a fall back to the local directory; the pipeline is built lazily, so an
        intake the service itself refuses still answers 409 for that reason.
        Evidence: `tests/test_source_ingestion_durable_real_stack.py`, 3 tests
        passing on a real stack (0 skipped), reading back through a second
        engine and a second S3 client. 2 of the 3 were re-run against the route
        with the promotion removed and failed, so they are not vacuous. The
        doubles are a stub `clamscan` at the ClamAV boundary and FastAPI's own
        dependency override for the caller; the quarantine service, the
        ingestion service, the pipeline, PostgreSQL and MinIO are real.
      - The reconciler has a caller: `metis.tasks.durable.reconcile_estate`,
        on beat hourly, over every tenant the store holds a record for and
        every publication run each has. It repairs nothing, by construction.
        Evidence: `tests/test_durable_reconcile_task_real_stack.py`, 3 tests
        passing on a real stack - it reports the owed outbox events and the
        unindexed source, says the same thing twice (so it repaired nothing),
        goes clean after a drain, and names a blob deleted out from under an
        acknowledged revision.
      WHAT IS STILL OPEN, and why the parent is not checked:
      - The journey stops at the source. `ground` and `build_course` still
        have no product caller, so claims, citations and the course are
        reachable only from a test. The parent's evidence sentence is a
        source-to-COURSE round trip, and half of it is not on the product
        path.
      - The ingestion package manifest is still a JSON file on local disk, so
        `GET /api/source-ingestion` still reads the file store.
      - The 68/65 file-store figures above are unchanged, re-measured
        2026-09-18 by the same method.
  - [x] M10.4.a Provision isolated real migrated database/object storage/search/
        queue/outbox with two tenants and deterministic fixture/cleanup.
        Evidence: harness.
        Evidence: `apps/metis/service/tests/real_stack.py`, with 12 tests
        across `tests/test_real_stack.py` and
        `tests/test_search_store_real_database.py`, all passing and none
        skipped. Run with `METIS_TEST_DATABASE_URL`, `METIS_TEST_S3_ENDPOINT`
        and `METIS_TEST_REDIS_URL` set; without them the tests skip and name
        what is missing. Each stack is:
        - its own PostgreSQL database, migrated to head by the service's
          alembic. The test checks `alembic_version` against the script head.
          The outbox (`store_outbox`) lives there, and so does search: this
          estate has no engine, so the M8.13 index is now durable in
          `search_index_document` (migration 070, `services/search_store.py`).
          M8.1's gap map moved the search_index contract from absent to
          payload_backed;
        - two MinIO buckets. The server itself refuses bytes that do not match
          the digest sent with them;
        - a Redis key prefix for the queue;
        - two tenants whose ids derive from a seed.
        The fixture is deterministic: two stacks provisioned at once share
        nothing, yet produce identical fixture digests. Provisioning is
        all-or-nothing: a stack failing at its second bucket is watched undoing
        the database and the first bucket. Teardown is checked: it reports what
        it could not remove, shown by making removal fail.
        The search store keeps revision order over arrival, keeps tombstones,
        stops serving quarantined documents, survives a new engine, serialises
        two appliers on the row lock, and scopes by tenant. Its product writer,
        the outbox relay, belongs to M10.4.b/.c.
  - [x] M10.4.b Ingest/quarantine/store/hash/extract/version a source and
        persist
        claims/citations/objectives/course/lesson/item/rubric/reviews/build.
        Evidence: source-to-course test.
        Evidence: `tests/test_source_to_course_real_stack.py`, 3 tests passing
        on a real stack (0 skipped).
        RE-MEASURED 2026-09-11: every stage of the existing source pipeline
        wrote JSON files under /tmp. None of it was durable, so the durable
        journey was built:
        - `services/blob_store.py`: content-addressed per tenant, the server
          verifies each `ChecksumSHA256`, a HEAD read-back precedes every
          receipt, and reads re-hash.
        - `services/source_pipeline.py`: two scans (the structural scanner
          plus a malware scanner), then bytes before the record that names
          them, a source revision, then extraction to a derivative blob and a
          second source revision.
        - Claims come from the deterministic M5.19 decomposer, linked to
          sentence spans of the stored text.
        - A citation binds each claim to its span, and outside works (URL/DOI)
          are cited separately.
        - Then objective, course, lesson, item, rubric, review, and a release
          whose manifest is itself a verified blob.
        Claims and citations are now stored aggregates (migration 071).
        `AuthorableKind` keeps the authoring API and generation jobs at the
        original nine, since a claim becomes canonical only by a human decision.
        Everything is read back through a second engine:
        - both revisions; the bytes and derivative by digest; each span's quote
          by digest;
        - the manifest equals the release;
        - the source retraction walk reaches every record built;
        - one audit and one outbox row per write, with nothing in the other
          tenant.
        With the scanner unavailable or reporting infection, the source is
        stored quarantined and extraction refuses it.
        The HTTP ingestion routes still write the file store. Moving them onto
        this pipeline is a cutover.
  - [x] M10.4.c Verify
        transactions/audit/outbox/restart/idempotency/concurrency, exact
        revisions, blobs/derivatives, search index, and tenant isolation.
        Evidence: durability tests.
        Evidence: `tests/test_durability_real_stack.py`, 13 tests passing on a
        real stack (0 skipped), plus the M10.4.a/.b suites; 428 pass across the
        store-dependent suites. RE-MEASURED: nothing ever drained the outbox
        (`published_at` was never set), so `services/outbox_relay.py` was
        built. Each event's index change and its delivery mark commit in one
        savepoint; claims use SKIP LOCKED; the relay reads the exact revision
        an event names and indexes only its `index` map. Proven:
        - A relay killed after applying leaves the event undelivered and the
          index unchanged, then redelivers.
        - Two relays at once deliver disjoint sets that together cover every
          event.
        - A poison event fails loudly and does not starve the events behind it.
        - A withdrawal tombstones the document. A learner's search finds the
          lesson, and the item's `answer_key` never reaches the index.
        - A write referencing a missing revision leaves no row in any of four
          tables.
        - A re-run step writes nothing, and the same bytes stay one object.
        - Every old revision reads back exactly; each audit entry names its
          actor; a changed or missing object is refused.
        - The derivative equals a fresh extraction of the stored original.
        - Two tenants with identical ids share nothing across revisions,
          outbox, audit, index or blob prefixes.
        FIXED a live M8.3 defect: the loser of a race for one revision was told
        `unknown_aggregate` instead of `revision_conflict`. The cause was
        `FOR UPDATE OF a` on a join, which Postgres re-checked against the old
        revision row after the lock wait. The race test caught it before the
        fix.
  - [x] M10.4.d Publish/write/read-back then source retract/quarantine/restore
        and reconcile every owner/store/index. Evidence: lifecycle round-trip.
        Evidence: `tests/test_lifecycle_real_stack.py`, 5 tests passing on a
        real stack (0 skipped), plus 3 restore tests in
        `tests/test_source_graph_real_database.py` and 2 in
        `tests/test_search_index.py`. One course is published through the M8.10
        saga to the exports bucket by `services/object_store_publication.py`
        (S3 destination; prerequisites read from the stores) and read back
        from the bucket. Then:
        - its source's rights lapse (M8.5 retraction): the export becomes a
          reconciler finding, compensation hides it without deleting it, and
          prerequisites report `rights` as the only move;
        - the source is quarantined: extraction refuses and the index stops
          serving it;
        - the source is restored (`source_graph.restore`, a `restore` index
          action, migration 072): owners' queues empty and search serves the
          lesson again;
        - the course is published again.
        After each step, `services/durable_reconciler.py` must find nothing
        across blobs, index, outbox, exports and owner queues. It is shown to
        name each kind of disagreement it looks for when one is injected.
        FIXED (latent until something resolves a propagation):
        `owner_queue` and `impact_preview` read resolved rows as open.
        The relay, reconciler and destination have no product caller yet: the
        HTTP ingestion cutover is open (M10.4.b), and Celery wiring is .e's
        queue.
  - [x] M10.4.e Inject commit/blob/outbox/queue/search/process failures and
        prove no acknowledged orphan/false success. Evidence: fault suite.
        Evidence: `tests/test_faults_real_stack.py`, 10 tests passing on a
        real stack (0 skipped, 0 failed). With the lifecycle, durability,
        harness, search and saga suites, 229 pass. The faults are real:
        - PostgreSQL triggers raise at the revision (deferred to COMMIT),
          outbox and audit rows. No row lands in any of the four tables. The
          bytes, written first, are reported as an unacknowledged orphan, and
          a retry adopts them.
        - An unreachable object store: no record names the bytes.
        - A trigger on the index. Only that event stays undelivered.
        - A deferred trigger on the relay's delivery mark: nothing is marked
          delivered and nothing is indexed.
        - A dead broker: no event is lost, and the reconciler reports every
          one still owed.
        - The service's own Celery worker is SIGKILLed while blocked
          mid-drain, with the course's event already applied and uncommitted
          (its row is locked). Nothing is marked, the write rolls back, and a
          fresh worker delivers all 3 exactly once.
        - A publisher is SIGKILLed after MinIO stored its PUT; a relay
          withholds the answer. The step stays `pending`. Resuming adopts the
          object with 0 writes, leaving one object.
        - A down destination never reports success; it recovers with one
          write.
        The relay now runs as `metis.tasks.durable.drain_outbox`, scheduled
        by beat on queue `metis.durable`. Before, nothing in the service
        drained the outbox.
        FIXED:
        - One index error rolled back the relay's whole batch on every pass,
          so delivery stopped for everyone (the test failed before the fix).
        - Same-instant revisions were delivered in random-UUID order
          (`ignored_older`; the .c test failed 4 of 4 alone).
        - Via e884e9ff9fa: every authoring-API edit nulled the aggregate's
          course, and a course-scoped caller could revise another course's
          aggregate.
- [ ] M10.5 Run QTI 3 import/export goldens, including accessibility features.
      Evidence: external validator or pinned conformance tool results.
      <!-- SPEC 2026-09-11: same external gate as M1.11.g — close them together
      or not at all (§2.5.6). .a–.c and .e are earnable from
      `libs/contracts/src/metis/qti-package.ts`, `services/qti*`,
      `tests/test_qti_*`; .d needs the two pinned validators' own output over
      our export: `1edtech-qti3-xsd` = the public QTI 3.0 XSD bundle validated
      with the `xmlschema` package (pin the download URL and sha256 under
      `apps/metis/service/tools/qti3/`), `longsight-qti3-cli` = installed from
      its public repository at a pinned commit. PNP, MathML and caption features
      must survive the validator, not only our own parser. -->
  - [x] M10.5.a Enumerate supported QTI profile/item-kind/scoring/feedback/
        stimulus/media/locale/accessibility cells and explicit unsupported ones.
        Evidence: coverage manifest.
        Evidence: `evidence/v1-workbenches/metis-qti/coverage-manifest.json`.
        It is generated by `scripts/export_qti_coverage.py` from the registry
        in `services/qti3_profile.py`, which export consults.
        - 63 cells. Every supported cell names the function that implements it
          (tested to exist), and every refused cell names its code.
        - The `contract` column is tested against M1.11's generated evidence.
          19 cells fall short of it, and all are listed:
          - Metis has no canonical hotspot item. The router sends hotspot to
            the selected-response family, which has no hotspot kind.
          - Written scoring is a loss, not the contract's extension.
          - Per-option feedback on ordering and matching is lossy.
          - No draft carries a hint.
          - Stimulus, media, captions, transcripts and PNP are unmapped.
        The runtime is `schemas/qti3.py` and
        `services/qti3_{content,render,map,parse,profile}.py`, with 33 tests in
        `tests/test_qti3_runtime.py`. It maps single-choice, multiple-choice,
        ordering, matching, constructed-response and essay drafts into QTI
        3.0.1, one item per locale, and reads them back. It refuses by code
        what it does not model.
        Measured outside the repo; the receipts are .d's:
        - 12 files in four packages pass the official QTI 3.0.1 XSDs.
        - The Longsight consumer validates the packages, and its scores agree
          with Metis's scorer on all 42 valid responses.
        No product route calls the runtime yet; that is .c's work.
  - [x] M10.5.b Build minimal/full/edge/multilingual/RTL/MathML/caption/
        transcript/PNP/interactive goldens with exact expected mappings/loss.
        Evidence: golden corpus.
        Evidence: `apps/metis/service/tests/fixtures/qti3/goldens/`, seven
        packages written by `scripts/export_qti_goldens.py` from the canonical
        inputs in `tests/qti3_golden_fixtures.py`, and compared byte for byte
        by `tests/test_qti3_goldens.py` (30 tests). Each golden's descriptor
        and its exact set of loss codes are committed with it, so a change in
        what leaves Metis fails a test.
        - minimal, full (partial credit with a penalty, feedback, cards),
          edge (escaped characters, a share that does not divide),
          multilingual (en/fr/ja), RTL (ar, dir=rtl), MathML (islands carried
          through the lesson canonicaliser).
        - caption, transcript, PNP and interactive are one package, as they
          are in practice: a stimulus set whose video carries WebVTT captions,
          an HTML transcript and a long description in both locales, each
          declared as a content-package variant with an Access for All
          resource description, with manifest dependencies and items that
          reference the stimulus in their own language.
        - The media is real: a 32x32 one-second H.264/AAC clip, a PNG, WebVTT
          and HTML, all on the profile's MIME allowlist. Media is
          content-addressed, so the goldens hold no duplicate bytes.
        Measured outside the repo (receipts are .d's): all 19 XML files pass
        the official QTI 3.0.1 XSDs; all seven packages pass the Longsight
        consumer's strict package validation; and its scores equal Metis's own
        scorer on all 36 valid responses to the goldens' items.
        New runtime: `services/qti3_stimulus.py` (an asset resolver, digest
        and MIME checks, adaptations per media kind) plus stimulus rendering,
        parsing and manifest dependencies/variants. The coverage manifest's
        disagreements with M1.11 fall from 19 to 14: media, captions,
        transcripts, PNP and dependencies now match it. Hotspot stays
        unmapped, because Metis has no canonical hotspot item.
  - [x] M10.5.c Import safely, validate canonical semantics/protected keys, then
        export deterministically with version metadata/manifests. Evidence:
        round-trip tests.
        Evidence: `apps/metis/service/tests/test_qti3_packages.py` (40 tests)
        over `services/qti3_package.py`, plus the parse and render changes it
        exercises. Every golden package is exported, read back, and the items,
        stimuli, media and canonical refs that come out are compared with the
        ones that went in; then the same package is broken one way at a time
        and each break is refused by its own code.
        - Import safely: the archive is bounded before anything is parsed
          (entries, entry size, total expansion, compression ratio, nested
          archive by MAGIC not extension, encrypted entry, symlink, duplicate
          normalized path) and every path is a plain relative path inside the
          package. The manifest is reconciled with the archive BOTH ways: a
          file a resource declares must exist, a file no resource declares is
          refused, and every dependency, variant, stimulus reference and media
          reference must name something present.
        - Canonical semantics: a key that names a choice the item does not
          have, a single-response item with two correct values, an order key
          that is not a permutation of the choices, a match key that is not a
          choice/target pair, a mapping that scores something nobody can
          respond with, or a mapping whose bounds can exceed the item's
          maximum are each refused by name rather than imported as an item no
          learner can answer.
        - Protected keys: `contains_protected_answer_material` was the literal
          `True` for every item file. It is now computed, because an
          externally scored item renders with neither a key nor response
          processing — the descriptor was declaring protected material in
          documents that held none. A test proves the declaration matches what
          the document contains, and that the key appears in no other file.
        - Deterministic export with version metadata: every resource now
          carries `qmd:qtiMetadata` (interaction type, feedback type, scoring
          mode, tool name/version/vendor), so a consumer can tell what wrote a
          package. Two builds of the same inputs are byte-identical, and the
          M1.11 archive policy is now read back OFF THE BYTES — lexicographic
          order, 1980 timestamps, mode 0644, no extras or comment, and a
          compressed size equal to level-9 raw deflate (9 of 14 entries
          distinguish level 9 from level 1, so the check can fail).
        Re-measured after the manifest change: all 19 committed golden XML
        files validate against the official QTI 3.0.1 XSDs with 0 errors, and
        all 7 packages still pass the Longsight consumer — 11 items checked,
        0 failed, 0 package errors. Those are .d's receipts to retain.
        CORRECTION (.d, same day): this line first read "exit 0", which was not
        a measurement. Run through `node_modules/.bin/qti3`, the consumer's
        entry guard declines to run and it exits 0 in silence; the numbers
        above are from re-running its resolved `dist/index.js`, which prints a
        JSON verdict. .d's runner refuses to write a receipt for a report with
        no verdict in it.
        The coverage manifest grows to 65 cells (`qti-metadata`,
        `bounded-package-import`).
        STILL NO PRODUCT ROUTE CALLS THE RUNTIME, and .a said this was .c's
        work. Measured why a cutover today would be a regression, not a
        migration: `services/item_import_helpers.py` resolves item-level media
        out of an imported package (`ItemImportRaw.media`, alt text required)
        and the runtime's `Qti3Item` carries no media — media lives on stimuli.
        Its archive reader is also not weaker than the runtime's; it checks
        magic bytes per entry, an extension allowlist, depth, ratio and
        encryption already. The runtime parser is deliberately narrow — it
        refuses response processing it cannot regenerate — so pointing the
        import route at it would refuse foreign QTI the product accepts today.
        The product path that assembles and reimports a package belongs to
        M10.10.d, and the parent M10.5 stays open behind it and .d/.e.
        2026-09-18: .d and .e are closed, so THE PARENT'S ONE REMAINING
        BLOCKER IS THIS PARAGRAPH — no module under `src/metis/api/` mentions
        `qti3_profile`, `qti3_package` or `export_package` (re-measured by
        grep on 2026-09-18; `api/qti.py` calls the older `qti_service`, not
        this runtime). A profile nothing serves is a profile, not an export.
  - [x] M10.5.d Validate through pinned independent QTI schema/conformance tool
        and at least one supported consumer where required. Evidence: reports.
        CLOSED 2026-09-18 AT 14 OF 14, WITH M1.11.g. The two receipts that could
        not be taken were hotspot's, and the reason was not the validators: Metis
        had no canonical spatial item to hand them. It has one now.
        - The canonical item: `schemas/selected_response.py` gains the fifth mode
          M1.5.b ratified — regions with shapes and coordinates, the image they
          are measured on held by digest with its pixel size, and a region key.
          A coordinate count that does not fit its shape, a region that runs off
          the picture, a key naming a region the item does not have, a label or
          an alt text missing a locale, and a maximum above the region count are
          each refused at authoring time. Scoring, preview and the safe
          projection carry regions the way they carried options; the projection
          shows every label and the image's text equivalent and never the key.
        - The three option-shaped scanners (answer consistency, leakage,
          ambiguity) now REFUSE a spatial item by code. Each is keyed by
          `OptionId`, so a source built from a region-based item would have held
          no responses at all and every rule would have passed over nothing.
        - The export: `qti3_map._hotspot_item` writes a `qti-hotspot-interaction`
          with the image, each region's shape, `coords` and `hotspot-label`, and
          the region key; `qti3_parse` reads it back; `qti3_profile` moves the
          cell from `unsupported` to `normalized` with its handler, and the
          coverage manifest's contract disagreements lose hotspot.
        - A LIVE INTEROP DEFECT, FOUND BY THE CONSUMER AND FIXED: the image was
          first written as `img`, which the pinned XSD admits inside a hotspot
          interaction. The Longsight consumer rejects it —
          `interaction.child.unsupported`, "qti-hotspot-interaction does not
          allow img as a direct child" — so the package was schema-valid and
          unreadable by a real consumer, which is the silent downgrade this gate
          exists to catch. It is `object` now, with the text equivalent as its
          content, and both tools pass.
        Evidence: `evidence/v1-workbenches/metis-conformance/`, 14 receipts, all
        `passed`, all `synthetic: false`, regenerated whole by
        `scripts/export_qti_conformance.py` over the two pinned tools. The six
        pre-existing kinds' `inputHash` values are UNCHANGED, so nothing about
        the other exports moved. `tests/test_qti3_conformance.py` now asserts
        `held == needed` rather than `held < needed`.
        `tests/test_selected_response_hotspot.py` (27 tests) covers the item
        itself, and `tests/test_qti3_*` (137) the package and the gate.
        The fixture image is real and committed
        (`tests/fixtures/qti3/media/animal-cell.png`, 240x160): every
        coordinate is measured on it, so the picture and the regions cannot
        drift apart.
  - [x] M10.5.e Fail silent extension/drop/default/downgrade and any supported
        cell lacking current proof. Evidence: conformance gate.
        Evidence: `services/qti3_gate.py`, run by `scripts/export_qti_gate.py`
        into `evidence/v1-workbenches/metis-qti/conformance-gate.json`, with
        15 tests in `tests/test_qti3_gate.py`. The gate is computed over real
        exported bytes — seven per-kind packages and all seven goldens, 40
        documents — never over a description of them. IT PASSES SINCE
        2026-09-18: it failed on exactly the two receipts that could not be
        taken (`missing-passing-receipt:assessment-kind:hotspot:*`) until
        M10.5.d gave Metis a canonical spatial item. `test_qti3_gate.py` now
        asserts the pass, with 7 supported kinds and 14 receipts, rather than
        the two blockers, so the verdict is still checked and not merely
        produced.
        The five checks, and what each one is shown catching in a test:
        - silent extension: a namespace that neither the pinned schema closure
          defines nor the profile declares. The allowlist is the 11
          `targetNamespace`s of the 21 pinned schemas — verified against the
          files themselves by `fetch_xsd.py`, not maintained by hand — plus the
          profile's own. Caught: an invented `xmlns:x`.
        - silent drop: a loss code the registry does not declare, or a document
          that does not come back byte-identical through the parser. Caught: an
          invented code, a re-indented document, and a document the parser
          refuses (reported as a blocker, never raised — the gate stays total
          over a hostile package).
        - silent default: a score maximum, mapping default or mapping bound
          left implicit. Caught: `normal-maximum` and `upper-bound` removed.
        - silent downgrade: the interaction in the document, the
          `qmd:interactionType` in the manifest and the contract's mapping for
          that kind must be one thing. Caught: an ordering item rewritten as a
          choice list, and a manifest that declares a different interaction
          than its document carries — which is the shape of the live defect
          M10.5.a found on the product's import path.
        - unproven cell: a supported kind with no current passing receipt from
          each pinned validator. Caught: no receipts at all (14 blockers), and
          a receipt whose `inputHash` is bytes this repository no longer
          builds (stale proof proves nothing).
        NEW, because the gate needed it: the runtime's loss vocabulary is now
        declared once, in `qti3_profile.LOSS_CODES` (26 codes, published in the
        coverage manifest). Before this, a refused cell named its code but a
        LOST thing did not: the 15 codes the corpus emitted were nowhere in the
        registry, so a drop could be recorded under any name at all and nothing
        would notice. A test scans the runtime's own source for every
        `Qti3Loss` code and requires the declared set and the emitted set to be
        equal, so the list cannot drift in either direction.
        The committed decision is re-computed by a test and must equal what the
        gate says today, so a package that starts extending, dropping,
        defaulting or downgrading in silence fails the suite rather than the
        next reader.
- [ ] M10.6 Run LTI 1.3/LTI Advantage security and flow tests. Evidence: OIDC,
      JWKS rotation, nonce, state, roles, deep link, and grade service.
      <!-- SPEC 2026-09-11: same external gate as M1.12.g (§2.5.6). .a–.e are
      earnable against our own platform simulator only as security/flow unit
      coverage (`tests/test_lti_service.py`, `tests/test_lti_api_contract.py`,
      `libs/contracts/src/metis/lti-advantage.ts`); the parent's evidence line
      additionally needs one launch per flow from an independent LTI 1.3
      platform — retain the receipt or record the failed attempt. The open LTI
      score route named in M0.15.d must be proven closed before the
      grade-service suite (.e) counts. -->
      <!-- the independent-platform half is DONE 2026-09-12; the parent stays
      open because §2.5.6 binds it to M1.12.g, which also wants the 1EdTech
      certification suite, and that is member-only.
      Moodle 4.5.13+ was installed headlessly on this server
      (`apps/metis/service/tools/lti/README.md` reproduces it), registered
      Metis as an LTI 1.3 tool through Moodle's own API, and drove every flow
      the evidence line names. Receipts, each the platform's own behaviour,
      are in `evidence/v1-workbenches/metis-conformance/moodle-lti-1p3/`:
      - OIDC + nonce + state: Moodle posts its login to
        `/api/lti/login/initiate`, we answer 303 with a state and nonce we
        minted, Moodle authorizes and posts a real RS256 `id_token`, and the
        launch verifies (`receipt-resource-link.json`).
      - JWKS: we fetch Moodle's keys; Moodle fetches ours to verify what we
        sign. ROTATION was exercised live — Moodle minted a new key pair and
        published only the new `kid`, the running tool held the old one
        cached, and the next launch verified on the refresh
        (`receipt-jwks-rotation.json`).
      - Roles: Moodle's Learner maps to `learner` (launch, view own result);
        its Instructor to `educator` with `select-content` and `post-scores`.
      - Deep link: Moodle's `LtiDeepLinkingRequest` is accepted, our signed
        response is posted back, and MOODLE PARSED IT into a real activity
        configuration (`receipt-deep-linking.json`).
      - Grade service: the tool exchanged a `private_key_jwt` for an access
        token at Moodle's token endpoint, created a line item, posted a score
        and read the roster through NRPS. The grade is in Moodle's own
        `grade_grades` table — learner1, 7.00000 of 10.00000, read straight
        out of its database (`receipt-grade-service.json`).
      Running it found three things our own tests could not, all fixed: a
      platform never sends our `platform_id`, a platform drives a browser
      rather than a JSON API, and a platform on loopback publishes over plain
      http. The tool-side AGS/NRPS client did not exist and now does
      (`services/lti_services_client.py`, 14 tests).
      WHAT REMAINS: the 1EdTech certification suite half. It is hosted and
      member-only, so §2.5.6's "record the attempt and leave the cell open"
      applies; no part of it was run and none is claimed. -->
  - [x] M10.6.a Test registration/issuer/client/deployment/JWKS discovery/cache/
        rotation/revocation and algorithm/key/confusion failures. Evidence:
        trust tests.
        Evidence: `apps/metis/service/tests/test_lti_trust.py` (33 tests) over
        the new `services/lti_trust.py`. Every test signs a real RS256 token
        with a real 2048-bit key, because the questions are about keys.
        THE CELL COULD NOT BE EARNED BY WRITING TESTS: there was nothing to
        test. `lti_service.py` verified a platform's `id_token` with HS256
        against a `platform_secret` taken at registration, and never fetched
        the `jwks_url` it stored. A LIVE DEFECT, not a gap: any holder of that
        secret — both sides, and anyone either leaked it to — could mint a
        launch for any learner in any role, and LTI 1.3's whole trust model is
        that no such secret exists. Fixed here; `platform_secret` is gone from
        the API.
        What the trust layer now refuses, each with the contract's own error
        code (`libs/contracts/src/metis/lti-advantage.ts`) and a test:
        - `algorithm-denied`: anything but RS256, refused on the header before
          a key is fetched — including `alg: none`, and HS256 signed with the
          platform's own public key, which is the classic confusion.
        - `key-id-missing`: no `kid`. Without one, verification becomes "does
          any published key match", which keeps honouring a withdrawn key.
        - `registration-inactive`, `issuer-mismatch`, `audience-mismatch`
          (exactly one registered client in `aud`),
          `authorized-party-mismatch` (`azp` rules for multiple audiences),
          `deployment-mismatch`, `issued-in-future`, `launch-expired`,
          `launch-lifetime-exceeded` (600 s), with a minute of clock skew.
        JWKS discovery, cache, rotation and revocation, all measured by
        counting fetches at the boundary:
        - discovery fetches the registration's URL and no other; a key
          published at another platform's URL does not verify here.
        - three launches, one fetch: a launch is not a network call.
        - after the TTL the next launch fetches again.
        - ROTATION: an unfamiliar `kid` triggers exactly one refresh and then
          verifies, and the old key keeps working while it is still published.
        - REVOCATION: a refresh REPLACES the key set. Merging is how a
          withdrawn key outlives its revocation, so a withdrawn key stops
          verifying at the next refresh, and `invalidate()` drops it at once.
        - a stream of invented `kid`s costs one refresh, not one per token:
          otherwise a tool is an amplifier pointed at its platform's JWKS.
        The tool's own service calls now use `private_key_jwt` against the
        tool's published keys (`tool_jwks_url`), so neither side stores a
        secret that could mint the other's traffic. `HttpsJwksFetcher` is the
        only thing that reaches a network: https only, no redirects, bounded
        body, and the protocol is the seam the tests replace.
        Registration gained `platform_jwks_url`, `tool_jwks_url` and `status`
        (re-registering as `suspended` stops every launch), and re-registering
        with a new JWKS URL drops what was cached for the old one. OpenAPI,
        the generated client and the schema inventory are regenerated.
        SIDE FINDING, left for M1.1: `evidence/v1-workbenches/
        metis-contract-ownership.json` is stale. Regenerating it today gives
        198 multi-variant OpenAPI groups and 58 competing definitions against
        the committed 40 and 0, and
        `scripts/v1-workbenches/generate-metis-contract-ownership.test.mjs`
        already fails 7 of 11 at HEAD — measured by running it against HEAD's
        own OpenAPI, so it is not this change. It was left as it is rather
        than half-regenerated here.
  - [x] M10.6.b Test OIDC login/launch issuer/audience/message/version/target/
        nonce/state/time/replay/session and malicious claim cases. Evidence:
        launch suite.
        Evidence: `apps/metis/service/tests/test_lti_launch.py` (27 tests) over
        the new `services/lti_login.py` and the rewired `POST /api/lti/launch`.
        As in .a, the suite could not be written against what was there: THERE
        WAS NO LOGIN. `/lti/launch` took `state` and `nonce` as request fields
        and checked the token's nonce against the one the caller had just
        supplied — a check that passes for anyone who sends both halves. There
        was nothing a replay could fail.
        A launch is now the second half of a conversation this tool starts.
        `POST /api/lti/login` is the OIDC third-party login: it validates the
        issuer, client and target against the registration, mints a 256-bit
        `state` and `nonce` from the system CSPRNG, remembers what it asked
        for, and returns the authorization URL (scope, response_type,
        response_mode, prompt, redirect_uri, login_hint, state, nonce).
        `/launch` then consumes the state and judges the token against that
        login. `nonce` is gone from the launch request: a nonce the caller
        supplies is a value the caller chose to match.
        What each case now costs, with the contract's error codes:
        - `state-replayed` for a state nobody asked for, for the same launch
          posted twice, and — deliberately — after a REFUSED launch, because a
          state that survives a wrong answer is a retry oracle.
        - `state-mismatch` for an expired login (10 minutes) and for a state
          belonging to another platform.
        - `nonce-mismatch` for a token carrying any other nonce, and
          `nonce-replayed` for a nonce already answered, which is the case a
          fresh login would otherwise launder.
        - `target-link-mismatch` when the token's target is not the one the
          login named or is not a registered redirect (registration now
          carries `tool_redirect_urls`, a set, as the contract's
          `toolRedirectUris` does, and exact membership is required).
        - `login-deployment-mismatch` when the deployment drifts between the
          two halves; `anonymous-resource-launch-denied` for a resource-link
          launch with no subject, while deep linking rightly needs none.
        - `lti.version-unsupported` and `lti.message-type-unsupported` are
          Metis codes, said to be Metis codes: the contract's vocabulary has
          no code for either, and filing them under one that means something
          else would be worse than naming them.
        Malicious-claim cases covered: a launch signed by a key the platform
        does not publish, a target pointing at another origin, an invented
        state, a stolen nonce, a version downgrade, an unsupported message
        type, and an anonymous attempt.
        KNOWN AND RECORDED, not hidden: the state and nonce stores are in
        process, so a restart forgets which were spent. It fails closed — an
        unknown state is refused — but a restart mid-attack reopens the replay
        window for tokens still inside their lifetime. The stores are a seam
        (`LoginStore`, `SpentNonces`) so a durable one can replace them; that
        is the same class of defect already recorded against
        `credential_service` in M9.18.
  - [x] M10.6.c Test role/context/resource/user/privacy mappings for learner/
        instructor/admin and unknown/custom/cross-tenant claims. Evidence: role
        tests.
        Evidence: `apps/metis/service/tests/test_lti_roles.py` (20 tests) over
        the new `services/lti_roles.py`. Before this, `launch_tool` copied the
        `roles` claim into its response verbatim: eleven role URIs, seven
        canonical roles and ten operations were pinned in the contract and
        none of them existed in the service, so every launch had the same
        authority as every other — none, or whatever a caller chose to infer.
        The mapping is not written here twice: the first test reads
        `evidence/v1-workbenches/metis-lti-advantage.json`'s own role matrix
        and requires all 11 rows to match this module's — scope, canonical
        role, operation list, context binding, authoritative identity and
        `displayClaimsTrusted: false` — so the code cannot drift from the
        contract without failing.
        The three rules, each with the case that motivates it:
        - ONLY RECOGNISED ROLES COUNT. A launch carrying only roles this
          profile does not map is refused with `unknown-role-denied`; a custom
          vendor role beside a known one is carried for display and grants
          nothing. An unrecognised default would make "unknown" the widest
          privilege instead of the narrowest.
        - A CONTEXT ROLE WITHOUT A CONTEXT GRANTS NOTHING. An instructor role
          sent with no context claim is an instructor of nothing: the
          canonical role is still reported, and the operation set is empty.
          Operations are the union of what each recognised role grants, so no
          single role wins by sounding senior.
        - IDENTITY IS SIGNED, DISPLAY IS NOT. The principal is
          `issuer#subject`; `name` and `email` became `display_name` and
          `display_email`, are never matched on, and a launch that carries
          neither still identifies its user. The roster stores them as display
          beside the signed subject it actually matches on.
        The matrix's own headline claims are asserted: a learner can neither
        author nor grade, a mentor reads neither roster nor grades.
        Cross-tenant: the same context id arriving from two platforms is two
        rosters, and an access token minted for one platform reads only that
        platform's roster — tested with two registered platforms, each with
        its own keys.
  - [x] M10.6.d Test deep-link request/response/signature/content/resource/
        placement/return/stale/deleted/forbidden cases. Evidence: deep-link
        suite.
        Evidence: `apps/metis/service/tests/test_lti_deep_link.py` (14 tests)
        over the new `services/lti_deep_link.py`, plus the API's new
        `GET /api/lti/jwks`.
        TWO DEFECTS FIXED, both of which made the response unusable or unsafe:
        - THE RESPONSE WAS SIGNED WITH METIS'S OWN SESSION SECRET (HS256, and
          `iss: "metis"`). No platform holds that secret, so no platform could
          ever verify a deep-linking response this service produced. It is now
          signed with the tool's own RSA key, names its `kid`, and the tests
          verify it exactly as a platform would — through the public half
          served at `/api/lti/jwks`. A deployment with no key configured signs
          nothing and says so (`lti.tool-key-not-configured`, 503), instead of
          signing with the session secret; the pattern and the wording follow
          the integrity-deep-link key already in `config.py`.
        - THE PLATFORM'S OPAQUE `data` CAME FROM THE CALLER. It is how the
          platform recognises its own request, and the request body had a
          `data` field anyone could fill. The field is gone; the value travels
          in the launch's `deep_linking_settings` and is echoed from there.
        `LTI_DEEP_LINKING_SETTINGS_CLAIM` was defined in the service and read
        by nothing, so everything the platform said it would accept was
        ignored. The settings are now captured at launch and honoured:
        `lti.deep-linking-type-denied` for an item type it does not accept,
        `lti.deep-linking-target-denied` for a presentation target it does not
        accept, `lti.deep-linking-multiple-denied` for a second item where one
        was asked for (and several where it allows them),
        `lti.deep-linking-return-url-invalid` for a return URL that is not
        https, and `lti.deep-linking-settings-missing` for a deep-linking
        launch that carries none. The response carries the platform's own
        `return_url`, which is where the browser must post it.
        Forbidden and stale: selecting content now requires the launch to hold
        the `select-content` operation from .c's mapping, so a learner's
        launch is refused (`lti.deep-linking-forbidden`); an expired launch
        token is refused, as is one signed by anything but this service.
        While fixing the stale case: `_decode_metis_token` checked expiry
        against the library's idea of now rather than the clock the call was
        given, so a caller passing a time got an expiry judged against a
        different one. It now uses the call's clock, which is also what makes
        the AGS and NRPS token checks testable in time.
  - [x] M10.6.e Test NRPS and AGS line items/scores/results/pagination/scopes/
        idempotency/corrections/late events/visibility. Evidence: service suite.
        Evidence: `apps/metis/service/tests/test_lti_services.py` (17 tests).
        Four of the named behaviours did not exist and were built:
        - RESULTS. There was no way to read what a line item holds; scores
          went in and nothing came out. `GET /api/lti/assignments/line-items/
          {id}/results` reads them, gated on the AGS result scope, which is
          also new (`.../lti-ags/scope/result.readonly`).
        - IDEMPOTENCY. A score carries a `timestamp` — when it was true, not
          when it arrived — and re-posting the same timestamp returns the same
          `score_id` marked `duplicate`, without applying it again. A retry
          after a timeout used to create a second score; a retried correction
          would have read as two corrections.
        - CORRECTIONS AND LATE EVENTS. A newer timestamp replaces the result
          and increments its `revision`, so a reader can see it was corrected.
          An older one is refused: arrival order is not event order, which is
          the lesson M8.11's grade ledger already paid for.
        - PAGINATION. A roster and a result set are both unbounded — a lecture
          course is thousands of people — and both were returned whole. Both
          are now paged in subject order, with a `next_page` token that is
          ours: a token this service did not issue is refused rather than
          parsed.
        Visibility, per scope, each tested both ways: posting a score takes
        the score scope and no other; reading results takes the result scope
        and no other (a score-scoped token cannot read back what it wrote);
        the roster takes the NRPS scope; a token minted for one platform
        cannot touch another's line item; an expired access token buys
        nothing.
        M0.15.d's PRECONDITION FOR THIS CELL, measured over the served app
        rather than assumed: `POST /lti/assignments/scores` is unreachable
        without a bearer token (422), refuses an empty, non-bearer or
        unparseable one, refuses a token that bought a different scope (403
        `missing required scope`), and accepts only a score-scoped one; the
        grades it writes are readable only with the result scope. That is the
        vector M0.15.d left unmeasured; M0.15.d itself stays open on its other
        three (the learner-readable answer keys, `/create/*`, and the
        submission shapes).
        While writing it: the AGS scope checks ignored the clock they were
        given, so an expired access token still bought a score write in any
        caller that passes a time. Fixed with `_decode_metis_token`'s clock
        from .d.
- [x] M10.7 Run OneRoster, SCORM, xAPI, Caliper, Open Badges, and gradebook
      interoperability fixtures. Evidence: compatibility report.
      CLOSED 2026-09-18, with all six children closed AND the parent's own
      sentence measured rather than inherited. The compatibility report is
      computed from the code today: 49 cells across all six profiles — 38 over
      HTTP, 8 library, 3 unsupported — with every implemented cell naming a
      handler that exists, every unsupported one naming the symbol whose
      absence makes it true, 0 silent losses, and every declared loss
      demonstrated by calling the code and watching the field not arrive.
      WHAT IS STILL UNSUPPORTED, AND SAYS SO: three OneRoster record types —
      academic session, term and category. They are not among the seven
      entities M10.7.b names, they are reported as gaps rather than omitted,
      and the ratchet fails the day one of their symbols exists.
      WHAT THIS PARENT DOES NOT CLAIM: no certification by any of the six
      bodies. What is held is fixtures, a generated matrix nothing can edit
      into agreeing with itself, and — for QTI, which is M10.5's — two pinned
      external tools' own output.
      <!-- SPEC 2026-09-11: service modules exist for all six
      (`schemas/{oneroster,scorm,xapi,caliper}.py`,
      `api/{oneroster,caliper}.py`, `services/open_badges_service.py`,
      `services/standards_exchange.py`, `services/lms_interop_matrix.py`) with
      `tests/test_{oneroster,scorm,xapi,caliper}_{service,api_contract}.py`;
      Open Badges has NO test file today, so .d starts by writing one. The
      standards exchange has no HTTP surface in this build (M8.15.d): .a's
      matrix says per cell whether the direction is served over HTTP,
      library-only, or unsupported, and a library-only cell is "compatible" for
      the library alone. -->
  - [x] M10.7.a Enumerate supported version/profile/operation/direction/entity
        cells and source-of-truth ownership per standard. Evidence: matrix.
        Evidence: `evidence/v1-workbenches/metis-interop/coverage-matrix.json`,
        generated by `scripts/export_interop_matrix.py` from the registry in
        `services/interop_coverage.py`, with 16 tests in
        `tests/test_interop_coverage.py`.
        47 cells over the six profiles M1.13 pins when this was written: 21
        served over HTTP, 4 library-only, 22 unsupported. The registry is
        generated, so the counts move as later items build things — .c and .d
        record the moves, and the committed matrix is the current number.
        Every cell names its profile, entity,
        operation, direction and the owner of the record, and a cell cannot
        describe itself incoherently — an unsupported cell that names a
        handler, an HTTP cell with no route, or an implemented cell owned by
        nobody all fail to construct, and the test proves each refusal.
        Every claim is checked against something outside the registry: each
        handler against the import system (a method on a class included), each
        route against the OpenAPI document the service really publishes, and
        the profile set against the contract's own decision matrix.
        FOURTEEN DISAGREEMENTS WITH M1.13, computed from its evidence rather
        than restated, and the last one is a defect:
        - Five profiles the contract records as `runtimeAdopted: false` are
          served over HTTP today (OneRoster, Caliper, both SCORM editions,
          xAPI). Only Open Badges is genuinely library-only.
        - Two declared directions nothing implements: OneRoster
          `metis-to-external` (M1.13 makes Metis the gradebook provider and
          nothing publishes a line item or result) and xAPI
          `bidirectional-explicit-owner` (no state or profile documents).
        - Six of OneRoster's ten record types are unmodelled: organization,
          academic-session, term, category, line-item, result.
          `OneRosterEntityType` carries user, course, class and enrollment.
        - IDENTITY: M1.13 pins `tenant+connector+sourcedId` for every entity
          and `emailAsJoinKey: false`. The runtime matches users by normalized
          email and classes by course slug, AND NO METIS MODEL PERSISTS A
          sourcedId at all. A re-sync cannot recognise what it created: change
          an email in the SIS and the next sync makes a second user.
        That last row is why .b is not a test-writing task — a round trip
        needs a stable identity to round-trip through.
        Also recorded as cells, because an absent row reads as "no such cell"
        rather than "not built": no OneRoster delta watermark or tombstone,
        no SCORM sequencing, interactions or objectives, no xAPI voiding, no
        Open Badges revocation or endorsements, and a badge's `proof` is
        detected but deliberately never treated as verified.
  - [x] M10.7.b Run OneRoster org/course/class/user/enrollment/line-item/result
        snapshot/delta/conflict/delete round-trips. Evidence: OneRoster suite.
        CLOSED 2026-09-18 ON ALL SEVEN. It was open on three. `organization`, `line-item` and
        `result` are not modelled at all (M10.7.a's matrix), so there is
        nothing to round-trip them through; `line-item` and `result` are the
        gradebook direction M1.13 makes Metis the provider for, and nothing
        publishes one. Closing this needs those built, not tested.
        AND THE GRADEBOOK HALF IS BLOCKED ON SOMETHING SHARPER THAN "UNBUILT",
        found 2026-09-13 by starting to build it. `line-item` and `result` are
        `gradebook-publish` / `metis-to-external`: Metis SERVES them and a SIS
        reads them. A published `result` names a STUDENT — OneRoster wants the
        student's `sourcedId` — and the grade ledger records whose grade a
        grade is ONLY as `learner_digest`, an irreversible sha256. Nothing in
        the estate maps a digest back to anybody: every use compares it to one
        the caller already computed ("a learner may only see signals whose
        learner_digest is their own"), which is right for privacy and is
        exactly why it cannot be published.
        THE JOIN THAT WOULD RESCUE IT DOES NOT EXIST EITHER. A digest can be
        RECOMPUTED for a student you can already name, so a roster cohort
        could in principle be matched. But the only grades the ledger holds
        are the LTI AGS ones M10.7.e's road now carries, and their digest is
        over the PLATFORM's user id, while `roster_external_reference` maps a
        `sourcedId` to a METIS user. Nothing connects a platform user id to
        either. So the chain runs sourcedId → Metis user → nothing.
        THIS IS A TENSION AND NOT AN OVERSIGHT, worth stating as one: M10.7.d
        found this estate serialising a learner's NAME to a Caliper consumer,
        and the answer was to identify people by digest. The same decision is
        what stops Metis being a gradebook provider. Closing the cell means
        deciding which of the two wins for this direction — a published
        gradebook needs a nameable student — and that is a design decision
        with a privacy consequence, not a missing model.
        THE DECISION WAS TAKEN 2026-09-18 (user): a scoped, auditable join.
        `line-item` AND `result` ARE SERVED NOW. Two of the three unmodelled
        entities are built; `organization` is what keeps this cell open.
        - The join: `roster_grade_identity` (migration 079) maps a learner
          digest to a SIS `sourcedId`, per tenant and per connector, unique in
          BOTH directions — two sourcedIds sharing a digest would put one
          learner's grades under two names, and two digests sharing a
          sourcedId would put two learners' grades under one.
        - THE SCOPE IS THE DESIGN, and a test holds it. `record_identity_async`
          has exactly one caller (`oneroster_service`), `resolve_for_publication`
          exactly one (`oneroster_gradebook`), and that one exactly one route.
          There is deliberately NO `resolve_one(digest)` helper: a
          general-purpose reverser is what a later caller reaches for, and it
          would end the digest's usefulness for every other surface at once.
          Every read takes an actor, a purpose from a fixed vocabulary and a
          detail, none of them defaulted, and writes its audit row in the SAME
          transaction as the read — so a publication that happened and an audit
          that did not cannot both be true.
        - A SECOND GAP FOUND BY BUILDING IT: the ledger could not say what a
          grade is ABOUT. `grade_id` is a digest over platform, line item and
          user, and a `LedgerEvent` carries neither the line item nor the
          learner, so nothing in the database could enumerate one line item's
          results. `grade_subject` (migration 079) is that record, written by
          the ingress when it appends and never rewritten; it is pseudonymous —
          the same digest `project()` already takes — so it adds a lookup and
          never an identity.
        - AN UNRESOLVED LEARNER IS REPORTED. A grade whose learner the roster
          has never named comes back in `unpublishable_line_items`, because a
          SIS receiving a short gradebook with no way to tell would record
          those learners as having done no work.
        - `GET /api/oneroster/gradebook` publishes both, tenant DERIVED from
          the caller. A result is derived from the ledger at read time and
          never cached here: a correction appended a second ago is in the next
          read, and this surface does not become a second record of a grade.
        Evidence: `tests/test_oneroster_gradebook_postgres_integration.py`,
        13 tests on a live PostgreSQL — publication, the unresolved learner,
        the orphan line item, a correction landing in the next read, the audit
        row per read, a refused purpose, a digest already published as
        somebody else, an identity change as an update, two tenants, two
        connectors, write-once subjects, and the two scope ratchets over the
        source. The matrix moves with the code: `oneroster-1.2:metis-to-external`
        is no longer an unserved direction, and the coverage test that asserted
        it was now asserts that it is served.
        AND `organization` IS BUILT TOO (migration 080, `roster_organization`).
        Metis has no organisation of its own — a tenant is not a school — so an
        org is kept as what it is: the source's record under the source's id,
        per tenant and per connector. That is enough for the thing the entity
        was needed for.
        A LIVE DEFECT IT CLOSES: `OneRosterUserRecord.org_sourced_ids` existed
        and NOTHING READ IT, so a user could claim membership of an
        organization nobody had ever declared and the sync took it. The claim
        is checked now — against this payload's orgs and against the ones this
        connector sent before — and an unknown one is a WARNING rather than a
        block, because the org may be in the next page and losing a real person
        over a record that is merely late would be worse.
        An org takes the same three round trips every other entity does:
        snapshot (create/update), delta (`date_last_modified` against the
        caller's watermark), conflict (two orgs with one sourcedId) and delete
        (`tobedeleted` deactivates and KEEPS the row, so the same sourcedId
        arriving again is not adopted as a new organization).
        Evidence: 9 more tests in `tests/test_oneroster_identity.py` (21 in the
        file), and 257 across the coverage, compatibility, contract,
        route-matrix, gap-map, migration and grade-interop gates.
        WHAT "SNAPSHOT/DELTA/CONFLICT/DELETE" MEANS FOR THE TWO PUBLISHED
        ENTITIES, said rather than implied: `line-item` and `result` are the
        direction where a SIS READS Metis. There is no delta, conflict or
        delete round trip to run on them, because this estate is not the party
        that changes them — it answers with what the ledger currently says, and
        a correction appended a second ago is in the next read.
        THREE OF THE TEN CONTRACT RECORD TYPES REMAIN UNMODELLED — academic
        session, term and category — and each still names the symbol whose
        absence makes its row true. They are not among the seven this cell
        names.
        WHAT WAS BUILT, because .a measured that a round trip had no identity
        to travel on: a sync could not recognise what it had created.
        `sourcedId` was never persisted; users were matched by email and
        classes by course slug, so an address change in the SIS made a SECOND
        PERSON, and a renamed class made a second course.
        - `roster_external_reference` (migration 073, applied to a live
          PostgreSQL) stores one row per external identity —
          tenant + connector + entity + sourcedId — against the Metis record it
          resolved to, with the source's own `date_last_modified` and a
          status. Two connectors numbering independently are two identities.
        - Identity now resolves through the reference, and email or slug only
          ADOPTS a record the first time a connector sees it. An email change
          is an update to that person; the planner compares emails now, which
          it never had to when the email was the identity.
        - Two sourcedIds claiming one Metis record is a blocking
          `identity_owned_by_another_source_record` conflict, not a merge.
        - DELTA: records carry `date_last_modified`, the request takes
          `modified_since`, and the response returns the `watermark` to ask
          with next time. It is the source's clock, so a re-run here cannot
          move it, and a record the source dated nothing is never skipped —
          silence is not a statement that nothing happened.
        - DELETE: `tobedeleted` deactivates the record and TOMBSTONES the
          reference rather than dropping it, so the same sourcedId arriving
          again is not adopted as somebody new.
        Evidence: `tests/test_oneroster_identity.py` (16 tests) for the rules
        and `tests/test_oneroster_postgres_integration.py` against a live
        database for the round trip: sync, change the email, sync again — one
        user, one reference, the new address — then withdraw and find the
        tombstone.
        A LIVE DEFECT FOUND BY RUNNING THAT, unrelated to rostering and worse:
        `AuthService.hash_password` RAISED ON EVERY INPUT. passlib 1.7.4 reads
        `bcrypt.__about__` to pick its backend and bcrypt removed it in 4.1
        (this venv has 5.0), so the error came back as a misleading "password
        cannot be longer than 72 bytes" for a 57-byte string. Registration
        could not work at all, and the suite was green because the only test
        that reaches this service patches it out. Now called through `bcrypt`
        directly, with over-long passwords refused rather than silently
        truncated to something a shorter password would also open, and hashes
        written by passlib still verify. `tests/test_auth_passwords.py` (10
        tests) hashes and verifies real passwords with nothing mocked.
  - [x] M10.7.c Run SCORM package/runtime/sequencing/bookmark/score/status/
        interaction/error and xAPI statement/state/profile/voiding fixtures.
        Evidence: SCORM/xAPI suites.
        WAS OPEN ON SCORM SEQUENCING AND THE xAPI DOCUMENT RESOURCES. Sequencing
        and navigation are not written or honoured — a package is one
        launchable activity — and there are no state or profile documents, so
        an activity cannot keep a learner's place here. Both are cells in
        M10.7.a's matrix with their reasons; closing this means building them.
        DONE, and both were defects rather than gaps:
        - xAPI VOIDING. A voiding statement (verb `voided`, object a
          `StatementRef`) was stored as an ordinary statement and the
          statement it withdrew KEPT READING AS CURRENT — the one thing
          voiding exists to prevent. Now the target is marked voided and
          carries the id of the statement that withdrew it; a query stops
          answering with it, and a replay still carries it, because a consumer
          catching up has to learn that it was voided. The verb alone voids
          nothing (it must be a StatementRef), a void names something this LRS
          holds, and a voiding statement cannot itself be voided — otherwise a
          retraction could be retracted and nothing would settle.
          `tests/test_xapi_voiding.py`, 11 tests.
        - SCORM INTERACTIONS. A commit carried `runtime_data`, a free-form
          dict, so a player's per-question detail arrived in whatever shape it
          liked and nothing checked it. An interaction is a typed record now:
          a response that fits its type (a true-false answer is true or false,
          a numeric answer is a number), a result from SCORM's vocabulary or a
          number, no question reported twice in one commit, and NO TYPE THE
          PACKAGE'S OWN EDITION LACKS — a 1.2 package reporting `long-fill-in`
          is reporting something no 1.2 player wrote.
          `tests/test_scorm_interactions.py`, 24 tests.
        Already covered by the existing suites and re-checked here: package
        and manifest, runtime commit, bookmark (`location` and `suspend_data`),
        score (raw/min/max scaled) and status (lesson and success), and a
        replayed commit staying one commit.
        The matrix moves with the code: `scorm-*/interaction` and
        `xapi-2.0/statement/void` are now served cells rather than reasons, so
        the coverage matrix is 24 HTTP cells and 19 unsupported.
        THE xAPI DOCUMENT RESOURCES ARE BUILT. Half of what kept this open is
        closed: `state-document/put-get-delete` and
        `profile-document/put-get-delete` are served cells now rather than
        witnesses named by their own absence, and an activity can keep a
        learner's place. Migration 075 (`xapi_documents`),
        `services/xapi_document_service.py`, thirteen operations under
        `/api/xapi/activities/{state,profile}` and `/api/xapi/agents/profile`,
        and `tests/test_xapi_documents.py` — 45 tests against a freshly
        migrated Postgres, service and HTTP both. The matrix is 30 HTTP, 7
        library and 12 unsupported.
        DURABLE FROM THE FIRST LINE, deliberately. This estate keeps losing
        the same argument with itself — LTI line items, LTI scores until
        M10.7.e, credential revocations (M9.18) — and a document store a
        restart empties would tell a learner they had never begun, which is
        the one thing a state document exists to prevent.
        THE CONCURRENCY IS THE RESOURCE. A learner with two tabs open is two
        writers. PUT replaces and POST merges, which are different verbs and
        not a preference; a merge is defined over two JSON OBJECTS, so a POST
        onto text or an array is refused rather than quietly made a replace
        that loses everything the client did not send; and a write over an
        existing document that names no expected version is refused 409 WITH
        the ETag to re-read, while one that names a version that has since
        moved is 412. A client cannot retry correctly unless it is told which
        of those two happened. A delete obeys the same preconditions, because
        deleting what somebody has just written loses their work exactly as
        overwriting it would.
        THE IDENTITY IS A TYPE AND AN INDEX, NOT A CONVENTION. A state belongs
        to one learner on one activity, an activity profile to the activity
        and every learner shares it, an agent profile to the learner across
        activities — so an activity profile that names an agent WILL NOT
        CONSTRUCT, because that mistake is how one learner's place gets served
        as everybody's. A registration makes a different document, so a second
        attempt does not resume the first. The whole identity is folded into
        one `storage_key` with a unique index over it, so two writers racing
        lose in the database rather than in an `if`.
        A CROSS-TENANT HOLE THIS ITEM OPENED AND M8.14's RATCHET CAUGHT. The
        first draft's table had no `tenant_id`, and every other part of a
        document's name — the activity, the agent, the document id — arrives
        from the caller. Any authenticated caller could therefore have read
        any tenant's state by naming its activity and its learner. Nothing in
        this item's own tests would have found it; the tenancy ratchet failed
        the full suite with `newly_untenanted=('xapi_documents',)` and that is
        the only reason it was found. The tenant is part of the identity now,
        derived from who is asking and never from a parameter, and folded into
        `storage_key` so the unique index enforces it rather than a query
        remembering to. Three tests assert the boundary directly: a read, a
        scoped delete and a listing all stop at it.
        A LIMIT FOUND BY WRITING THE TEST, AND KEPT AS A LIMIT. `XapiActor`
        requires a non-empty `account_name`, so an agent identified by a
        MAILBOX alone — legal xAPI, and what a platform that issues no
        accounts sends — cannot be constructed here at all, and no document
        can be keyed by one. The first draft carried an mbox fallback; it was
        unreachable code. It is gone, the constraint is asserted by a test,
        and the key still carries its `account:` namespace so that adding an
        mbox branch later cannot silently merge an account `x@y` with a
        mailbox `x@y`. The cells are `compatible` rather than `lossy` because
        such an agent is REFUSED 400 rather than stored with its mailbox
        dropped — a refusal is not a silent mapping.
        AND THE OBJECTIVES ARE TYPED NOW TOO. `cmi.objectives.n` was the other
        free-form casualty beside the interactions: a package reported what a
        learner had ACHIEVED and it arrived inside `runtime_data`, a dict
        nothing checked, so an objective scored twelve out of ten and carrying
        a status its own edition does not define was stored exactly like a
        good one. `ScormObjective` — the symbol M10.7.a named as the witness —
        is real, and a score that does not hold together against its own range
        will not construct.
        AN OBJECTIVE IS NOT AN INTERACTION, and the two are kept apart on
        purpose: an interaction is one question, an objective is a thing the
        course says the learner can now do, and several interactions bear on
        one. That is why an interaction NAMES objectives by id rather than
        repeating them — if it carried the objective's own score, two
        questions could disagree about what a learner had achieved and nothing
        would say which was the record.
        AND NOTHING AN EDITION COULD NOT HAVE WRITTEN. SCORM 1.2 has ONE
        objective status word and no scaled score, so a 1.2 package reporting
        a completion apart from its success, or a scaled score, is refused —
        the same rule the interactions got. `tests/test_scorm_objectives.py`,
        13 tests. The matrix is 32 HTTP, 7 library and 10 unsupported.
        SCORM SEQUENCING IS BUILT, 2026-09-18, and this cell closes with it.
        A package was one launchable activity; it is an activity tree now, and
        the manifest carries `imsss:sequencing` for a 2004 package.
        A DOCUMENTED SUBSET, WITH THE REST REFUSED BY NAME.
        `schemas/scorm_sequencing.py` lists 30 constructs honoured and 7
        refused, each with the section of the book that defines it, and
        `services/scorm_manifest.py` RAISES on a manifest asking for
        randomisation, constrained choice, rollup considerations, delivery
        controls, auxiliary resources, an exit-condition rule or `retryAll`.
        Dropping one silently would deliver a different course from the one the
        author sequenced, and the learner would have no way to tell.
        The rulebook is `services/scorm_sequencing.py`, with the book's own
        process identifiers on its functions (`OP.1`, `NB.2.1`, `SB.2.3`,
        `RB.1.5`) and the book's own exception codes on its refusals, so it can
        be read against the specification rather than against its author.
        Honoured: the four control modes; flow forward and back including
        descent into a cluster; choice, with `hiddenFromChoice`, `disabled` and
        `choiceExit` each refused by their own code; `skip` and
        `stopForwardTraversal`; attempt limits; post-conditions
        (`continue`/`previous`/`retry`/`exitParent`/`exitAll`); measure,
        objective and completion rollup with `all`/`any`/`none`/`atLeastCount`/
        `atLeastPercent` child sets and objective measure weights; and start,
        resume, suspend, exit and abandon.
        TWO RULES THAT ARE EASY TO GET WRONG AND ARE TESTED FOR:
        - An unknown is not a false. SN 4.5 evaluates a rule combining an
          unknown condition to unknown, and an unknown rule does not fire; a
          "not satisfied" retry that read silence as failure would send a
          learner round again on a judgement nothing had made.
        - A cluster with no rollup rule keeps what it held. SCORM gives no
          cluster a default, and inventing one would satisfy a section nobody
          said was satisfied.
        DURABLE FROM THE FIRST LINE (migration 078,
        `services/scorm_sequencing_store.py`). A sequencing session is a
        learner's place in a course; in process memory — where the rest of the
        SCORM service still is, which is M8.1's separately recorded gap — it
        would be lost on every deploy, sending the learner back to the first
        activity and handing back the attempts an author limited. Two tabs are
        two writers: every write names the version it read and the loser is
        told rather than silently overwriting.
        Over HTTP: `POST /api/scorm/sequencing/{launch,navigate,tracking}` and
        `GET /api/scorm/packages/{id}/activity-tree`, launch-token
        authenticated like the runtime commit and declared as such in
        `DECLARED_POSTURES`. A player may only report on the activity it is
        RUNNING — otherwise one SCO could set another's objectives, which is
        how a course marks itself complete.
        Evidence: `tests/test_scorm_sequencing.py` (29, the rulebook),
        `tests/test_scorm_sequencing_manifest.py` (16, write and read back,
        including a whole-tree round trip and every refusal) and
        `tests/test_scorm_sequencing_postgres_integration.py` (10, over HTTP
        against a live PostgreSQL, read back through a second engine). 173
        more across the contract, route-matrix, gap-map and migration gates.
        THE MATRIX MOVES WITH THE CODE: `sequencing/sequencing-and-navigation`
        is a served cell for both profiles — `compatible` for 2004 and
        `version-skew` for 1.2, because SCORM 1.2 has no `imsss:sequencing` and
        writing 2004 elements into a 1.2 manifest would send a player rules its
        own edition does not define.
  - [x] M10.7.d Run Caliper entity/event/profile/privacy/order and Open Badges
        achievement/assertion/evidence/signature/status/revocation fixtures.
        Evidence: Caliper/badge suites.
        CLOSED 2026-09-18. The two things that kept it open were built in the
        body below and the checkbox was never flipped; re-verified today rather
        than taken on the note's word:
        - `verify_proof` and `read_credential_status` exist in
          `services/open_badges_proof.py` and are exposed through
          `open_badges_service` — the two symbols M10.7.a's matrix named as the
          witnesses whose ABSENCE made those rows true.
        - The registry, read from the code today, holds NO unsupported cell and
          NO remaining witness for either profile: open-badges is 2 compatible
          and 6 lossy, caliper 2 compatible and 3 lossy.
        - 138 tests pass across `test_open_badges_service.py`,
          `test_open_badges_proof.py`, `test_caliper_profiles.py`,
          `test_caliper_service.py` and `test_outbound_ssrf.py`, and the
          adversarial grep over both badge modules returns nothing.
        The honest limits below are unchanged and are limits, not gaps: no
        third-party-issued badge has been verified here, and the Caliper
        acknowledgement position is as volatile as the event log it points
        into.
        WAS OPEN ON BADGE SIGNATURE AND STATUS/REVOCATION. Neither is a fixture
        that failed; both are things this estate does not do for somebody
        else's badge, and they are the two that decide whether an inbound
        badge means anything. Closing this means verifying a foreign issuer's
        proof and reading a credential status list. Both are cells in
        M10.7.a's matrix with their reasons.
        CALIPER DONE — and all three of entity/privacy/order were defects the
        contract already forbade:
        - THE GRADING PROFILE COULD NOT BE RECORDED.
          `metis-interoperability-profiles.json` pins `GradeEvent`/`Graded` →
          `GradingProfile`, and the request schema's literals listed neither,
          so the one profile the contract names for grades was rejected at the
          door. The contract row and the code now agree, and the first test
          reads the rows out of the contract so they cannot drift apart again.
        - EVENTS CAME BACK IN ARRIVAL ORDER. The contract pins
          `eventTime-ascending-then-sequence`; `_filter_events` returned
          insertion order, so an event that HAPPENED an hour earlier and
          arrived five minutes later sorted last — the exact case ordering
          exists for. Now sorted by when it happened, with arrival only
          breaking ties, in queries and in the exported envelope alike.
        - THE LEARNER'S NAME LEFT THE ESTATE. The contract pins
          `directPiiSerialized: false` and every payload carried
          `actor.name` — including the sensor envelope, which goes to somebody
          else. What leaves now identifies the actor without naming them;
          Metis's own record still has the name, and an OBJECT may still be
          named, because a lesson is not a person.
          `tests/test_caliper_profiles.py`, 11 tests, covering entity identity,
          per-event profile, dedup, privacy and order.
        OPEN BADGES: the module had NO tests, which for an importer is the
        worst place to have none — everything it does is decide whether
        somebody else's document is trustworthy enough to read.
        `tests/test_open_badges_service.py`, 27 tests: the round trip, the
        deterministic write (sorted keys, no whitespace drift, so a badge can
        be hashed), the required contexts and types, and fourteen documents
        that are badge-shaped and are not badges (a credential of another type,
        an achievement with no criteria, a subject that names nothing, a
        `validFrom` reading "one tuesday"), plus an alignment with no target,
        which is dropped rather than half-read.
        A REAL GAP CLOSED: EVIDENCE. An achievement says what was required;
        evidence says what was actually SHOWN, and it was dropped on read and
        had nowhere to go on write, so an imported badge was a claim with
        nothing behind it. It round-trips now, and an entry with no narrative
        is a link rather than a record and is not read as one.
        A NON-GAP CONFIRMED: `proof_present` is recorded as CLAIMED and never
        as verified. The suite proves the distinction with a forged document —
        an impostor issuer and the string `obviously-not-a-signature` — which
        parses as present and unverified, because treating a proof's presence
        as proof is how badge systems are fooled.
        The matrix moves with the code: `caliper-1.2/event/ordered-read` and
        `open-badges-3.0/evidence/read-and-write` are served cells now, and
        what remains unsupported says so more precisely — `verify-proof`
        because `credential_service.verify_credential` checks a proof against
        METIS'S OWN verification method, so a foreign issuer's badge comes back
        `not_found` rather than verified. 25 HTTP, 5 library, 19 unsupported.
        BOTH ARE BUILT NOW, AND THE ITEM STAYS OPEN ON ONE STEP OF TRANSPORT.
        `services/open_badges_proof.py` verifies a foreign issuer's
        `eddsa-jcs-2022` proof and reads a BitstringStatusList entry, exposed
        as `open_badges_service.verify_proof` and `.read_credential_status` —
        the exact two symbols M10.7.a's matrix named as the witnesses whose
        ABSENCE made those rows true, so the rows flipped by being made real.
        `tests/test_open_badges_proof.py`, 29 tests. The matrix is 28 HTTP,
        7 library and 14 unsupported (the sentence above was written when it
        was 25/5/19 and later items moved cells without updating it).
        WHY THAT SUITE AND THAT KEY FORM. `eddsa-jcs-2022` over a `did:key`
        is the combination checkable with NO NETWORK — the public key is
        INSIDE the identifier — so the first badge verification this estate
        performs adds no fetch, no resolver and no registry to trust.
        A VERDICT WITH TWO VALUES WOULD BE A LIE. A badge signed with a suite
        this estate does not implement is UNCHECKED, and a verifier that
        answers only valid/invalid has to call it a forgery. `no_proof`,
        `unsupported_cryptosuite`, `unresolvable_verification_method` and
        `uncanonicalizable` all mean "nobody here looked"; `signature_invalid`
        is the only outcome that accuses the issuer, and `NOT_CHECKED` names
        the difference so a caller cannot read one as the other.
        AND A VERIFIED PROOF NAMES THE KEY, NOT THE ISSUER. It says the bytes
        were signed by that key. Whether that key belongs to the institution
        on the badge is a trust decision, and there is no issuer registry to
        make it against — the same absence M10.13.f, M10.14.e and M10.20.a
        each record. The verdict says so in its own detail string.
        A CANONICALIZATION THAT IS NEARLY RIGHT REPORTS A GOOD BADGE AS A
        FORGERY, so JCS is implemented properly rather than borrowed.
        MEASURED: this estate's existing `canonical_json_bytes` sorts by code
        point and RFC 8785 sorts by UTF-16 code unit — `😀` and `＀` come out
        in opposite orders — and Python writes `1.0`, `1e-07` and `1e-06`
        where ECMAScript writes `1`, `1e-7` and `0.000001`. A number this
        canonicalizer cannot reproduce EXACTLY is refused as
        `uncanonicalizable` rather than hashed differently from the way its
        issuer hashed it.
        STATUS: a revoked badge no longer reads as valid. A supplied list is
        decoded (multibase, gunzip, bounded at 16 MiB against a compression
        bomb) and the bit read most-significant-first, which a test checks at
        all eight positions in a byte — read the other way round and every
        badge in the first byte gets somebody else's status. A suspension
        list is refused as an answer to a revocation question.
        AND THE LIST IS FETCHED NOW, THROUGH THAT GUARD. `fetch_status_list`
        is the THIRD fetch somebody outside aims, and the most attacker-chosen
        of the three: an LTI fetch needs a registration an administrator made
        or a launch a platform signed, and this one needs only somebody
        handing over a badge. It goes through `refuse_unless_public` and the
        same pin the LTI paths got in M10.8.e — dial the address that was
        approved, keep the hostname for `Host` and the TLS SNI — with a
        bounded read and a bounded decompression behind it.
        FETCHING IS OPT-IN, because it makes this service issue a request
        somebody else chose; without `fetch=True` the badge's URL is never
        dialled. And a list that cannot be fetched reads `unreadable_list`,
        never `unset` — the failure that would matter most is a badge whose
        status could not be read coming back looking like a badge that is
        fine, and a test asserts it does not.
        `test_outbound_ssrf`'s "the two fetches somebody else aims" became
        "named one by one", which is the assertion that was always meant.
        AND A CALIPER CONSUMER CAN TELL A GAP FROM AN EMPTY PAGE. Export was
        a query by cursor with no acknowledgement, so a consumer that asked
        for the next page and received an empty one could not tell whether
        there was nothing new or whether something had gone missing: both look
        identical from outside. `POST /api/caliper/deliveries` records how far
        a consumer has processed and answers with `outstanding` — so an empty
        page beside a non-zero count is a problem it can SEE.
        TWO REFUSALS, BOTH ABOUT NOT HIDING A GAP. An acknowledgement that
        goes BACKWARDS is refused: a consumer that has processed through 40
        and then claims 20 would make work already done report as work still
        to do, which reads exactly like a gap that is not there. And one PAST
        the highest sequence recorded is refused: nobody can have processed an
        event this estate never sent, and taking the claim would mark real
        events as delivered. A repeat of the SAME position is not a refusal,
        because a retry after a timeout is the ordinary case.
        A consumer that has never acknowledged reads zero rather than absent —
        nothing processed is a position, not a missing record — and two
        consumers keep their own, because one being behind is not the other's
        problem.
        RECORDED AS A LOSS: the position is held beside the event log and is
        as volatile as it is. An acknowledgement of events a restart forgets
        cannot outlive them, so making this durable means making the log
        durable first, and doing only half would let a consumer's position
        outlive the events it points into.
        AND ENDORSEMENTS ARE READ NOW. `parse_endorsement` — the last symbol
        M10.7.a named for this profile — reads an `EndorsementCredential` into
        who vouched, what they vouched for, and what they said, so who else
        stands behind an achievement is no longer lost on the way in.
        THE SUBJECT IS THE WHOLE POINT. An endorsement whose subject names
        NOTHING is refused rather than parsed, because an endorsement of
        nothing reads as vouching for whichever credential happened to be
        beside it — which is exactly how an endorsement gets borrowed. And a
        document claiming to be an `OpenBadgeCredential` AND an
        `EndorsementCredential` at once is refused, because that would let an
        issuer award a badge and endorse it in one document and have the
        endorsement counted as somebody else's word.
        An endorsement with no comment still endorses: weaker, and still
        somebody's word, and refusing it would drop a real one for saying
        little. Its proof is CLAIMED, like a badge's — but unlike when that
        rule was written there is a verifier now, and a caller that wants an
        endorsement checked passes it to `verify_proof` rather than reading
        `proof_present` as an answer.
        The matrix is 32 HTTP, 8 library and 9 unsupported.
        HONEST LIMIT OF THE EVIDENCE: the suite signs with the same
        canonicalizer the verifier uses, so it proves the chain agrees with
        ITSELF. No badge issued by a third-party implementation has been
        verified here — none exists offline and inventing one proves nothing.
        What IS independently checked is the serializer, against canonical
        strings written out by hand. The residual risk is a canonicalization
        difference on a shape no test covers, and its symptom would be
        `signature_invalid` on a genuine badge.
  - [x] M10.7.e Run internal gradebook import/correction/visibility/LTI-
        OneRoster reconciliation with duplicate/out-of-order events. Evidence:
        grade interoperability suite.
        CLOSED 2026-09-18. It was open on the prerequisite below, and M10.7.b
        closed it: `result` is modelled and published, so reconciliation across
        the two roads is a JOURNEY a grade takes rather than a capability of
        the ledger. A score arrives on the LTI AGS road, a retry of it is one
        event, a rescore that was true earlier and arrived last never becomes
        the grade in force, and what the SIS reads is what the ledger DERIVES —
        asserted from the other side, against `project()`, rather than restated
        from the publisher.
        AND THE DEFECT THIS CELL RECORDED AND DID NOT FIX IS FIXED: THE GRADE
        WAS DURABLE AND THE LINE ITEM IT HANGS ON WAS NOT. Line items lived in
        a class-level dictionary and `sync_score` refuses one it cannot find,
        so after a restart a platform posting to a line item METIS ITSELF
        created was answered `line item is not registered`, 403, forever, with
        nothing it could do about it. `lti_line_item` (migration 081) is that
        table, written where `create_line_item` writes.
        It is NOT the binding table, deliberately: `grade_ingress_binding` says
        what a line item SCORES and somebody has to assert that; this says the
        line item EXISTS, which is a thing this tool did. Conflating them would
        let a registration nobody made be inferred from a binding somebody did.
        THE STORE IS CHOSEN BY NAME AND NEVER FALLEN BACK TO.
        `LtiService.use_line_item_store(InProcessLineItems())` is how a suite
        with no database asks for an in-process one, the way
        `use_jwks_cache` already worked; a store that silently became
        in-process when the database was unreachable would put the defect
        straight back. `reset()` now forgets WHICH store is in use rather than
        emptying one — a reset that cleared the table would be one import away
        from clearing a real tenant's line items.
        Evidence: `tests/test_grade_ingress_journey.py` 28 tests (the new one
        creates a line item, clears exactly what a restart clears, and posts a
        score to it successfully), `tests/test_oneroster_gradebook_postgres_integration.py`
        14, `tests/test_grade_interoperability.py` 16, and 115 across every
        suite that constructs an `LtiService`.
        WHAT WAS OPEN, AND WHY IT IS NOT NOW: the OneRoster road could not be
        built because `result` was not modelled at all (M10.7.b). It is.
        WHAT WAS OPEN, AND IS NOW CLOSED FOR LTI AGS. The durable ledger M8.11
        built HAD NO WRITER: nothing under `src/` imported
        `services/grade_ledger.py` except the model registry, and a score
        posted over LTI AGS landed in `LtiService`'s class-level dicts, which
        `reset()` clears and a restart forgets. `POST /lti/assignments/scores`
        now carries the accepted score into the ledger, and
        `tests/test_grade_ingress_journey.py` proves it the only way worth
        proving: post a real score with a real AGS token, clear exactly what a
        restart clears, and read the grade back out of Postgres. 26 tests
        against a freshly migrated database.
        THE HARD PART WAS A FIELD NOTHING CARRIES. A `LedgerEvent` names the
        attempt and the `item_revision_sha256` the value was arrived at
        against — that is the point of M7.10's chain — and an AGS post carries
        a number, a user and an opaque line-item id. There is no honest
        derivation from an identifier to a content digest, so the digest is
        NOT derived: `grade_ingress_binding` (migration 074) is where somebody
        says what a line item scores, and a score against an unbound line item
        is refused rather than recorded with an invented revision. A ledger
        holding a confident digest of nothing is worse than an empty one,
        because the empty one is visibly empty.
        AND THE TENANT COMES FROM THE SAME PLACE, for the same reason. An AGS
        post says nothing about whose records it belongs in — the platform
        registration does not record who registered it — so binding a line
        item is a tenant CLAIMING it, and a line item two tenants claimed is
        refused naming both rather than assigned to whichever sorted first.
        WHAT IS DERIVED, IS DERIVED AND NOT GUESSED. The grade and the attempt
        are the `(platform, line item, user)` triple, which is the platform's
        own notion of one person's work on one assignment; the learner and the
        actor are digests, not names, which is M10.7.d's Caliper finding
        applied before it could recur; and the source sequence is the count of
        posts applied to that result, because `lti_ags` is a bursting source
        and the protocol supplies no counter — two posts bearing one instant
        are an `ambiguous_order` refusal without it, which a test asserts.
        THREE REFUSALS THAT ARE NOT GAPS, each with its own test: a line item
        nobody bound; a score the platform reports as `Pending`,
        `PendingManual` or `Failed`, because a provisional number recorded as
        `scored` is the grade in force until something else arrives; and a
        line item whose maximum disagrees with the bound scale, because
        rescaling 40/50 into 80/100 on the way in is exactly the silent
        mapping M10.7.f blocks.
        A REFUSED SCORE DOES NOT FAIL THE POST. AGS is the platform's protocol
        and its rules were met; failing a well-formed post because nobody on
        this side bound the line item would make a Metis omission look like a
        platform error. The post stands and `LtiScoreResponse.ledger` carries
        the receipt, so the platform is told exactly what became of the number
        instead of being told nothing.
        The read path is `GET /api/gradebook-ingress/projection`, derived from
        the entries on every read rather than served from the stored row — a
        projection that has fallen behind its own ledger is the failure a read
        path can hide — and scoped to the caller's own claim, so a line item
        somebody else bound answers the same 404 as one nobody bound.
        RECORDED, NOT FIXED, AND FOUND BY BUILDING THIS: THE GRADE IS DURABLE
        AND THE LINE ITEM IT HANGS ON IS NOT. `_line_items` is the same kind
        of class-level dict the scores were in, and `sync_score` refuses a
        line item it cannot find — so after a restart a platform posting to a
        line item Metis itself created is told `line item is not registered`
        and answered 403, forever. The grade road is durable from the post
        onward and the thing being posted about evaporates. The fix has the
        shape this item just built: a table, written where
        `create_line_item` writes today. The binding table is NOT that table —
        it says what a line item scores, not that it exists — and conflating
        them would let a registration nobody made be inferred from a binding
        somebody did.
        The absence is held as its inverse now:
        `test_the_grade_ledger_has_a_writer_under_src` fails the day the
        ingress is deleted, because every ledger test would still pass
        without it and the store would quietly go back to being one no road
        reaches.
        `tests/test_grade_interoperability.py`, 16 tests against a freshly
        migrated Postgres, driving import, correction, visibility, duplicates
        and out-of-order arrival across all five sources at once.
        THE DEFECT IT WAS WRITTEN TO FIND: THE ALPHABET WAS DECIDING WHICH
        GRADE STOOD. `SourceRef.ordering_key` ended in the system's NAME, and
        the ambiguity check compared that same key — so a tie between two
        systems was invisible to the check that exists to catch ties, and was
        broken by `lti_ags` < `oneroster`. A platform post and a roster record
        landing on the same second ordered by spelling, and the one that sorted
        last became the grade in force. The module's own docstring says a tie
        is an ambiguity and not a coin flip; across sources it was a coin flip
        with the alphabet as the coin.
        A SEQUENCE IS A COUNTER INSIDE ONE SOURCE — an import's first event is
        not before a platform's seventh — so `separates_from` now asks what the
        sources actually said: a different instant separates them, the same
        instant from two systems does not, and a source's own counter separates
        only its own events. Such a pair is refused as `ambiguous_order` naming
        BOTH systems, and `as_history` refuses to derive over one too, so a
        ledger that somehow holds one cannot quietly project a winner.
        A SECOND, SMALLER ONE: a projection over entries that do not yet make a
        history — a rescore whose opening score has not arrived, which is
        ordinary while an import catches up — reached the caller as a pydantic
        validation dump from two layers down. It is a `LedgerError` naming the
        grade and how many entries are waiting; the entries stay, and the
        projection works the moment the score lands.
        What the fixtures confirmed already worked: a retry is one event, and a
        retry carrying the version its first attempt carried is still accepted
        because a delivery guarantee is not a stale-version error; one
        identifier from two systems is two events; a term replayed BACKWARDS
        into a second tenant produces a byte-identical history; one system
        corrects another's event by the id that system used; and M7.10's
        visibility matrix over a ledger-derived history withholds the value
        from a learner before release and shows them the correction after it.
  - [x] M10.7.f Publish per-cell compatible/lossy/extension/unsupported/version-
        skew results and block silent mapping. Evidence: compatibility report.
        Evidence: `evidence/v1-workbenches/metis-interop/compatibility-report.json`,
        generated by `scripts/export_compatibility_report.py` from the same
        registry M10.7.a's matrix comes from, with 21 tests in
        `tests/test_interop_compatibility.py` and 15 in `tests/test_interop_loss.py`.
        All 49 cells classified: 15 compatible, 13 lossy, 4 extension, 1
        version-skew, 16 unsupported. `fidelity` is a REQUIRED field, so a new
        cell cannot arrive unclassified, and a label its own fields contradict
        will not construct — a compatible cell that names a loss, a lossy one
        that names none, an extension that says nothing it adds.
        BLOCKING SILENT MAPPING, and it was not blocked: EVERY INBOUND
        STANDARDS PAYLOAD WAS SILENTLY DISCARDING WHATEVER IT DID NOT MODEL.
        Pydantic's default is to drop an undeclared field without a word, so a
        SIS sending `middleName`, `grades` and `agents`, a platform sending an
        xAPI `authority`, a sensor sending a Caliper `session`, or a player
        committing `cmi.exit` got back a success over a record quietly smaller
        than the one it sent. Refusing the whole sync over `middleName` is not
        interoperability either — so the payload is now walked against the
        model that parsed it and every unread path comes back in
        `fields_not_carried` on the response. `services/interop_loss.py`, wired
        into all four ingresses (OneRoster sync, xAPI statements, Caliper
        events, SCORM commit), bounded in depth, breadth and list length
        because the payload is somebody else's. The rule is then enforced in
        the type: a cell that RECEIVES over HTTP and declares a loss nothing
        announces WILL NOT CONSTRUCT, and the report publishes `silent_losses`
        (empty) so a reader can check it without running the suite. The rule
        binds where a sender exists to be told — an HTTP request with a
        response. A library call (the badge parser) has no sender to answer, so
        its losses are published in the report instead, which is the other way
        of not being silent; the three cells it applies to say so in their own
        rows.
        THE SECOND RATCHET, WHICH IS WHY THIS ITEM FOUND ITS OWN CHECKLIST
        STALE: an implemented cell proved itself by naming a handler that
        exists, and an UNSUPPORTED cell proved nothing at all. M10.7.b built
        `roster_reference`, the delta watermark and `tobedeleted` tombstones,
        and three cells went on reporting all three as unbuilt for an entire
        item — the matrix over-reported gaps and nothing could contradict it.
        Every unsupported cell now names the symbol whose ABSENCE makes it
        true (`OneRosterLineItemRecord`, `ScormSequencing`, `XapiStateDocument`,
        `open_badges_service.verify_proof`), and the test fails the day one of
        them exists. The three stale rows are corrected: sourcedId persistence
        and tombstones are compatible, the delta is an extension (a
        payload-level watermark, not OneRoster's REST filter), and the identity
        join's own row now says what it does — sourcedId first, a normalized
        email fallback ONLY on a first sync, which M1.13's `emailAsJoinKey:
        false` does not sanction and without which a first sync copies every
        user the estate already has.
        17 losses named as field paths, each demonstrated by calling the code:
        SCORM 1.2 refusing `long-fill-in` (the one version-skew cell), the
        Caliper envelope leaving `actor.name` behind, `fields_not_carried`
        naming what a badge cannot hold — and A BADGE'S `validUntil` IS NEVER
        READ, so an expired badge parses exactly like a live one, which the
        report now says out loud.
        The matrix moves with it: 28 HTTP, 5 library, 16 unsupported.
- [ ] M10.8 Run BFF authorization, idempotency, concurrency, pagination, rate,
      SSRF, and tenant negative suites. Evidence: route matrix complete.
      <!-- SPEC 2026-09-11: .a's inventory is generated from
      `apps/metis/service/openapi/metis.openapi.json` (767 operations) joined to
      the M8.15 classification register in `services/api_contract.py`; never a
      hand-written route list. .b runs anonymous plus the three JWT roles plus a
      second tenant, and because `student` is the default role every learner
      case is also the "token with no role claim" case. .e enumerates every
      outbound fetch by listing the `httpx` call sites under
      `src/metis/services` (source connectors, LMS/LTI, webhooks, media) before
      writing SSRF cases. .f is a `verify` gate: a route/role/failure row with
      no test fails it. -->
  - [x] M10.8.a Generate full route-method-operation/role/audience/object/
        property/function inventory from runtime/OpenAPI and map tests.
        Evidence: route matrix.
        Evidence: `evidence/v1-workbenches/metis-bff/route-matrix.json`,
        generated by `scripts/export_route_matrix.py` from
        `services/route_matrix.py`, with 17 tests in `tests/test_route_matrix.py`.
        882 operations — the SPEC's 767 was 2026-09-06 and the surface has
        grown — each joined to the function that serves it, the roles that
        reach it, its audience, its object, the protected properties its
        responses can reach, and the test modules that name its path. Nothing
        is hand-written: the operations and statuses come from the published
        document, the handler and its dependencies from the AST of
        `metis/api/*.py` and `metis/main.py`, the posture from M8.15's
        register, and the test mapping from path literals in `tests/*.py`.
        653 operations are named by a test and 229 ARE NAMED BY NONE. The
        mapping is deliberately literal — a test reaching a route through a
        helper is not seen — so 229 is an upper bound on what is untested and
        the right direction for a coverage gate to be wrong in. Worst by
        object: admin 57 of 198, item-bank 28 of 137, and measurement-ledger
        12 of 12 — a whole surface no test names. That list is M10.8.f's.
        THREE DEFECTS IN THE INVENTORY ITSELF, each found by a check written to
        fail: 4 operations matched no handler (a trailing slash normalised
        away, a `{asset_path:path}` converter published without its converter,
        and `/health` and `/ready` hanging off the app rather than a router);
        an f-string's trailing piece was being read as a path of its own; and,
        the one that mattered, NINE ROUTES WERE READ AS ANONYMOUS BECAUSE THEIR
        AUTHENTICATION WAS ONE DEPENDENCY DEEPER — `export_diagnostics` routes
        depend on `export_tenant`, which depends on `get_current_user`. A scan
        that reads a route as open when it is not is wrong in the worst
        direction, so the dependency graph is walked, `metis/deps.py` included.
        Roles now: 537 instructor, 179 any-authenticated, 125 admin, 41
        anonymous — and every anonymous one has a posture M8.15 declared open,
        checked against the register rather than restated.
        The protected-property column reads 0, and 0 is only worth reading
        because the report names what was looked for: one published schema
        carries one (`AssessmentContext.answer_key`) and only a REQUEST body
        reaches it, so it is accepted and never returned. The walk that found
        nothing is proven able to find something — a synthetic schema three
        deep through a list and a union — and to survive a reference cycle.
  - [x] M10.8.b Test anonymous/learner/guardian/author/reviewer/teacher/admin/
        operator and cross-tenant IDs/fields/search/counts/exports. Evidence:
        authorization suite.
        CLOSED 2026-09-18. Both halves are done: the cross-tenant read sweep
        below (2026-09-12) and the persona vocabulary the last paragraph was
        open on.
        THE EIGHT ARE EXPRESSIBLE NOW (`schemas/personas.py`). A capability
        table says what each persona may do, and `CurrentUser` asks it rather
        than asking which of two words a token carried — which is why five
        personas could not be added before without rewriting every route.
        A NEW PERSONA WIDENS NOTHING. `student` still means learner and
        `instructor` still means teacher, asserted by a test over all three
        legacy words, so no token that exists reaches anything it did not
        reach yesterday. `is_instructor()` asks for `teach_class` rather than
        `author_content` deliberately: that seam gates 537 operations, and
        granting all of them to the new `author` and `reviewer` would be the
        one thing adding a vocabulary must not do.
        A ROLE NOBODY DEFINES RESOLVES TO NOTHING, not to the narrowest
        persona: a token carrying `role: "superuser"` must not quietly become a
        learner and work, because then nobody finds out that the issuer and
        this service disagree.
        THE SWEEP RUNS ALL EIGHT: `tests/test_route_authorization.py` offers
        every published operation to anonymous, a token with no role claim, a
        learner, a guardian, an author, a reviewer, an operator and a teacher,
        each below its bar — 18 tests, every offer refused 401 or 403 and never
        422, because a validation error means the request reached the body.
        WHAT WAS DELIBERATELY NOT DONE, and is now asserted so a later item
        re-takes the decision rather than drifting past it (user decision
        2026-09-18): the 537 operations behind the instructor seam KEEP it.
        Which of them are authoring, which reviewing and which teaching is a
        per-route judgement the checklist does not supply, so an author, a
        reviewer, a guardian and an operator reach what a learner reaches and
        no more — which the sweep proves rather than assumes.
        WAS OPEN ON FIVE OF THE EIGHT PERSONAS AND ON THE CROSS-TENANT READ
        SWEEP.
        `CurrentUser.role` holds one of THREE values — student, instructor,
        admin — so guardian, author, reviewer, teacher and operator are
        distinctions this item asks the surface to make and it cannot make
        them: every route that should tell a guardian from a learner, or a
        reviewer from an author, sees the same caller. And the two-tenant read
        sweep (create in A, read as B, over ids/fields/search/counts/exports)
        exists for the eight authoring routes M8.14.b built it for, not for
        the other 874. Closing this means a role vocabulary that can express
        the eight and a generated two-tenant sweep.
        THE CROSS-TENANT SWEEP IS NOW GENERATED AND RUN, 2026-09-12.
        `tests/test_cross_tenant_read_sweep.py` takes every GET operation in
        the published document that needs no path parameter — 197 collection
        reads — and offers each to a second instructor who has created
        nothing. 138 of them answered 200, 46 refused with 403, and NOT ONE
        returned any of the 8 identifiers belonging to the first tenant.
        The assertion is over BYTES, not counts: a count passes whenever two
        tenants happen to hold the same number of rows, and a field check
        misses an id echoed inside a nested payload or an error message. The
        sweep refuses to pass vacuously — it asserts it had identifiers to
        look for and that it reached more than twenty permitted reads.
        AND IT FOUND A LIVE 500. `/api/admin/authoring-hub/workspace`
        answered `internal_server_error` for the second instructor and 200
        for the first. `AuthoringHub.viewer_id` was typed `Identifier`,
        whose pattern is `^[a-z][a-z0-9._:-]{2,159}$` and so requires a
        leading lowercase LETTER — but a viewer id is a USER id, which is a
        UUID. Ten of the sixteen possible leading hex characters are digits,
        so the authoring hub was unopenable for most real callers and fine
        for the rest, which is why it looked like it worked.
        The unit tests never caught it because they passed
        `viewer_id="actor:one"`, a slug shape the route never produces. The
        field is a `uuid.UUID` now, the tests pass a real user id, and both
        a digit-leading and a letter-leading account answer 200 against the
        running service. One schema moved in the published document.
        WAS STILL OPEN ON: the five personas. `CurrentUser.role` held three
        values and the item names eight. Closed 2026-09-18, above.
        DONE, and it found a cross-tenant hole. `tests/test_route_authorization.py`
        drives the REAL application with REAL bearer tokens — nothing
        overridden, because the dependency you would override is the thing
        under test — and offers all 882 operations to every caller below their
        bar: anonymous, a token with NO ROLE CLAIM (which defaults to
        `student`, so it is a learner's token), a student and an instructor.
        1,628 offers, every one refused 401 or 403 and NEVER 422, because a
        validation error means the request reached the body, which means it
        passed the gate. Four bad tokens — signed by somebody else, expired,
        carrying no subject, and not a token at all — are each 401.
        THE DEFECT: A CALLER COULD NAME ITS OWN TENANT. `oneroster_service`
        opened with `tenant_id = body.tenant_id or actor_user_id`, so an
        authenticated administrator could put any tenant's id in the request
        body and read, write and re-point THAT tenant's `roster_reference`
        rows — the mapping that decides which Metis user a SIS sourcedId
        becomes on every later sync, so re-pointing it lands the next sync's
        name, email and withdrawal on an account of the attacker's choosing.
        M8.14 established the property this breaks: a caller cannot name its
        own tenant. The tenant is now derived from the authenticated user, by
        a `deps.tenant_of` shared with the twenty modules that had each
        written the same `uuid5` inline — which is how a twenty-first came to
        take it out of a request body — and a body that names a different one
        is REFUSED 403 rather than quietly ignored, because ignoring it would
        leave a caller believing it had written somewhere it had not.
        A SECOND FINDING, IN THIS SESSION'S OWN GATES: three drift gates
        (M10.7.a's coverage matrix, M10.7.f's compatibility report, M10.8.a's
        route matrix) compared the committed artifact to the generator BYTE
        FOR BYTE. The commit hook reformats generated JSON after the generator
        writes it, so all three were green in the working tree and RED at
        HEAD. They compare documents now, and the fix is verified by running
        prettier over the artifacts and re-running the gates.
  - [x] M10.8.c Test idempotency key replay/conflict/scope,
        ETag/expected-version races, simultaneous edits/decisions, and event
        dedup/order. Evidence: mutation suite.
        CLOSED 2026-09-18 ON WHAT THE DOCUMENT SAYS — the last open half.
        ALL 177 KEYED OPERATIONS PUBLISH THE CONFLICT NOW; it was 0, then 95,
        and the remaining 82 were held as a ceiling with the note that
        shrinking it meant "reading those mappings rather than running a
        script over them". They were read, one at a time, and every one of the
        82 could answer 409:
        - twenty-six modules map a domain error whose status is a runtime
          attribute — five of those classes DEFAULT to 409, so any bare raise
          is one, and the rest pass 409 explicitly at the raise;
        - `agent_runtime` maps any code its table does not list to 409 by
          default;
        - `generation`'s `cancelled` failure class is 409 in `FAILURE_STATUS`.
        Each is documented against that evidence and not one was documented for
        tidiness — documenting a status an operation cannot return is the same
        error pointing the other way.
        THE CEILING IS GONE RATHER THAN SET TO ZERO, which is the rule M8.15's
        own status ratchet was retired under: a baseline over nothing is a gate
        that cannot fail. The test asserts the thing itself, so an operation
        that gains a key without its conflict fails immediately.
        THE EXPECTED-VERSION HALF: 25 of 26 publish 409 or 412; it was 2 of 14.
        THE ONE THAT DOES NOT IS CORRECT AND IS NAMED RATHER THAN COUNTED —
        `POST /api/gradebook-corrections/decisions` answers 200 with a typed
        `CorrectionRefusal` in the body, because a stale version there is a
        DECISION about a correction and not a transport failure: the reviewer
        is told what the store holds and why their decision was not taken, and
        a 409 would lose the reason.
        Evidence: `tests/test_mutation_concurrency.py`, 11 tests, and 142
        across the contract, openapi and route-matrix gates.
        WAS OPEN ON WHAT THE DOCUMENT SAYS. 177 of the 463 mutating operations
        take an idempotency key and NOT ONE publishes the status a replay or a
        reused key answers with; 12 of the 14 taking an `expected_version`
        publish neither 409 nor 412. The behaviour is right now — the last
        test in the suite measures the silence and fails the day any operation
        starts documenting it — but a client generated from this document has
        no branch for the conflict, so it reads a 409 as an unknown error.
        Closing this means the statuses reaching the published contract.
        THE STATUSES HAVE REACHED IT, AND THE RATCHET THAT HELD THEM IS GONE.
        M8.15 recorded FIFTY-SIX modules that raise a status none of their
        operations document — 404 forty-five times, 409 forty, 403
        twenty-one — and held the number as a ratchet because documenting
        them was "a change to ninety route modules' decorators". That change
        is made. The gap is ZERO modules, and the ratchet's own rule said what
        to do next: delete it rather than empty it, because a baseline over
        nothing is a gate that cannot fail. So `STATUS_GAP_BASELINE` and
        `status_gap_ratchet` are deleted and
        `test_no_module_raises_a_status_it_never_documents` asserts the thing
        itself — a module that starts raising something it does not publish
        fails immediately instead of being written down and lived with.
        DOCUMENTED PER HANDLER, NOT PER MODULE, which is the part that needed
        judgement rather than a script. A function's statuses are what it can
        actually raise: the `HTTPException`s in its own body plus those its
        module's error mapper can produce for it. A read that catches nothing
        gets nothing — `/evaluation` on six of these modules promises no 404,
        because it cannot answer one. Documenting a status an operation cannot
        return is the same error pointing the other way.
        AND THE MODULE-LEVEL GATE WAS COARSER THAN IT LOOKED. `admin` was not
        in the baseline at all — it raises 409 and some one of its eighty-nine
        operations documented it, which satisfied a gate that asks about
        modules. Fifty-one of its handlers still published nothing. The
        operation-level measurement in `test_mutation_concurrency` is the
        finer instrument and it is the one this item is written against.
        KEYED OPERATIONS PUBLISHING THE CONFLICT: 0 OF 177 BEFORE, 95 NOW.
        The remaining 82 are held as a ceiling that may shrink and must never
        grow, and they are not one kind of thing. Some handlers genuinely
        cannot conflict. The others raise a domain error whose status arrives
        as a RUNTIME ATTRIBUTE — `ContentFreshnessError` defaults to 409 and
        never writes it as a literal — so the operation answers 409 and no
        static pass can see that it does. Shrinking the ceiling means reading
        those mappings one at a time, which is what keeps this open.
        The refusals-unpublished debt fell with it: 121 operations required a
        role and documented no failure at all; 102 do now.
        DONE, AND THE FIRST CLAUSE WAS A DEFECT. AN IDEMPOTENCY KEY IS A RETRY
        TOKEN AND THIS ONE WAS A UNIQUENESS TOKEN. `aggregate_store` consulted
        the key LAST, through a unique constraint on the outbox, so a caller
        that did not hear the answer and sent the same request again was
        refused `stale` — indistinguishable from somebody else having got
        there first, which is the ONE distinction the key exists to make. The
        key is now asked before the revision checks: the same intent is
        answered with the first attempt's acknowledgement (same revision, same
        ETag, same audit line, same outbox event, `replayed: true`), and a key
        pointed at a different intent is refused, because answering it with
        the first attempt's result would confirm a write that never happened.
        The store's own outbox row carries the audit id now, so a replay
        answers with the acknowledgement that was issued rather than a fresh
        one that merely looks like it.
        A SECOND, SMALLER ONE: that refusal reached the caller as 422
        `validation_failed` — the authoring layer's fallthrough for every
        refusal it had no case for. A well-formed request that names an intent
        the caller has already named differently is a conflict, so the
        published outcome vocabulary gains `duplicate_key` (409). M8.6's own
        rule then did the rest: a state in the contract that nothing produces
        is a promise nobody keeps, so its reachability test obliged a case
        that produces it.
        `tests/test_mutation_concurrency.py`, 10 tests against a freshly
        migrated Postgres: a retry written once and answered twice, an edit
        retried against its own first attempt, a key pointed elsewhere, a key
        with a changed payload, two tenants using ONE key and getting two
        writes, a transition replayed under its own key, a stale revision told
        what the store holds, two threads at one revision producing one
        winner and one refusal with nothing written by the loser, and the same
        retry run twice at once still writing once.
        Event dedup and order were done in M10.7 and M8.11 and are not
        re-measured here: xAPI statement conflict, Caliper event dedup and
        ordering, SCORM commit replay, and the grade ledger's
        `(tenant, system, source_event_id)` with its advisory lock.
  - [ ] M10.8.d Test cursor signing/scope/stability/expiry, filter/sort limits,
        rate/quota/budget/concurrency bypass, and privacy-safe errors. Evidence:
        protocol suite.
        THE RATE LIMITER EXISTS NOW; QUOTA AND CURSOR EXPIRY STILL DO NOT.
        `services/request_throttle.py` joins the three pieces that were lying
        about separately, and `main.py` applies it as middleware — a
        middleware and not a dependency, because the finding was about every
        authenticated route and a dependency would have had to be added to
        eight hundred and ninety-odd operations to answer it.
        `tests/test_backpressure.py` — renamed from
        `test_backpressure_absence.py`, because it is not about an absence any
        more — drives the REAL application against the REAL Redis and watches
        the fourth request of a three-request window come back 429 with a
        `Retry-After`. Its old assertions are the same claims pointing the
        other way: the limiter HAS a caller, the configured number IS read, a
        route CAN answer 429.
        IT IS A FIXED WINDOW AND THE CODE USED TO CALL IT SLIDING. Measured:
        `check_rate_limit` sets a counter with a TTL on the first request and
        increments until the TTL expires, so the window is anchored to that
        request rather than moving with the clock. The docstring said sliding
        and the implementation never was. The consequence is the ordinary one
        and it is now written down: a caller can spend the whole allowance at
        the end of one window and the whole allowance at the start of the
        next, so the real short-term ceiling is twice the configured number.
        `rate_limit_burst` IS STILL READ BY NOBODY, and a test asserts that on
        purpose. A fixed-window counter has no burst parameter to read;
        spending it would mean a token bucket, where a burst allowance is a
        real thing with a refill rate. Folding it into the window maximum
        would be inventing a meaning the name does not have. (That test had to
        stop grepping for the name to find this out — every mention of
        `rate_limit_burst` is now in prose EXPLAINING that nothing reads it,
        so it asks the AST for an attribute access instead.)
        IT FAILS OPEN WHEN REDIS CANNOT ANSWER, deliberately and with a test.
        A limiter keeps load off a service; one that refuses everything when
        its own dependency is down converts a Redis outage into a total
        outage. Every other way of not running names itself too — `disabled`,
        `no_limiter`, `unthrottled_path`, `nonpositive_maximum` — because a
        throttle that quietly does nothing is the state this cell was opened
        on, and "it did not run" should be a value a test can read rather than
        a silence it has to infer.
        IT IS ON IN PRODUCTION AND OPT-IN ANYWHERE ELSE, and that policy was
        WRONG THE FIRST TIME. Enabling it everywhere but the testing
        environment looked obviously right and failed TWENTY-SEVEN suites,
        because they build the real application with default settings —
        `environment` defaults to `development` — and drive hundreds of routes
        at one address in one process. `test_route_authorization` alone offers
        every operation to every caller. Those suites measure the service, and
        a throttle firing inside them measures the suite. The finding was
        about a DEPLOYED service accepting requests without limit, so that is
        where it runs by default; `rate_limit_enabled` turns it on anywhere.
        The policy is a named function rather than an expression inline in the
        middleware, because it is a decision somebody should be able to read
        and test on its own — and because the first version of it was wrong.
        `test_protocol_limits`'s "nothing here limits a rate" flipped with it,
        and the new sentence is the precise one: the limiter is INSTALLED here
        and not ARMED here, so sixty requests still pass and the reason has
        changed from "nothing limits anything" to "this configuration asked
        for no limit".
        AND THE CURSOR EXPIRES NOW. The item-bank cursor — the one opaque
        cursor in the estate, the rest being positions — carries an `iat` and
        is refused past thirty minutes. IN BOTH DIRECTIONS: a cursor issued in
        the FUTURE is refused too, because otherwise a forged issued-at would
        be the way to make one last for ever and the expiry would check only
        honest cursors. Version 1 cursors are refused rather than honoured —
        one with no issued-at cannot be expired, so accepting it would leave
        the hole open for as long as anybody held one, and for a page token
        the cost of that is asking for the first page again.
        NOTHING ABOUT IT IS IN THE PUBLISHED DOCUMENT, deliberately: a
        lifetime is the server's business and a `cursor_ttl` field would be a
        promise a client could rely on.
        A TEST HAD TO STOP ASSERTING DETERMINISM TO ALLOW IT. An issued-at
        makes two otherwise identical searches produce different cursor
        strings, and `test_signed_cursor_is_deterministic_…` compared the
        bytes. It passed only while both calls landed inside the same second —
        a test that fails on a slow morning. It asserts what actually mattered
        now: the same question asked twice continues at the same place,
        checked by decoding rather than by comparing strings.
        STILL OPEN ON QUOTA. A quota is a budget over a longer period than a
        window — a month's generation spend, a term's exports — and nothing
        keeps one. The rate limiter is a window and cannot be read as one.
        WHAT THE ORIGINAL MEASUREMENT SAID. Sixty
        requests in a row to an authenticated route are all served; the only
        middleware this application installs is a response timer and CORS; no
        route answers 429. There is no limiter to bypass because there is no
        limiter, and the test that says so is written to fail the day one
        appears. No cursor carries an issued-at or a lifetime either: the
        signed one binds a SNAPSHOT, so it stops working when the data moves,
        which is stability and not expiry.
        DONE, AND ONE PARAMETER WAS BOTH UNFLOORED AND UNCAPPED.
        `GET /api/authoring/{kind}?limit` was a bare `limit: int = 100` that
        reached `LIMIT :limit` unchecked: `?limit=-1` came back a 500 — the
        database's complaint, rendered as "an unexpected error occurred", for
        what is plainly a bad request — and `?limit=100000000` was simply
        served. It is `Query(default=100, ge=1, le=500)` now, so the bound is
        ENFORCED and PUBLISHED, and `?revision` is floored at 1 because
        revisions start there.
        The rest of the limits hold and are now checked against the document
        rather than believed: every page size a caller can name, in a query
        parameter or a request body, declares a maximum AND a minimum; every
        `sort`/`order_by` is a closed vocabulary rather than a column name; and
        the only integer parameter left without a floor is a stream position,
        recorded by name so a new one has to be looked at.
        CURSORS. One opaque cursor exists — `ItemBankSearchRequest.cursor` —
        and it is signed and bound to the query digest, the snapshot digest,
        the tenant and the role. Every other paginated surface hands back a
        POSITION, which is forgeable into nothing a caller could not already
        ask for, because the query is re-scoped server-side on the next call.
        The test pins that there is exactly one opaque cursor, so a second one
        arrives with a signer or fails here.
        BUDGET is built and CONCURRENCY was proven in M10.8.c: `reserve_budget`
        takes a reservation inside a transaction, so a job that does not fit
        never reaches a provider — but `reserved_cents` appears in NO published
        schema, so a caller cannot see the reservation that binds it.
        PRIVACY-SAFE ERRORS. Another tenant's aggregate answers `not_found`
        rather than `forbidden` — a 403 on a record you cannot see confirms
        that it is there — and carries none of its payload; a validation
        failure is a fixed sentence with a field list and no traceback.
        `tests/test_protocol_limits.py`, 11 tests.
  - [ ] M10.8.e Test URL/feed/LMS/repository/webhook/media fetch SSRF,
        redirects, DNS rebinding, internal metadata, schemes/ports, size/time
        bombs. Evidence: SSRF suite.
        OPEN ON DNS REBINDING FOR EIGHT OF THE NINE FETCHES. Source intake
        resolves, checks, and then PINS the peer address it validated, so a
        name that answers differently on the second look is caught. The LTI
        paths resolve and check and then hand the URL to `httpx`, which
        resolves again — the window between the two lookups is exactly what a
        rebinding attack aims at. Closing this means a transport that dials
        the address that was validated, which is what
        `source_intake_network` already has and nothing else does.
        THE REBINDING WINDOW IS CLOSED. `outbound_targets.pin_to_validated_address`
        returns a request aimed at the LITERAL the policy approved, and both
        LTI fetches use it, so neither hands a NAME to a client that would
        resolve it a second time. The hostname rides in `Host` so the server
        still routes, and in the TLS SNI so the CERTIFICATE is still verified
        against the name — drop that and pinning would have bought safety by
        giving up authentication.
        `httpx.stream` cannot carry `extensions`, so both fetches now go
        through an `httpx.Client`; the SNI hostname travels there.
        AND THE PIN IS ASSERTED BY WATCHING WHERE A REQUEST IS AIMED, which
        is the part the existing cases could not do. Every test here already
        passed whether the address was pinned or not: they drive a fetch at a
        0.2-second timeout and assert it FAILS to connect, which proves the
        policy let it through and says nothing about where it went. The nine
        new cases capture the outgoing request instead — the fetcher, the
        policy and the pin are all real, and only DNS and the HTTP client are
        substituted at their boundaries. One of them answers public on the
        first lookup and private on every one after, and the request still
        goes to the public literal, because there is no second lookup to
        move it.
        A SEAM THAT WAS MISSING ON ONE SIDE. `HttpxTransport` took a resolver
        and `HttpsJwksFetcher` did not, so the JWKS path could only have been
        tested by the internet agreeing with the test. It takes one now, the
        same way, and the two fetchers are symmetric.
        WHAT KEEPS THIS OPEN, AND A CORRECTION TO WHAT THAT MEANS. Every
        fetch somebody OUTSIDE aims is pinned now — the two LTI ones here and
        the badge status list M10.7.d added, which is the third and the most
        attacker-chosen. The seven model providers read their base URL from
        settings and are not.
        THE FIRST VERSION OF THIS SENTENCE SAID THEY SHOULD GO THROUGH "THE
        SAME POLICY AND THE SAME PIN", AND THAT IS WRONG. Reading the call
        sites to do it showed why: `refuse_unless_public` would REFUSE an
        operator who points a model provider at an internal gateway on
        `10.0.0.5`, which is an ordinary and deliberate deployment. The
        policy that is right for a URL a platform chose is the wrong policy
        for a URL an operator chose, and applying it uniformly would have
        broken working estates in the name of closing a checkbox.
        So what remains is smaller and different: pinning without the
        globality check buys little on its own — each request re-resolves, so
        an operator whose DNS is hostile is compromised from the first answer
        either way — and the useful control for these would be an
        operator-declared allowlist of model hosts, which is a new mechanism
        rather than a reuse of this one. That is what this stays open on.
        DONE, AND TWO OF THE NINE FETCHES POINTED WHEREVER SOMEBODY ELSE SAID.
        The spec's first instruction was to enumerate every `httpx` call site
        under `src/metis/services` before writing a case, and enumerating
        found nine: seven read their base URL from settings, and two do not.
        `lti_trust` fetches the JWKS an ADMINISTRATOR registered, and
        `lti_services_client` calls the AGS and NRPS endpoints that arrive in
        CLAIMS INSIDE A LAUNCH TOKEN — a platform that signs its own launch
        chooses those. Neither checked where it was going, so
        `https://169.254.169.254/latest/meta-data/iam/security-credentials/`
        was a URL this service would have called WITH A BEARER TOKEN
        ATTACHED, and `https://10.0.0.5/lineitems` was another.
        `services/outbound_targets.py` makes the check `source_intake_network`
        already made, written once so the two agree: resolve the name and
        refuse unless EVERY address it answers with is public. One public
        answer beside a private one is refused, which is the shape of a
        rebinding attempt. A TEST WRITTEN TO ASK FOUND A HOLE IN THE CHECK
        ITSELF: Python reports `224.0.0.1` as `is_global`, because multicast
        is not in the special-purpose registry it consults, so multicast,
        reserved and unspecified addresses are now refused by name.
        LOOPBACK IS A DECISION EACH CALLER MAKES rather than a global rule.
        M10.6 allowed it for JWKS deliberately — RFC 8252's exception, and
        the only reason a platform on this machine is testable — and this item
        does not undecide it. The AGS client refuses it: a key document is
        read and discarded, while those calls carry a token and write.
        SIZE BOMBS. Both LTI fetches capped the body with
        `response.content[:limit]`, which caps what is KEPT and not what
        arrives — the whole body is in memory by the time it is sliced. Both
        read with a budget now and stop mid-stream, proven against a response
        that never ends.
        Already held and re-checked rather than rebuilt: no fetch anywhere
        follows a redirect (asserted across all nine), and source intake's own
        SSRF suite covers private DNS, redirect revalidation, peer mismatch,
        oversize, deadlines, credentials, fragments, ports and the host
        allowlist.
        `tests/test_outbound_ssrf.py`, 32 tests, including an inventory that
        FAILS WHEN A NEW `httpx` CALL SITE APPEARS until somebody says who
        aims it.
  - [x] M10.8.f Fail CI for any untested route/role/failure row or protected
        field leak. Evidence: coverage gate.
        Evidence: `evidence/v1-workbenches/metis-bff/coverage-gate.json`,
        computed by `services/coverage_gate.py` from M10.8.a's inventory,
        runnable as `pnpm run metis:coverage:gate` (exit 1 on any failing
        row), with 11 tests in `tests/test_coverage_gate.py`.
        Three rows, each failing in BOTH DIRECTIONS — a new bad row fails it,
        and a recorded row that got fixed and was left on the list fails it
        too, because a list that can only grow is an excuse with a file name
        (M8.14 settled that for tenancy):
        - AN UNTESTED ROUTE. 229 of 882 operations are named by no test. That
          is today's debt, recorded operation by operation in
          `src/metis/data/coverage_gate_baseline.json`, and the gate is what
          stops the 230th.
        - A REFUSAL NOBODY PUBLISHED. 121 operations require a role and
          document no 4xx at all, so a client generated from the document has
          no branch for the 403 it WILL get — M10.8.b proved every one of them
          refuses. An anonymous route is not asked for one, and a test proves
          the gate makes that distinction.
        - A PROTECTED FIELD IN A RESPONSE. No baseline for this one and no
          way to record an exception: `answer_key` and the eleven properties
          beside it may not become reachable, and the gate fails with nowhere
          to write it down. Zero today.
        Every check is paired with a doctored surface that fails it: an
        invented untested route, a baseline entry that gained a test, an
        operation that stopped documenting its refusal, and a response that
        reaches `answer_key`. The baseline lives beside the code rather than
        in `evidence/`, because evidence is written by a generator and a
        baseline is edited by a person.
- [ ] M10.9 Run full-journey Playwright: source ingest to grounded course
      publish. Evidence: live backing and verified target.
      <!-- SPEC 2026-09-11: host per stage. Today source intake, grounding and
      authoring exist under BOTH `apps/metis/web/src/app/create/*` (brief,
      outlines, lessons, item-bank; port 3020) and `apps/metis/admin`
      (grounding, workspaces, hub, publication; port 3021). Until M0.1.c is
      ratified the journey is written against the pages that exist and each
      stage names its app and route; a stage with no page in either app is a
      recorded gap, never a mocked step. Live backing per §2.5.2/§2.5.3; `next
      build && next start` and chromium only per §2.5.7. The "authoritative
      target" in .d is the LMS the M9.12 publication saga reads back from; a
      local LTI platform stub counts only if it is the same stub the saga
      already trusts, and the evidence says so. -->
      THE ROOT BLOCKER IS CLOSED 2026-09-13. It held for two days and it was
      real: source intake had no page in either application —
      `apps/metis/web/src/app/create` had no source or intake route, and the
      admin app's `source-impact` and `training-intake` are different
      surfaces (impact analysis over a source already held, and training-data
      intake). The admin app now serves `/source-intake`, and the stage map
      it is measured by reports ELEVEN stages walkable out of eleven, where
      it reported ten yesterday.
      WHAT THAT DOES AND DOES NOT UNBLOCK. The first stage can now be walked
      through a page by a person, which is what .a–.e each waited on. It does
      not settle the rest of any of them: .a still owes the standard and LMS
      seeds and the extraction assertion, .b still needs content seeded and
      the gateway — a third process — stood up, .c still owes three of its
      four gates, and .d/.e still have no authoritative target to read back
      from. This parent stays unchecked on those, not on the page.
  - [ ] M10.9.a Seed roles/tenant/standard/LMS and upload one valid plus
        malicious source through real backing; assert
        quarantine/extraction/provenance. Evidence: intake journey.
        OPEN ON TWO THINGS, DOWN FROM THREE. The standard and LMS seeds are
        not done (the roles and the tenant are), and EXTRACTION is not
        asserted: this stage proves quarantine, scanning and provenance, and
        the extraction step is the next endpoint along.
        THE PAGE BLOCKER IS CLOSED 2026-09-13, AND IT WAS THE BIG ONE. It
        read: the source-ingestion API publishes 41 operations — 52 when
        re-counted from `app.openapi()` on 2026-09-13; the API grew, the
        finding did not change — and NEITHER `apps/metis/web` NOR
        `apps/metis/admin` references one of them, so
        there is nothing to walk in a browser — recorded, never mocked, as
        this item's spec requires, and measured by a test that fails the day a
        page appears. That test has now flipped, on purpose and in both places
        it was taken: `test_a_page_reaches_the_source_intake_api` in
        `tests/test_source_intake_journey.py`, and
        `test_every_stage_of_the_journey_now_has_a_page` in
        `tests/test_journey_stage_map.py`. Both assert the opposite of what
        they asserted yesterday and are worth more that way round: a deleted
        route, or a page that stops naming the intake API, puts the first
        stage back out of reach and fails there.
        - THE PAGE. `apps/metis/admin/src/app/source-intake/page.tsx`, routed
          at `/source-intake` and reached from the sidebar, over a proxy at
          `src/app/api/source-ingestion/[[...path]]/route.ts` and a client of
          its own — `admin-client` is pinned to `/api/admin` and this API is
          not under it. The proxy forwards `await request.arrayBuffer()` and
          not `.text()`, because a multipart body does not survive being
          decoded to a string and re-encoded.
        - WALKED IN CHROMIUM, 4 tests in
          `apps/metis/admin/e2e/source-intake-workspace.spec.ts`, against the
          real page and a stubbed intake API — the routing, the upload
          control, the digests and the findings are the application's own.
          The stub returns the receipt the service really produces, INCLUDING
          `malware_scanner_unavailable`, so a page that quietly dropped the
          finding an estate with no scanner gets would fail there. Each test
          also carries the suite's axe pass: zero serious or critical WCAG
          2.1 AA violations.
        - THE TWO DIGESTS ARE SHOWN SIDE BY SIDE, because their difference is
          the evidence that what is stored is the envelope and not the
          plaintext, and the page says which of the two things it is looking
          at rather than leaving a reader to compare hex by eye.
        - A REFUSAL IS CARRIED, NOT SWALLOWED. The service refuses an upload
          outright until an intake key is configured; the page renders that
          detail in an alert and shows no receipt. That is the fail-loud seam
          .a already proved at the service, now visible to the person holding
          the file.
        DONE, AGAINST A RUNNING SERVICE AND A REAL DATABASE.
        `tests/test_source_intake_journey.py`, 7 tests, driven over HTTP at
        `METIS_JOURNEY_BASE_URL`; receipts in
        `evidence/v1-workbenches/metis-journey/intake-receipts.json`.
        - QUARANTINE. An upload never lands as plaintext: the artifact is an
          AES-256-GCM envelope under a named key, and the receipt carries the
          content digest AND the envelope digest, which are different numbers.
          The service REFUSED to accept an upload at all until a key was
          configured — a fail-loud seam, met before any of this could run.
        - PROVENANCE. The source version records `intake_kind`, the original
          locator, the retrieval time, the quarantine and evidence URIs, the
          version number and a tenant DERIVED from the uploader.
        - THE SCAN DISCRIMINATES. An XML declaring an external entity pointed
          at `file:///etc/passwd` is `xml_external_entity`, CRITICAL; the same
          pipeline over a Markdown file raises it not at all, which is the
          check that proves the finding is about the file.
        - AND IT SAYS WHEN IT CANNOT SEE. With no malware scanner configured
          every receipt carries `malware_scanner_unavailable` as a CRITICAL
          finding rather than a clean bill. An instrument that is not there
          does not get to agree.
        - The receipt is bound to the bytes: quarantine id, content digest,
          envelope digest and size all match the upload it describes, and it
          names the policy, the policy digest and the scanner-set digest.
  - [ ] M10.9.b Review source authority/rights/currency, confirm
        claims/citations, author objectives/outline/lesson/item/media and clear
        high-risk grounding. Evidence: authoring journey.
        OPEN ON THE WALK ITSELF. Every stage page now RENDERS against a live
        service — which it could not this morning — but the journey database
        holds no content yet, so the pages render empty. Walking .b means
        seeding an objective, an outline, a lesson, an item and a media asset
        through the live APIs first, and the authoring half of that lives in
        the WEB app, which calls `/api/metis/v1/*` — the gateway, a third
        process this journey has not yet stood up.
        DONE: THE MAP, AND THE FIX THAT MADE ANY OF IT POSSIBLE.
        `evidence/v1-workbenches/metis-journey/stage-map.json`, generated by
        `scripts/export_journey_stage_map.py` from the two applications' own
        source, with 4 tests in `tests/test_journey_stage_map.py`. Eleven
        stages, and as of 2026-09-13 ELEVEN of them walkable. It said ten
        until then, and the eleventh — source intake — had NO PAGE IN EITHER
        APPLICATION, which was M10.9.a's finding arrived at from the other
        direction: .a searched for references to the API, this asks which
        stages have a route, and the two agreed. They agree still, in the
        other direction: the admin app serves `/source-intake` and this map
        now reports no unwalkable stage at all.
        THE JOURNEY CROSSES TWO HOSTS, and the map says which: authoring
        (objectives, outline, lesson) is the web app against the gateway, and
        reviewing (grounding, claims, publication) is the admin app against
        the service. Neither application serves both halves.
        The five admin stage pages — grounding-gates, rights, claims, media,
        publications — were driven in chromium against the live service and
        every one rendered its own heading with no authentication alert. That
        is only true because of the defect this stage found and fixed: React
        runs a CHILD's effects before its PARENT's, so every admin page
        fetched before `AdminAuthProvider` installed the API client's token
        accessor, every first request went out unauthenticated, and nothing
        re-fetched. Against mocks — which is all any existing admin spec has
        ever run against — it never showed. Against a live service the whole
        admin application read "Authentication required".
  - [ ] M10.9.c Run correctness/accessibility/quality/review gates, resolve a
        blocker through a new revision, and bind approvals. Evidence: review
        journey.
        OPEN ON THREE OF THE FOUR GATES AND ON THE BROWSER. This walks the
        REVIEW gate — human signoff — end to end over HTTP; the correctness,
        accessibility and quality gates have their own routes and are not
        walked here, and none of it is driven through a page, because the
        review stage's pages need content the journey database does not hold
        (M10.9.b's blocker).
        DONE: THE SIGNOFF GATE, AND FOUR ROUTES NOTHING HAD EVER CALLED.
        M7.13 built the gate and tested the ENGINE; `/signoffs`,
        `/attempt-checks`, `/outcomes` and `/invalidations` were four of the
        229 operations M10.8.a found that no test names.
        `tests/test_review_gate_journey.py`, 11 tests against a running
        service. Every refusal was produced by the service while the test was
        being written, and each is a door somebody could otherwise walk
        through: no interactive session (`not_interactive`), no tenant header
        ("nothing to scope it to"), no conflict declaration
        (`conflict_not_declared`), an evidence digest that is not the one the
        attempt was prepared against (`evidence_changed`), a second factor
        older than fifteen minutes (`step_up_required`), and a gate opened
        under a policy digest that is not the rules in force
        (`gate_policy_changed`).
        A PASSWORD IS REFUSED AT THE DOOR rather than counted and found
        wanting: a stale passkey reaches the engine and comes back
        `step_up_required`, while a password never becomes an actor at all.
        AND THE TWO PROPERTIES THAT MATTER. A quorum of two counts PEOPLE: the
        same person signing twice leaves one signatory and an uncleared gate,
        and two people in two roles clear it. A signoff binds the revision it
        was given, so a NEW REVISION starts unsigned — otherwise "approved"
        would mean "approved once, for something else", which is the blocker
        this item asks to be resolved by revising.
  - [ ] M10.9.d Build/schedule/publish/sync and verify authoritative target
        read-back before published state. Evidence: publication journey.
        OPEN ON THE AUTHORITATIVE TARGET, WHICH IS NOT AN LMS. This item's
        spec names the LMS the M9.12 saga reads back from; the saga's
        `Destination` protocol has exactly ONE implementation in this estate
        and it is an OBJECT STORE. There is no LMS destination to read back
        from, so the read-back property is proven against reported facets
        rather than against a live platform, and the sync half is unwalked.
        And nothing here is driven through a page: M10.9.b's blocker.
        DONE: PUBLISHED IS COMPUTED, NOT DECLARED.
        `tests/test_publication_journey.py`, 11 tests over HTTP against a
        running service, including `schedules/{id}/preview` and
        `schedules/{id}/confirm` — two of the 229 operations M10.8.a found
        that no test names.
        - A candidate pins nine dimensions into one digest, and the same nine
          readings of the same revision produce the same digest.
        - MATCHING BYTES ALONE IS NOT PUBLISHED. Four facets are required —
          bytes, metadata, visibility, links — and the saga verifies only the
          first, so a target with bytes matched and nothing else read is
          `unknown`: not published, and not failed either. All four matched is
          `published`; a facet that came back different is `failed`; a facet
          nobody checked keeps it `unknown`; and a failed attempt cannot read
          as published however the facets look.
        - A facet that was NOT CHECKED may not carry a time it was checked.
          The model refuses that contradiction, which is how the record stays
          true rather than merely complete.
        - A schedule is planned against THIS candidate's digest and says so;
          confirming answers with the REFUSALS, so an empty list is the
          confirmation and there is no second place for the same fact to
          disagree with itself.
        NOBODY CAN APPROVE ANYTHING TODAY, measured over HTTP. The approval
        route refuses the assembler BY IDENTITY, and `publication_tenant`
        makes every user their own tenant — so the second person does not get
        a refusal about roles, they get `not_found`. The two rules together
        mean no candidate can be approved by anyone at all. The derivation
        documents this limitation in its own docstring; this stage is the
        first thing to measure it from outside.
  - [ ] M10.9.e Retract the source and assert stale/blast-radius/unpublish/
        notification/history plus accessibility/tenant/tier boundaries.
        Evidence: full E2E trace.
        NOT ATTEMPTED, and recorded as such rather than part-done. This is the
        one stage that cannot be walked in isolation: retracting a source and
        watching the blast radius requires a source, content derived from it,
        and a publication — .a, .b and .d in ONE database, which is exactly
        what M10.9.b is blocked on.
        What it will need, from the routes that exist:
        `POST /api/admin/catalog/source-withdrawals`,
        `POST /api/admin/source-impact/{package_id}/changes` — which takes an
        `expected_graph_sha256`, so the source graph has to be there to be
        named — then
        `GET /api/source-ingestion/{package_id}/source-changes/{change_event_id}/blast-radius`,
        `POST /api/admin/catalog/bundles/{bundle_id}/unpublish` and the
        remediation route. The accessibility, tenant and tier boundaries are
        assertions over that trace rather than separate calls.
- [ ] M10.10 Run full-journey Playwright: item authoring to accessible QTI
      export. Evidence: validation report.
      <!-- SPEC 2026-09-11: item authoring pages are
      `apps/metis/web/src/app/create/item-bank/*` (capture, stimulus-sets,
      previews, accessibility, answer-leakage) and
      `apps/metis/admin/src/app/items/[itemId]/*` (stem, response, key,
      rationale, accessibility, gate, use); export diagnostics is
      `apps/metis/admin/src/app/exports`. .d's external validation is M10.5.d's
      receipt. .e's key-absence assertions read `page.content()` and the network
      log, never visibility (M9.15's method), and cover the learner preview
      served by `apps/metis/web`. -->
      OPEN, AND NOT BECAUSE NOTHING WAS WALKED. Five children are recorded:
      .a captured and completed an item, .b proved a preview writes no
      learner record, .c broke and mended it through three of four gates,
      .e found and fixed a learner reading the answer key. What the PARENT
      claims is the end-to-end sentence — authoring THROUGH TO accessible
      QTI export — and both halves of the ending are missing: .d's export
      validation is M10.5.d's receipt and M10.5.d is open, and NONE of this
      is driven through a page. Every stage here is HTTP against a running
      service; the item-bank pages exist and no journey opens one. The
      "Playwright" in this item's own title is the part not done.
  - [ ] M10.10.a Capture/complete a representative stimulus-linked item with
        objective/standard/key/rationales/rubric/source and protected fields.
        Evidence: item journey.
        OPEN ON THE STANDARD, THE RUBRIC AND THE STIMULUS. The capture carries
        an OBJECTIVE and a SOURCE; a standard alignment, a rubric and a linked
        stimulus set are separate surfaces this stage does not reach, and
        none of it is driven through a page.
        DONE, OVER HTTP AGAINST A RUNNING SERVICE.
        `tests/test_item_authoring_journey.py`, 11 tests: capture a draft,
        author a complete selected-response revision against that exact draft
        revision, then read the item back on all four of its surfaces as an
        author and as a learner.
        - A capture keeps the objective, the source reference and WHO wrote
          it, with the tenant derived from the caller. A kind outside the
          vocabulary is refused by name.
        - A revision is bound to the draft revision AND its digest; a stale
          digest is refused, and content naming a source the draft does not
          have is refused as `selected_response_source_reference_drift`. The
          item's sources are the draft's sources, not a second list.
        - THE PROJECTION IS A LEARNER SURFACE AND SAYS SO: `surface:
          "learner"`, `protected_properties_visible: false`, and an
          `excluded_properties` list that NAMES the five it removed —
          `response_rule`, `answer_key`, `correct_rationale`,
          `distractor_rationales`, `misconceptions`. None of them is in the
          bytes. The protected export names the authority it answered to.
        - A learner reaches NONE of the four surfaces, including the
          projection built for a learner: the item bank is an authoring
          surface and a learner meets items through an assessment.
        WHICH IS THE POINT OF READING THIS BESIDE M10.10.e. The mechanism to
        withhold a key — a named learner projection with an explicit excluded
        list — already existed one module away from the assessment read that
        handed the key over.
  - [ ] M10.10.b Add modality-specific accessibility/locale/PNP content and
        verify real learner preview creates no attempt/grade/calibration event.
        Evidence: preview assertions.
        OPEN ON ADDING THE CONTENT. The accessibility and locale surfaces the
        authored revision carries come from this suite's own fixture rather
        than from a caller adding them modality by modality, and PNP
        (personal needs and preferences) is not touched at all. What IS
        proven is the second half of the clause, which is the one with a
        property in it.
        A PREVIEW IS A LOOK, NOT AN ATTEMPT.
        `tests/test_item_preview_journey.py`, 4 tests against a running
        service with the journey's database read directly — because the thing
        under test is A WRITE THAT MUST NOT HAPPEN, and the only witness to
        that is the table. Rendering a preview and scoring a response against
        EVERY option, including the right one, leaves `submissions`,
        `grade_ledger_entry` and `item_calibration_promotion_inputs` exactly
        as they were.
        The scoring is real while it happens: one option comes back `correct:
        true` and another `false`, so the preview is scoring rather than
        declining, and it reports `stable_option_identity_used`, meaning it
        scored the option's identity and not its position.
        AND THE COUNTER MOVES FOR A REAL ATTEMPT — a learner submitting
        through `/api/assessments/{id}/submit` takes `submissions` from n to
        n+1. Without that half, "nothing was written" could just as well have
        meant "nothing can be written here".
  - [ ] M10.10.c Trigger ambiguity/key/accessibility/QTI incompatibility, fix
        via revisions, and complete independent review/gates. Evidence:
        remediation journey.
        THREE OF THE FOUR WALKED; OPEN ON QTI INCOMPATIBILITY, which is
        M10.5.d's receipt and M10.5.d is open — the same blocker M10.10.d
        carries. `tests/test_item_remediation_journey.py`, 10 tests over HTTP
        against a running service, plus
        `evidence/v1-workbenches/metis-journey/remediation-journey.json`.
        Each class is walked the same way, and the second half is the half
        that can fail: make the gate say NO, fix it, make the SAME gate say
        YES. A gate that only ever says NO proves nothing.
        - Ambiguity: a distractor assessed as also satisfying the stem. The
          service derives `nonkey_correctness_not_rejected` from the key
          partition itself — nothing in the request names a category or a
          severity — and the gate refuses to publish.
        - Answer key: the keyed option's own text planted on a learner-visible
          `ui/stem`, found across a complete 22-channel, 144-artifact sweep as
          an `exact` match.
        - Accessibility: one automated check failing on the live item;
          `a11y-check:structure-semantics`, severity error, gate blocked.
        A CLEARANCE IS ABOUT ONE REVISION OF ONE ITEM. After the remediation
        revision the gate computed over the previous text stopped applying by
        itself: `mutation_reasons: ['responses_changed']`, and prepare turned
        `review_required` on. Nothing had to tell it.
        FOUND AND FIXED: THE AUTHOR COULD WAVE AWAY THEIR OWN ANSWER-KEY LEAK.
        The leakage gate answered `pass` / `release_allowed: true` over a scan
        that said `leak_detected`, carrying in its own `raw_findings` an
        `exact` exposure marked `exception_eligible: false`. The review
        counted only `confirm` as unresolved and never asked WHO decided, so
        the one instructor who wrote the item and submitted the artifacts
        could type "false positive" and ship the key. Every layer declares
        this is not exceptable — the finding, the review content and the gate
        content all carry a `Literal[False]` — and the one path around them
        was relabelling. `review_answer_leakage` now refuses a `resolve` or
        `false_positive` from the scan's own submitter; `confirm`, the safe
        direction, stays open to everybody.
        WHO THE SECOND PERSON CAN BE, measured rather than assumed: an
        instructor IS their own item-bank tenant, so a second instructor gets
        404 in their own scope and 403 naming the author's — peer review is
        not a thing this service can express. An admin naming the tenant it
        acts into is the only shape independence has here, and the journey
        drives the release through one.
  - [ ] M10.10.d Assemble QTI package, inspect per-item diagnostics/loss report,
        validate externally, reimport, and compare semantics. Evidence:
        validation report.
        NOT ATTEMPTED, AND BLOCKED ON M10.5.d. This item's external validation
        IS M10.5.d's receipt, and M10.5.d is open: there is no canonical
        hotspot item to validate, so the conformance cell cannot close. The
        assemble-and-diagnose half is built — M10.5's `qti3_*` runtime
        publishes 26 loss codes behind a conformance gate — and the
        reimport-and-compare half needs the package this stage would
        assemble.
  - [ ] M10.10.e Assert answer-key absence from learner/UI/package-unprotected/
        logs/URLs and role/tenant/accessibility behavior. Evidence: E2E suite.
        A LEARNER COULD READ THE ANSWER KEY. FIXED.
        `GET /api/assessments/{id}` and `GET /api/assessments` returned
        `correct_answer` and `explanation` to EVERY AUTHENTICATED CALLER, and
        both docstrings said so as though it settled the matter — "so this
        read requires an authenticated caller". The learner sitting the
        assessment is authenticated. A student calling either route was handed
        the answers to the quiz they were about to take. Proven live: a
        student's GET came back with `correct_answer: "Chlorophyll"` and the
        explanation, then the same request after the fix came back with
        neither.
        The fix is a projection, not a filter on one field. A caller who is
        not an instructor gets the questions with `correct_answer`,
        `explanation` AND the evidence bundle removed — the bundle's claims
        are extracted from the explanation's own text, so redacting the field
        and leaving the bundle would MOVE the answer rather than withhold it.
        Measured M9.15's way: a word planted in the explanation and nowhere
        else, looked for in the RESPONSE BYTES. The correct option's text is
        one of the options a learner must see, so its presence proves nothing.
        TWO WRITES ANSWERED 500 FOR EVERY CALLER, found on the way in.
        `POST /api/courses` serialises `modules` and `POST /api/assessments`
        serialises `questions`; both are lazy relationships, and reading one
        during serialisation happens outside the async session's greenlet.
        Every create raised `MissingGreenlet` and returned "an unexpected
        error occurred". Both load the relationship before the response is
        built now. Nothing caught it because no test creates either over HTTP.
        `tests/test_answer_key_absence_journey.py`, 6 tests against a running
        service, including the one that makes the others mean something: an
        instructor STILL sees the key, so a fix that redacted for everybody
        would fail here.
        OPEN ON THE REST OF THE CLAUSE: the UI, the unprotected part of a
        QTI package, logs and URLs are not covered, and neither are the
        tenant and accessibility behaviours.
        M10.10.c reached the machinery that WOULD cover them and found the
        limit. `answer-leakage` enumerates 22 learner-visible channels —
        `log`, `url`, `deep_link`, `qti` package payloads, `export`, `error`,
        `trace` among them — and a scan of a complete 144-artifact set across
        all of them returns `coverage.complete: true`. But A COVERAGE RESULT
        ATTESTS TO THE SUBMISSION, NOT TO THE SYSTEM: every artifact is
        supplied by the caller. `LearnerVisibleLeakageArtifact` has NO
        producer in this estate — it is defined in the schema, consumed by
        the helpers, typed in the generated client, and constructed only by
        test fixtures. The workbench page at
        `apps/metis/web/src/app/create/item-bank/answer-leakage` is a
        textarea a human pastes a capture into, and it fails closed on an
        empty one, which is the right posture and not a harvest.
        So the honest reading is: the SCANNER is proven across 22 channels
        and the gate that judges it is now sound (M10.10.c), and nothing
        walks the running system's logs, URLs or packages to feed it. That
        harvest is what this clause still needs.
- [ ] M10.11 Run full-journey Playwright: psychometric review to item revision.
      Evidence: calibrated data fixture remains isolated.
      <!-- SPEC 2026-09-11: review pages are
      `apps/metis/admin/src/app/psychometrics` and
      `apps/metis/web/src/app/create/item-bank/estimates/*` (dif-review, IRT).
      The synthetic calibrated sample is generated by the M6 learner-simulation
      service with the isolation suite it ships (`learner_simulation`); .e
      proves isolation at the database (rows tagged synthetic never join
      analytics, grade or training tables), not by UI absence. -->
      OPEN ON TWO THINGS, WITH .a, .c AND .d DONE. The word in this item's
      own title is Playwright and nothing here is driven through a page:
      the review pages named above exist and no journey opens one, which is
      also what leaves .e's accessibility clause unwalked. And .b is open on
      parameters, fit and standard errors, because the review contract
      REFUSES an estimate without uncertainty and nothing in this estate
      fits a model to responses — the IRT surface attests an external tool's
      output and no tool is wired to it.
      What the stage did establish is that the fairness analysis is sound
      under both questions: it invents nothing on a null sample carrying
      real sampling noise (.b), and it finds a planted shift on the one item
      it was planted on (.c). Those are only worth anything together.
  - [x] M10.11.a Seed isolated synthetic calibrated response sample with exact
        item/model/method versions, privacy-safe cohorts, and known CTT/IRT/DIF/
        exposure outputs. Evidence: fixture manifest.
        THE M6.5 SIMULATION COULD NOT PRODUCE ONE, and that had to be fixed
        first. Its response rule drew against the learner's prerequisite
        satisfaction alone, so the delivered item could not move accuracy —
        while the pool engine beside it bands eligibility by a mastery
        DIFFICULTY window and offers a `nearest_target_difficulty` strategy.
        A simulation whose selection layer targets difficulty and whose
        response layer cannot feel it measures nothing it varies. Measured on
        the baseline corpus: the correlation between an item's observed
        p-value and its declared difficulty was +0.575, the WRONG SIGN, on
        two to nine responses an item. The model is now Rasch — ability is
        the logit of the persona's declared satisfaction, difficulty is the
        item's declared centilogits, and at the average difficulty of zero
        the probability is the persona's satisfaction exactly, so that field
        keeps its meaning. `response_method` and its schema literal are v2.
        DONE: THE TRUTH CAME FIRST.
        `src/metis/schemas/calibration_fixture.py`,
        `src/metis/services/calibration_fixture.py`,
        `scripts/export_calibration_fixture.py` (`pnpm run
        metis:calibration:fixture`, `--check` compares documents not bytes),
        `tests/test_calibration_fixture.py` (13 tests), and
        `evidence/v1-workbenches/metis-calibration-fixture.json`.
        400 synthetic learners give 4,100 responses over 16 items, generated
        by the M6.5 engine through the same item-pool eligibility engine a
        real delivery calls. Item difficulties and persona abilities are
        DECLARED and the responses are drawn from them, which is what makes
        the outputs known rather than asserted: the observed proportions
        correct recover the declared difficulty ordering at a Spearman
        correlation of −0.9794.
        - Exact versions: engine id, engine version, `response_method`,
          eligibility engine id, the cohort seed, a digest of the whole
          request, and each item pinned to its revision sha256.
        - Privacy-safe cohorts: a cohort here is a PERSONA, an authored
          distribution, so it names no attribute of a person. All 16 items
          clear the 30-per-cell floor in both cohorts — the same floor
          `dif_analysis` will judge the sample by, because a sample built to
          feed an analysis may not be assembled under a laxer rule than the
          one that will judge it. The model refuses a released cell below the
          floor, and refuses a suppressed cell that still carries the counts
          it suppressed.
        - CTT: proportion correct and the item-REST correlation, so an item
          is never correlated with a total containing itself.
        - Exposure: per-item count and rate in basis points.
        - DIF: BOTH GROUPS ARE ONE POPULATION WEARING TWO LABELS, drawn from
          a single declared ability range, so the ground truth is
          `none_injected`. A fixture whose groups genuinely differ only tests
          that a detector finds a planted difference; this one tests the
          direction that matters, that a detector does not INVENT one. And it
          publishes the size of its own noise: with no DIF present at all,
          sampling alone still produced a largest group gap of 0.2036. A
          detector whose threshold sits under that reports unfairness against
          an innocent item on every null sample it is ever shown.
        - Isolation: the M6.5 import-closure and output-identity scan over
          this exact run — isolated, 0 unlabelled records, 0 real-identity
          matches — and the manifest refuses to validate if it is not.
  - [ ] M10.11.b Review raw parameters/fit/SE/sample/uncertainty/DIF/privacy/
        declared difficulty without exposing learner records. Evidence: review
        journey.
        DIF, PRIVACY AND AUTHORITY WALKED; OPEN ON PARAMETERS, FIT AND SE,
        and the reason is the honest one: the review contract REFUSES what an
        unfitted sample can offer. `Estimate` will not accept a difficulty, a
        discrimination, a guessing parameter or a point-biserial as a bare
        number — "a point estimate on its own reads as precise and a reviewer
        compares two spurious digits" — and the standard error it wants means
        fitting a model to responses. NOTHING IN THIS ESTATE FITS ONE. The
        IRT surface is an attestation of an external tool's output and no
        tool is wired to it, so M10.11.a's sample can put a proportion
        correct, a sample size and an exposure rate in front of a reviewer,
        and must report difficulty and discrimination as `unavailable` with
        the reason. `tests/test_psychometric_review_journey.py`, 14 tests.
        THE MULTIPLICITY CORRECTION IS LOAD-BEARING, MEASURED. DIF is the one
        analysis here that really computes — weighted logistic regression,
        Newton-Raphson, inverse observed information, two-sided Wald, Holm —
        so M10.11.a's sample was run through it for real. In a sample with NO
        differential item functioning in it, 11 of 16 items produced a
        uniform effect at or beyond the policy's materiality threshold of
        0.5, the largest reaching 1.45, and the largest |z| was 2.05 — whose
        UNADJUSTED p-value clears alpha. Holm over the family of 32
        hypotheses takes the smallest adjusted p to 1.0 and all 16 come back
        `no_statistical_signal`, `direction: none`, no human review required.
        Without the correction this run publishes a fairness finding against
        an innocent item.
        THE EXISTING DIF SUITE COULD NOT HAVE SHOWN THAT. Its null fixture
        gives reference and focal the identical correct-count in every
        stratum — (4, 7, 10, 13, 16) against (4, 7, 10, 13, 16) — a null with
        no sampling variation in it at all. Nothing in it ever asks what the
        correction is for. M10.11.a's sample wobbles as much as chance makes
        it wobble, which is why it can.
        Privacy: no result carries a learner reference or a per-response row;
        every released cohort sample is at or above the floor it declares;
        and M9.6's complementary suppression holds — one suppressed cell
        beside a published total gives the cell back, so a second is withheld
        and `recoverable_by_subtraction` comes back empty.
        Authority: promoting is a psychometrician's alone, restricting and
        retiring need a psychometrician AND a fairness reviewer, and an
        analyst may only ask for a re-run or record what they saw.
        A statistical signal is not a cause and never an individual: every
        result carries `bias_cause_established: false` and
        `individual_inference_supported: false`.
  - [x] M10.11.c Trigger insufficient/misfit/DIF or exposure blocker and record
        psychometric/fairness decision. Evidence: gate journey.
        DONE, WITH A MATCHED PAIR. `tests/dif_control_fixtures.py` and
        `tests/test_dif_gate_journey.py`, 10 tests over the real analysis.
        Both samples come from ONE generator, one seed and one set of
        declared item difficulties; the only difference is a shift of 120
        centilogits planted on one item for the focal group. So a detector
        that answers differently is answering to the plant, and one that
        answers the same is not detecting anything. M10.11.b asked the
        negative question — does it invent a finding where there is none —
        and this asks the positive one. A gate that never fires and a gate
        that always fires each pass half of these tests and neither is a
        gate.
        - DIF BLOCKER: exactly one item flagged, the planted one. Uniform
          effect −1.065, Holm-adjusted p 0.0019, `material_signal`,
          `human_review_required: true`, and `direction:
          reference_favored` — the right way round, since the focal group
          met that item harder. The same item in the null answers
          `no_statistical_signal`, and the other seven are unmoved in both.
        - INSUFFICIENT BLOCKER at 40 learners a group against the
          `locale_family` floor of 50: `status: insufficient`, `finding:
          inconclusive`, effect and significance both `suppressed`, and the
          `sample`, `model_fit` and `multiplicity` checks fail. AN
          INSUFFICIENT RUN HIDES ITS OWN CELL SIZES — both cohort samples
          come back suppressed with `source_n: null`, because reporting
          "too few to analyse: 40 and 40" would publish the very counts the
          floor exists to protect.
        - THE DECISION NEEDS TWO DISCIPLINES AND TWO PEOPLE. Recording one
          requires a psychometrician AND a fairness reviewer, two distinct
          reviewer ids, both recommending the action actually recorded. One
          person signing twice is refused, one discipline signing twice is
          refused, and a second signature on an action only the first
          recommended is refused. No decision may set
          `establishes_bias_cause` or `individual_inference_supported`: a
          statistical signal says an item behaved differently, not why, and
          never anything about one person.
  - [x] M10.11.d Create a new item revision/remediation, retain old calibration/
        use/decision, and set new revision uncalibrated until evidence exists.
        Evidence: lifecycle assertions.
        DONE. `tests/test_calibration_lifecycle_journey.py`, 7 tests.
        A CALIBRATION BELONGS TO A REVISION, NOT TO AN ITEM. Three things
        have to hold at once and they pull in different directions: the new
        revision is not calibrated because nobody measured it, the old
        calibration is not deleted because it was true of the item it was
        about, and the old evidence does not quietly carry over.
        - The revision that has its own evidence is
          `eligible_for_ratification` with every check passing — the half
          that can fail, so the refusals below mean something.
        - Present the same 640 well-formed attempts against a revision they
          were not gathered on and `exact_revisions` reads `stale`, the
          status drops to `manual_review`, and the route is never
          `calibration_ratification`. It reads `stale` rather than `fail`
          because nothing is wrong with the evidence — it is about
          something else.
        - THE OLD ATTEMPTS STOP COUNTING RATHER THAN MERELY BEING FLAGGED.
          `effective_sample`, `response_distribution` and
          `administration_diversity` all go from `pass` to `insufficient`.
          This is the distinction that decides whether the guard holds: a
          system that flagged the mismatch and still counted 640 attempts
          would report a well-powered calibration with a warning attached,
          and a warning beside a green number is read as a green number.
        - SUPERSEDING IS NOT ERASING. The old receipt still names the
          revision it was measured on, its sample and its attempt count, and
          it is sealed over its own content — repointing it at the new
          revision breaks the seal detectably. It also never claims
          `promotion_applied`, and it expires, so an old calibration cannot
          be leaned on forever.
        The failure this guards against is one of omission: keeping a
        calibration keyed to the item rather than the item REVISION means a
        one-word fix to a stem silently inherits a difficulty estimate
        measured on different words, and nothing anywhere reports an error.
  - [ ] M10.11.e Prove synthetic fixture cannot enter real analytics/grade/
        training data and verify role/tenant/accessibility boundaries. Evidence:
        isolation E2E.
        ISOLATION, ROLE AND TENANT PROVEN; OPEN ON ACCESSIBILITY, which needs
        a browser against the psychometrics pages and is the same unwalked
        UI half M10.10's parent carries.
        `tests/test_calibration_isolation_journey.py`, 10 tests.
        THE STRUCTURAL PROOF IS THE STRONGER ONE. The transitive import
        closure of everything this stage runs — the M6.5 simulation, the
        fixture generator and the DIF analysis — contains no database, ORM,
        learner store, gradebook, analytics or export module. That says the
        sample CANNOT reach a learner record, which is a better claim than
        "it did not this time"; counting rows can only ever show the second.
        The scan is proved able to fail by walking outward from
        `aggregate_store`, which is built on SQLAlchemy and is duly caught.
        NO SANDBOX IS CLAIMED. The closure does pull in socket and TLS
        transitively through the standard library, and the test asserts that
        it does, so the claim cannot quietly grow into one about confinement.
        Dynamically: the sample's isolation report is `isolated` over a
        non-empty scan with every record synthetic-labelled, 0 unlabelled and
        0 real-identity matches; no learner reference survives into a DIF
        result; and generating the fixture and running both analyses moves no
        row in any of the nine tables a person's work lands in — submissions,
        the four grade-ledger tables, the two gradebook-correction tables,
        training intake and calibration promotion inputs.
        FOUND AND FIXED: A STUDENT REACHED AN `/api/admin/` ROUTE.
        `psychometric_review` took plain `get_current_user` while all four
        sibling psychometric surfaces — `dif_analysis`, `irt_diagnostics`,
        `calibration_promotion`, `exposure_control` — already required
        instructor or admin. `AUTHORITY_FOR_USER` maps `"student"` to
        `analyst`, an authority that may request a re-run and record a
        review. Measured over HTTP: a student got 200 and
        `authority: analyst` from `/api/admin/psychometrics/queue`. Nothing
        had leaked, because the tenant is derived from the caller and a
        student's queue is their own and empty — but the exposure was one
        scoping parameter away and the route is mounted under `/api/admin/`.
        It requires instructor authority now and the same request answers
        403, measured against the running service.
        OPEN, AND THE SAME SHAPE AS M10.9.d: NOBODY CAN BE A FAIRNESS
        REVIEWER. `restrict` and `retire` are the two dual-control actions
        and they are permitted to a psychometrician and a fairness reviewer.
        `fairness_reviewer` is reached only by a user whose platform role is
        `"reviewer"`, and this service issues `student`, `instructor` and
        `admin`. So the discipline that exists to check a psychometrician
        cannot be occupied by anybody, and the second signature on a
        high-impact fairness action is unobtainable. That is a gap in the
        role vocabulary rather than in the mapping, and closing it needs a
        platform role this service does not have.
- [ ] M10.12 Run full-journey Playwright: integrity decision, appeal, and
      gradebook correction. Evidence: role separation.
      OPEN. .a and .b are done; .c has no surface to walk, .d has no store,
      and .e's answer is a defect rather than an assertion. Nothing here is
      driven through a page, which is this item's own title again.
      <!-- SPEC 2026-09-11: pages are
      `apps/metis/admin/src/app/integrity-signals`, `.../governance` (decisions
      and appeals) and `.../grade-corrections`. Role separation is by actor id
      through the M7 conflict-of-interest and signoff records (see the M10
      heading note); the JWT `admin` role alone must NOT be enough to decide an
      appeal one has reviewed, and .e asserts exactly that. -->
  - [x] M10.12.a Seed a synthetic learner attempt, attributed uncertain
        integrity signal/evidence, separate reviewer/decider/appeal/grade roles,
        and policy. Evidence: fixture.
        DONE over HTTP against a running service.
        `tests/test_integrity_decision_journey.py` records a signal carrying
        its model id, its model version, its collector version, a confidence
        INTERVAL with a stated basis and sample size, and the raw classifier
        output beside the threshold it was judged against — so a reviewer can
        see how close to the line it fell rather than only which side.
        A SIGNAL IS AN INDICATION AND THE TYPE WILL NOT LET IT BE A FINDING.
        The projection carries `is_conclusion: false` and
        `language: classifier_indicates` as literals, so no payload, however
        constructed, can present a detection as a conclusion. The evidence is
        a chain in which each link names the digest of the one before it, so
        nothing can be inserted into a settled chain without breaking every
        link after it.
        The four roles are separate in the engines rather than in one fixture:
        the reviewer and integrity-lead audiences come from the caller's role,
        the decider independence is by actor digest in the appeal engine, and
        the grade roles are the scorer, requester, reviewer and approver of
        the correction engine.
  - [x] M10.12.b Review signal/evidence/model/version/uncertainty, request
        evidence, and record initial human decision/explanation. Evidence:
        decision journey.
        DONE. `tests/test_integrity_decision_journey.py`, 11 tests over HTTP.
        The queue shows the signal, a view names the audience that saw it and
        leaves an access-audit trace with a digest and a time, asking for
        evidence moves the signal `new` to `evidence_requested` and records
        who asked, the history reads back, and an action naming a version
        that has moved is refused `stale_expected_version`.
        NO REVIEW VERB CAN CARRY A GRADE OR A CONSEQUENCE, and not because a
        check forbids it: the four verbs are `request_evidence`, `dismiss`,
        `escalate` and `open_case`, and `ReviewAction` has no field for one.
        FOUND: THE ESCALATION THIS AUDIENCE MODEL EXISTS FOR CANNOT HAPPEN.
        `escalate` is one of the four verbs and there is nobody to escalate
        to. `_authority` sets `tenant_id` to the caller's own user id for
        every audience, and this router — unlike the item-bank surfaces,
        which take `?tenant_id=` for exactly this reason — offers no
        parameter to act into another tenant. Measured: an integrity lead
        gets 404 on both the history and the view of a signal a reviewer
        recorded.
        AND THE ACCOUNTABILITY RECORD IS THE CASUALTY. The access audit
        exists so somebody can see which reviewer opened which signal. A
        reviewer is correctly refused it, 403. The integrity lead who may
        read it gets 200 and an empty list, because it is scoped to their own
        tenant and the reviewer's views sit unreadable in another. The views
        it exists to record are the exact views nobody can read.
  - [ ] M10.12.c File learner appeal, enforce independent restricted reviewer,
        add evidence, and overturn/modify/remand with immutable history.
        Evidence: appeal journey.
        NOT WALKABLE: THE APPEAL WORKFLOW HAS NO SURFACE AND NO STORE.
        `apps/metis/service/src/metis/api/appeal_workflow.py` is 27 lines and
        mounts exactly ONE route, `GET /api/appeal-workflows/evaluation`,
        which returns the engine's own labelled-corpus report. There is no
        route to file an appeal, add evidence, decide, overturn, modify or
        remand, and no table holds an appeal case — the only appeal table in
        the schema is `item_exposure_appeals`, which belongs to M6 exposure
        control and is a different thing.
        The engine itself is real and well made: `appeal_workflow_engine` is
        a state machine whose `decide` refuses a decider who owned the
        detector or reviewed the signal (`not_independent`), who declared a
        conflict with this learner, who names a stale version, or who acts
        before the evidence and the learner's explanation are settled — all
        BY ACTOR DIGEST rather than by JWT role, which is what this stage's
        spec asks for. It is imported by its own evaluation corpus and by
        `integrity_audit_engine`, and by nothing that a person can reach.
        So the independence rule this item exists to walk is implemented and
        unreachable. Closing this needs the routes and the persistence, not a
        test.
  - [ ] M10.12.d Request/approve append-only grade correction and reconcile
        aggregates/integration/learner visibility. Evidence: correction journey.
        APPEND-ONLY AND LEARNER VISIBILITY WALKED; OPEN ON RECONCILIATION,
        because there is nothing durable to reconcile.
        `tests/test_grade_correction_journey.py`, 7 tests over HTTP. A grade
        is a sequence of events and an approval appends one — the disputed
        event stays in the history beside the value that replaced it, and
        the sequence numbers stay ordered and unique. A correction cannot be
        approved without a notice to the learner (`learner_not_noticed`),
        and a decision naming a version that has moved is refused.
        THE TABLES HAVE A READER AND A WRITER AS OF 2026-09-13. They did not
        before: `POST /decisions` read the request, the history and the prior
        decisions out of the request body, computed, and returned, loading
        nothing and saving nothing. Migration 044 created
        `gradebook_correction_grade_events` and
        `gradebook_correction_correction_decisions` and NOTHING IN THIS
        REPOSITORY READ OR WROTE EITHER — no ORM model, no repository, no
        statement naming them, checked repo-wide — and a test asserted that
        absence so the day a writer appeared it would say so. That test has
        now flipped to `test_the_store_the_server_consults_is_real`, and it
        asserts the property that was actually missing: not that a row exists,
        but that a decision recorded by one HTTP call CONSTRAINS THE NEXT ONE.
        `models/gradebook_correction_store.py`, `services/gradebook_correction_
        store.py`, and migration 076, which indexes the `content_json::jsonb
        ->> 'request_id'` expression the lookup filters on so the separation
        check does not become a scan of every decision a tenant ever made.
        Measured against a live service: 20 decisions and 4 grade events
        durable in the two tables after the journey suite ran twice, and the
        suite passes on the second run because the rules read state rather
        than reset with the process.
        A DEFECT IN THE ABOVE, FOUND AND FIXED THE SAME DAY. The first
        version of the route resolved its tenant with
        `resolve_item_bank_tenant`, which returns the actor's RAW UUID, while
        the grade ledger these corrections correct is written by
        `api/gradebook_ingress.py` under `uuid5(NAMESPACE_URL,
        "metis-tenant:" + user.id)` — the derivation `deps.tenant_of`
        documents as "derived and never accepted". For the SAME signed-in
        instructor those are two different UUIDs, so a correction and the
        grade it corrects were landing in different partitions. 23 API
        modules use the uuid5 derivation; this route was the only one using
        the item bank's, which is that family's convention for its own
        services. Nothing misbehaved — no code joins the two stores yet, and
        no exploit is claimed — but a reconciliation across two partitions
        is impossible BY CONSTRUCTION rather than merely unbuilt, which is
        the thing this very clause asks for. Now `tenant_of(current_user.id)`;
        instructor authority was already required by the route's dependency,
        so the resolver's role check was redundant. Re-measured against a
        live service on a fresh database: the 9 journey tests pass over HTTP,
        all 9 decisions and 2 grade events land under
        `tenant_of(the instructor)` — the ledger's partition — and the same
        caller approving their own request is still
        `REFUSED ['reviewer_cannot_approve']`.
        RECONCILIATION IS STILL NOT WALKED. Two corrections to one grade now
        share state, which is what this clause said was impossible, so the
        blocker as written is gone. But `POST /aggregates` still recomputes
        over inputs the caller hands it, so nothing yet reconciles an
        aggregate ACROSS the corrections now on record. That is what remains
        here, and it is a smaller thing than what it replaced.
  - [ ] M10.12.e Assert original reviewer/system actor cannot decide appeal/
        grade, protected evidence redaction, tenant isolation, and
        accessibility. Evidence: separation E2E.
        ASSERTED. THE GRADE HALF NOW HOLDS; THE APPEAL HALF CANNOT BE ASKED.
        A SEPARATION OF DUTIES ENFORCED AGAINST HISTORY THE ACTOR SUPPLIES
        IS NOT A SEPARATION OF DUTIES. The three grade rules are right and
        each has its own refusal — the scorer cannot review, the requester
        cannot decide, the reviewer who moved a request on cannot approve it
        — and all three fire when the server is told. But only ONE of their
        four inputs came from the session. `actor_digest` is derived from
        the token and no body field can move it, which is the half that was
        always right. The grade history, the correction request and the prior
        decisions were all read out of the REQUEST BODY, and
        `prior_decisions` defaulted to an empty tuple.
        MEASURED OVER HTTP, ONE CALLER, ONE REQUEST, BEFORE: they ask for
        changes and the service records their digest as the reviewer; they
        then APPROVE THE SAME REQUEST and it SUCCEEDS, because the default
        value of a field is to mention nothing. Only the identical call that
        disclosed the first decision was refused. The rule was asking the
        person it is meant to constrain.
        FIXED 2026-09-13, AND THE FIX WAS PERSISTENCE RATHER THAN A CHECK.
        The same walk against the same service now answers
        `reviewer_cannot_approve` on the second call, with NOTHING in that
        call mentioning the first: `POST /decisions` loads the prior
        decisions from the store and `prior_decisions` is gone from the
        published request shape — refused, not ignored, because a silently
        dropped field would leave a caller believing they had disclosed
        something. The store is .d's, and the tenant it is scoped by comes
        from the session through `resolve_item_bank_tenant`, never from a
        payload; scoping it by a body field would have put the hole back one
        level down.
        WHAT IS STILL BODY-SUPPLIED, said plainly: the grade history and the
        correction request. Those are two of the four inputs and they are
        unchanged, so this clause's finding is half closed, not closed.
        The appeal half of this clause cannot be asserted at all — see .c,
        where the independence rule exists and no route reaches it.
        Redaction and tenant isolation hold on the signal surface: a view
        names the fields it showed and withheld, a reviewer is refused the
        access audit (403), and a learner is placed in the learner audience
        and sees an empty queue. Accessibility stays open with every other
        UI clause in §M10.
- [ ] M10.13 Run WCAG 2.2 AA automation, keyboard journeys, and screen-reader
      announcement tests across authoring and preview. Evidence: report matrix.
      OPEN, with .b done and the rest measured. THE THREE DEFECTS THIS STAGE
      FOUND WERE ALL INVISIBLE TO THE GATE THAT EXISTED — two because the
      rule was switched off, and one because no scan can see when a live
      region is mounted. That is the report matrix's real finding: the
      coverage was wide (every route visited) and shallow (one state, one
      rule set, no traversal).
      A real assistive-technology run belongs to S11.5.f and is not
      claimable here; this stage's own spec note says so, and .d is
      DOM assertions by design.
      <!-- SPEC 2026-09-11: §2.5.8 — 2.2 tags through the kit policy, not
      `axeAfterEach`'s 2.1 set. The screen-reader cell (.d) is DOM/live-region
      assertions here; a real assistive-technology run is S11.5.f's matrix and
      is not claimable on this Mac, so the report matrix names it as not run.
      -->
  - [ ] M10.13.a Maintain critical route/state/role/action inventory for hub,
        course/item/source/graph/review/calendar/localization/media/credential/
        integrations and learner preview. Evidence: coverage matrix.
        ROUTE COVERAGE IS ESSENTIALLY COMPLETE AND IS NOT THE GAP.
        Measured across both applications: all 65 admin route groups are
        visited by at least one spec, and 10 of the 11 web route groups are
        — only `saved` is never opened. 71 admin specs and 66 web specs.
        THE GAP IS STATES AND ACTIONS, NOT ROUTES, and M10.13.b/.d proved it
        twice over. Both contrast defects lived in an EMPTY STATE, so every
        spec that visited the route with data in it passed; and the 41
        unannounced live regions are a mounting pattern no route visit can
        see. A route-level matrix would have shown green for all three.
        So the inventory this item asks for is the wrong shape to catch what
        was actually wrong here. What the stage needed, and what .b and .d
        now have, is a matrix over STATES — empty, loading, error, populated
        — and over the mount-time behaviour of announcements. Recording that
        rather than producing a route table that would have said everything
        was fine.
  - [x] M10.13.b Run automated rule scans across themes/viewports/locales/zoom/
        reduced motion after key interactions and error states. Evidence: scan
        reports.
        THE SIGNOFF WAS GREEN BECAUSE THE RULE WAS OFF. FIXED.
        `wcag-aa-signoff-v1-p2-3513.spec.ts` is named "Metis admin WCAG 2.1
        AA signoff" and ran with `disableRules: ['color-contrast']`,
        deferring to a design-token migration named with no owner and no
        date. 1.4.3 Contrast (Minimum) is AA and is the rule a real interface
        is likeliest to break, so the signoff was silent about exactly the
        thing it was named for. The carve-out is REMOVED and the spec passes
        with the rule on.
        TWO REAL DEFECTS BEHIND IT, both in shared components and both in
        EMPTY STATES, which is why every scan of a populated page passed:
        `DataTable`'s "no rows" message was #9ca3af on white, 2.53:1, and
        `ActivityFeed`'s was #9ca3af on #f9fafb, 2.42:1. Both are #64748b
        now, 4.76:1 and 4.63:1. The empty state is the only message a reader
        has when there is nothing else on the card, so it is the worst place
        to be faint. The failures looked intermittent until the cause was
        found: they appeared whenever a fetch failed and left a table empty.
        WCAG 2.2 RAN FOR THE FIRST TIME. Both Metis applications take their
        gate from `axeAfterEach`, which declares the 2.1 terms, and the one
        hand-rolled helper in `apps/metis/web` declares the same four. So
        nothing in Metis had ever asked for the 2.2 terms. Measured against
        the axe-core installed here (4.11.4), they add EXACTLY ONE RULE:
        68 rules to 69, and the addition is `target-size` — 2.5.8 Target
        Size (Minimum). It also ships `enabled: false` in axe's metadata, so
        naming the tag is what turns it on; scanning with no tags at all
        leaves it off.
        Result: NO `target-size` violations across eight authoring surfaces
        at desktop width, nor on the hub at 390px, which is where pointer
        targets are smallest. That is a fact nobody had established.
        `e2e/support/wcag22.ts`, `e2e/wcag22-target-size.spec.ts` (10 tests)
        and `e2e/contrast-carve-out-census.spec.ts` (13 surfaces, a ceiling
        of zero that can only be ratcheted down). 52 of 52 green over two
        repeats, chromium.
        OPEN: themes, locales and reduced motion are not varied here — the
        scans run one theme, one locale and default motion.
  - [ ] M10.13.c Run keyboard-only focus/order/visibility/shortcut/non-drag/
        editor/graph/calendar/media/modal/save/conflict journeys. Evidence:
        keyboard reports.
        TRAVERSAL WALKED ON THE LEARNER APP; the widget-by-widget list above
        is not, and the counts say why that matters.
        MEASURED FIRST: across the 65 specs in `apps/metis/web`, twelve press
        a key and NONE PRESS TAB. The keys they press are arrows, Enter, Home
        and End — widget keys, pressed inside a component that already holds
        focus. Nothing asked the question a keyboard user asks first, which
        is whether Tab reaches the controls at all and in what order. The
        admin app is thin rather than empty: 4 of 69 specs press Tab, and the
        one named "every control is reachable and operable from the keyboard"
        presses it once, between two adjacent filters.
        `apps/metis/web/e2e/keyboard-traversal.spec.ts`, 5 tests, 15 of 15
        green over three repeats on chromium. The assertions are properties
        rather than one page's layout, so they keep meaning when it changes:
        every visible control is reachable by Tab alone; the order Tab visits
        them in is document order, with no positive `tabindex` lifting a
        control out of it; focus is visible on whatever holds it; nothing
        traps the keyboard; and the sign-in form can be completed and
        submitted without a pointer at all.
        The login page passes all five. Two things had to be right in the
        harness before that meant anything: Next's dev overlay is focusable
        and Playwright's locators pierce its shadow root, so it had to be
        stepped over on both the walk and the enumeration or the test
        measures the harness; and Tab WRAPS to the top after the last
        control, so an order check spanning the wrap reads a correct cycle
        as a jump backwards.
        OPEN: the editor, graph, calendar, media, modal, save and conflict
        journeys the item names, and shortcuts and drag alternatives. This
        walks one page thoroughly rather than eleven surfaces shallowly.
  - [ ] M10.13.d Run screen-reader name/role/value/instruction/status/live
        update/ validation/selection/decision and semantic alternative journeys.
        Evidence: announcement reports.
        FOUND: 41 LIVE REGIONS THAT ARRIVE WITH THEIR MESSAGE AND ARE NEVER
        ANNOUNCED. `aria-live="polite"` and `role="status"` tell assistive
        technology to watch an element and read what CHANGES inside it. NVDA
        and JAWS attach that watch when the region enters the accessibility
        tree, so a region inserted already containing its text has nothing to
        change — the text was there when the watch began. The shape across
        `apps/metis/web` is the silent one:
            {feedback ? <p role="status">{feedback.message}</p> : null}
        and the shape that works is the region rendered from the start,
        empty, with the message written into it.
        The consequence is not subtle: a learner submits a form, a sighted
        user sees the confirmation, and a screen-reader user is told nothing.
        41 of these across 37 files. `role="alert"` is excluded and is NOT a
        defect — an alert is defined to be announced when it appears, so
        inserting one with its text is correct; there are 13 of those and
        they are left alone. Getting that distinction wrong would have
        condemned a correct pattern.
        `src/lib/live-region-insertion.spec.ts` is a ceiling rather than a
        zero, because each site has to be moved by hand with its styling.
        Three of the most learner-facing are fixed — the standards alignment,
        objectives and decisions pages — and the ceiling stands at 38 and may
        only go down. The gate also proves it can fail and refuses to count
        an alert.
        WHAT WAS ALREADY GOOD: 24 of the 65 web specs assert on live regions
        and 62 product files declare one, so the vocabulary is used widely
        and the defect is in HOW the region is mounted, not in whether
        anybody thought about announcements.
        OPEN: name/role/value and the validation, selection and decision
        journeys are not walked, and a real assistive-technology run is
        S11.5.f's matrix rather than this one — this stage's own spec note
        says so, and it is not claimable here.
  - [ ] M10.13.e Verify real learner preview parity and PNP/MathML/code/table/
        media/interaction alternatives. Evidence: content accessibility report.
        NOT WALKED AS A JOURNEY, but two precise findings came out of reading
        the delivery renderer, and both are about the same image.
        FOUND: AN IMAGE NOBODY DESCRIBED IS PRESENTED AS DECORATION.
        `LessonDeliveryRenderer` renders `<img alt={media.alt_text ?? ''}>`.
        An empty `alt` is not "no description" — it is an assertion that the
        image is decorative and assistive technology should skip it. The
        model cannot tell the two apart: `alt_text` is nullable and there is
        NO `decorative` flag anywhere in the web app or the service schemas,
        so a null can only mean nobody wrote one. A learner using a screen
        reader is silently given nothing, and is not told that anything is
        missing — while the same component renders a visible "Reason: …"
        when media is withheld, so the codebase plainly knows how to say a
        thing is absent.
        AND THE FALLBACK THAT WOULD FIX IT EXISTS AND IS UNREAD.
        `image_fallback_alt` is declared on `lesson_technical.py`, published
        in the OpenAPI document and typed in the generated client — and no
        component in `apps/metis/web` reads it. Its only other appearance in
        the repository is one test fixture, set to `null`. `MediaBlock` is
        also handed a `fallbackText` it does not use for the image either.
        No test covers the null case, which is why this survived: the one
        renderer spec sets `alt_text` to a real sentence.
        What IS present and good: `role="math"` carries `aria-label` from
        `speech_text`, code blocks are labelled by language, and audio and
        video carry titles — so the alternatives vocabulary is real and it is
        the image path that drops it.
        OPEN: preview parity between the learner view and the authoring
        preview, PNP, and table and interaction alternatives are unwalked,
        and only 3 files in the web app mention PNP at all.
  - [ ] M10.13.f Triage owners/severity and block release on current unresolved
        applicable violations. Evidence: zero-blocker report matrix.
        RELEASE IS BLOCKED ON THEM NOW, WHICH IT WAS NOT BEFORE. The gate
        that was supposed to do this ran with `color-contrast` disabled, so
        the one rule most likely to be violated could not block anything.
        That carve-out is removed (.b) and three gates stand in its place,
        each of which fails rather than warns:
        - `wcag-aa-signoff-v1-p2-3513.spec.ts`, 13 surfaces, now with
          contrast on.
        - `contrast-carve-out-census.spec.ts`, the same 13 surfaces against
          a contrast ceiling of ZERO that may only be ratcheted down.
        - `wcag22-target-size.spec.ts`, 8 surfaces plus a 390px viewport
          under the WCAG 2.2 terms.
        - `live-region-insertion.spec.ts`, a ceiling of 38 on regions that
          arrive with their message, down from the 41 measured.
        CURRENT UNRESOLVED APPLICABLE VIOLATIONS, all found this stage:
        - contrast on two shared empty states — FIXED, both components.
        - 38 live regions that arrive with their message — OPEN, held at a
          ceiling; each needs moving by hand with its styling.
        - a null `alt_text` rendered as `alt=""`, and `image_fallback_alt`
          published by the API and read by nothing — OPEN (.e).
        - keyboard traversal on ten of the eleven surfaces the item names —
          OPEN (.c).
        NO OWNER FIELD EXISTS TO TRIAGE INTO, which is the honest answer to
        the first half of this item. The estate has no accessibility owner
        registry and `skipAxe` — the control this would be recorded against
        — is a bare boolean with no owner, reason or expiry, 247 uses across
        241 files estate-wide. Metis is the good citizen there: 1 of its 137
        specs uses it, and the one new use in `wcag22-target-size.spec.ts`
        replaces the scan with a WIDER one rather than giving anything up.
        A severity ordering is available and used — everything above gates on
        axe's `serious` and `critical` — but owners and expiries are not
        expressible, so the triage half of this item stays open.
- [ ] M10.14 Run performance budgets for large courses, item banks, graphs,
      search, save, preview, and export. Evidence: trend reports.
      OPEN ON A CHAIN THAT STARTS OUTSIDE THIS REPOSITORY. Production volume
      is unrecorded, so .b cannot build a representative corpus; .c and .d
      profile against it; .e needs both a baseline and a ratified budget, and
      the budget type deliberately refuses to exist without a ratifier. Each
      link is measured rather than assumed, and .a still found and gated a
      real defect on the way: a script budget that cannot bind.
      <!-- SPEC 2026-09-11: §2.5.7. Budgets in .a are ratified numbers written
      before measuring; fixtures in .b go into an isolated database, never the
      dev one; .c profiles one scenario at a time with the memory checkpoint
      between scenarios. -->
  - [ ] M10.14.a Ratify scenario/device/network budgets for load/interaction/
        render/save/search/export latency, payload/query, CPU/memory/network,
        throughput, and error rate. Evidence: budget spec.
        BLOCKED ON AN AUTHORITY, AND THE TYPE IS RIGHT TO BLOCK IT.
        `LatencyBudgetSpec` requires `ratified_by` and `ratified_at`, so a
        budget cannot exist in this estate without naming who ratified it.
        That is the correct design and it means this item cannot be closed by
        writing numbers: I can propose them and cannot ratify them. Recording
        rather than inventing a ratifier.
        WHAT METIS ALREADY HAS, measured:
        - A good budget CONTRACT. `LatencyBudgetSpec` carries p50/p95/p99 per
          tier and per stage, pairs device WITH network because "a p95 for a
          phone on 3G is not a p95", refuses a higher percentile with a
          tighter budget, and refuses a stage that times out before its own
          budget is spent.
        - Exactly ONE ratified instance: `budget:adaptive-next-item` v1.0.0,
          ratified by the Performance Working Group on 2026-08-01 — and it
          lives in `adaptive_latency_evaluation.py`, an evaluation corpus,
          so it is a budget the engine is scored against rather than one the
          running system is held to.
        - Client budgets on 5 routes in `lighthouse-budget.json`: paint and
          interaction timings, resource sizes and counts.
        NONE OF THE SEVEN SCENARIOS THIS ITEM NAMES HAS A BUDGET — large
        courses, item banks, graphs, search, save, preview and export. Nor
        does any Metis artefact budget query count, memory, CPU, throughput,
        concurrency or error rate; the estate's own I9.14.a recorded 6 of 12
        dimensions budgeted anywhere and Metis is not in that count.
        FOUND: A BUDGET THAT CANNOT BIND IS NOT A BUDGET. On all five
        budgeted routes the `script` resource budget EXACTLY EQUALS the
        `total`, so it is satisfied by every page the total already admits
        and can never be the constraint that fails. Scripts are the dominant
        payload in a Next application, so the one resource type most worth
        constraining was the inert one. `image` and `font` sit below their
        totals and do bind — checking that stopped this being overstated.
        `src/lib/performance-budget-coherence.spec.ts`, 5 tests: every routed
        path is budgeted, every route states a total, paint metrics are
        ordered, and the inert count is held at a ceiling of 5 that may only
        go down. It is a ceiling rather than a zero because correcting it
        means choosing numbers, and this item's own rule is that budgets are
        ratified BEFORE being measured against — a number picked to fit the
        bundle that exists is a description.
  - [ ] M10.14.b Create production-scale
        course/item/stimulus/media/source/graph/ locale/revision/review fixtures
        with documented distributions. Evidence: fixture manifest.
        BLOCKED AT ITS ROOT: NOBODY KNOWS WHAT PRODUCTION SCALE IS. The
        estate's own persistence inventory records it —
        `evidence/v1-workbenches/metis-persistence-inventory.json` carries
        `observedProductionBytes: null`, `observedProductionRows: null` and
        `productionVolumeEvidence: "not present in the repository; capacity
        baseline required before migration"`.
        A fixture built without that is not production-scale, it is a
        fixture of a size somebody chose, and profiling it would produce
        numbers that look like measurements and answer no question. The
        distributions this item asks to be documented are exactly the thing
        that is missing.
        The machinery to build one exists and is proven: M10.11.a generates
        400 synthetic learners and 4,100 responses into an isolated database
        with a manifest, which is the shape this item wants. It is the target
        SIZE that is unknown, not the method.
  - [ ] M10.14.c Profile course/item workspaces, graph/list, search/facets,
        autosave/diff/conflict, learner preview/media, and export/diagnostics
        independently. Evidence: scenario reports.
        BLOCKED ON .b. Profiling a fixture of an arbitrary size measures the
        fixture. The per-scenario discipline this item asks for — one at a
        time, with a memory checkpoint between — is the right method and is
        already the rule this repository works under; what it has nothing to
        run against is a corpus anybody can defend as representative.
  - [ ] M10.14.d Capture client/server traces, query plans, bundle/network/
        hydration/virtualization costs and memory leaks over sustained use.
        Evidence: profiles.
        BLOCKED ON .b for the query plans and the sustained-use leak hunt,
        both of which are statements about behaviour AT a volume. The bundle
        and network half is separable and partly budgeted already — the
        five-route lighthouse budget in .a covers resource sizes and counts —
        but .a also found that its script budget cannot bind, so the bundle
        number it reports is not currently constrained by anything.
  - [ ] M10.14.e Compare trends to baselines/budgets, assign regressions, rerun,
        and retain current passing evidence. Evidence: performance gate.
        BLOCKED ON .a AND .c: a trend needs a baseline to move from and a
        budget to be judged against, and neither exists for the seven
        scenarios this stage names. Assigning a regression additionally needs
        an owner field, and the same gap M10.13.f recorded applies here —
        the estate has no performance owner registry either.
- [ ] M10.15 Run load tests for imports, gate jobs, adaptive selection, LMS
      sync, grade events, and analytics. Evidence: backpressure and SLO results.
      OPEN, AND .f ANSWERED ITS OWN QUESTION WITHOUT A LOAD RUN: there is no
      backpressure in this service to verify. The rest waits on .a, which
      waits on a production baseline that does not exist, exactly as M10.14
      does. Running load anyway would produce numbers about a corpus nobody
      chose against limits nobody set.
      <!-- SPEC 2026-09-11: runs on the Linux dev server (`free -m` checkpoint,
      stop threshold written into .a's plan); a load run attempted on this Mac
      is recorded as a failed attempt, never as evidence (§2.5.7). -->
  - [ ] M10.15.a Define realistic arrival rates/concurrency/payload
        distributions/ tenant mixes/dependencies/failures/SLOs/resource stop
        thresholds per workload. Evidence: load plans.
        OPEN. "Realistic" is the word that blocks it, and for the same reason
        M10.14.b is blocked: the estate records
        `observedProductionRows: null` and "capacity baseline required", so
        an arrival rate written here would be a number somebody chose rather
        than a rate anybody observed. A target capacity could be DECLARED
        instead — that is a design decision rather than an observation and
        would be honest — but it needs the same ratifying authority M10.14.a
        is blocked on.
        The resource stop threshold is the one part that is already real and
        is written down outside this checklist: this repository's own rule is
        a `free -m` checkpoint before every heavy invocation and a teardown
        below roughly 3 GB available on this box.
  - [ ] M10.15.b Load source/QTI/standards imports and extraction/media/
        quality/psychometric gate queues with bounded parallelism. Evidence:
        import/gate report.
        BLOCKED ON .a's plan and on .f: loading a queue that has no
        backpressure measures how fast it can be overwhelmed.
  - [ ] M10.15.c Load adaptive next-item selection/tutor context at learner
        concurrency with cold/hot caches and degraded models/stores. Evidence:
        adaptive report.
        BLOCKED ON .a. This is the one workload with a ratified latency
        budget — `budget:adaptive-next-item`, p50/p95/p99 per stage — so it
        is the first that becomes runnable once an arrival rate exists.
  - [ ] M10.15.d Load LMS/roster/grade/webhook sync, duplicate/out-of-order/
        corrected events, replay/reconciliation, and provider limits. Evidence:
        integration report.
        BLOCKED ON .a AND ON THE ABSENT DESTINATION M10.9.d recorded: the
        saga's `Destination` protocol has one implementation in this estate
        and it is an object store, so there is no LMS to sync against.
  - [ ] M10.15.e Load privacy-safe analytics ingestion/aggregation/query with
        high-volume attempts/events and suppression. Evidence: analytics report.
        BLOCKED ON .a. The suppression half is separately proven: M10.11.e
        and M10.12 show cohort cells withheld below the floor and complementary
        suppression enforced, but neither was measured under volume.
  - [ ] M10.15.f Verify backpressure/fairness/retry/circuit/queue age/lag/
        resource/cost/tail SLOs and stop before unsafe host pressure. Evidence:
        resource-safe SLO results.
        THERE IS NO BACKPRESSURE TO VERIFY. This service has three pieces of
        a rate limiter and no rate limiter:
        - `config.py` declares `rate_limit_requests_per_minute = 60` and
          `rate_limit_burst = 10`;
        - `deps.py` implements `check_rate_limit`, a correct sliding-window
          counter over Redis that sets a key with a TTL, refuses past the
          maximum and increments otherwise;
        - `main.py` maps 429 to `rate_limited` and "Too many requests."
        And nothing joins them up. `check_rate_limit` has ZERO callers in
        `src/` or `tests/`; the two configured numbers are read NOWHERE
        outside their own declaration; and no route or middleware can
        produce the 429 the error mapper knows how to render. The only
        middleware installed is CORS and a request timer.
        Somebody reading the configuration would reasonably conclude callers
        are limited to sixty requests a minute. They are not limited at all,
        and a load run would discover that the service accepts everything
        until it falls over — which measures the absence of a control rather
        than the presence of a limit.
        `tests/test_backpressure_absence.py`, 6 tests, asserts the three
        pieces exist AND that nothing connects them, so the day it is wired
        the suite says so and the tests flip into assertions about the limit.
        Better than a comment, which is what an unwired control usually
        gets.
        THE DAY CAME (M10.8.d, 2026-09-13). The pieces are joined:
        `services/request_throttle.py` with a middleware in `main.py`, and the
        file is `tests/test_backpressure.py` now — 27 tests, the last of which
        drives the real application against the real Redis and watches the
        fourth request of a three-request window come back 429. Every
        assertion above flipped as it was written to. Read M10.8.d for the
        three decisions worth knowing: it is a FIXED window and the code used
        to call it sliding, `rate_limit_burst` is still read by nobody because
        a fixed window has no burst parameter, and it fails open when Redis
        cannot answer.
        SO BACKPRESSURE EXISTS TO VERIFY NOW, and this item's own clause is
        still not verified: what a load run would measure is the limit at its
        configured sixty a minute under real concurrency, which needs the
        production volume M10.15.a is blocked on.
        The rest of the clause — fairness, retry, circuit breaking, queue age
        and lag, cost and tail SLOs — is unverified behind .a–.e.
- [ ] M10.16 Run answer-key leakage, prompt injection, BYOM, PII, grade tamper,
      integrity bypass, and cross-tenant adversarial tests. Evidence: zero
      release blockers.
      OPEN, AND IT STILL CANNOT CLAIM ZERO RELEASE BLOCKERS, but for one
      reason now rather than two. The self-approval hole this section measured
      is CLOSED (.e, 2026-09-13): a reviewer can no longer approve their own
      grade correction, because the rule is checked against a store instead of
      against the caller's account of themselves. What remains is .f — four
      workflows unreachable because every user is their own tenant — and that
      is a tenancy model, not a defect a suite can close. The two findings its
      own spec note starts from are also closed: the answer key no longer
      reaches a learner, red then green in that order, and SSRF on the fetch
      sites is refused.
      <!-- SPEC 2026-09-11: .a starts from a known live finding — `GET
      /api/assessments` and `GET /api/assessments/{id}` (`api/assessments.py`)
      return `QuestionResponse.correct_answer` and `explanation` to ANY
      authenticated caller, including the default `student` role (measured
      2026-09-11; see M0.15.d). The suite must be red on it first, the fix lands
      under M0.15.d, then the suite goes green — in that order. .c's BYOM paths
      are the BFF decision store
      (`apps/oshun/bff/src/metis/metis-byom-decision-store.ts`) plus the
      service's per-feature `*_MODEL_BASE_URL` settings, and its SSRF cases
      reuse M10.8.e's fetch-site list. .f's two-tenant vectors reuse M0.15.f's
      36×9 matrix. -->
  - [ ] M10.16.a Scan learner UI/API/search/cache/client
        state/assets/accessibility/ packages/logs/URLs/tutor/analytics for
        answer/rationale/hidden-test leaks. Evidence: key leakage suite.
        THE API CHANNEL IS DONE, RED THEN GREEN, IN THAT ORDER — which is
        what this item's own spec note asks for. The known live finding it
        names was real: `GET /api/assessments` and `GET
        /api/assessments/{id}` returned `correct_answer` and `explanation`
        to every authenticated caller including a student. Measured over
        HTTP before the fix, a learner's GET came back with
        `correct_answer: "Chlorophyll"` and the explanation; after it,
        neither. `tests/test_answer_key_absence_journey.py`, 6 tests, and
        the fix removes the evidence bundle too, because its claims are
        extracted from the explanation's own text — redacting the field and
        leaving the bundle would move the answer rather than withhold it.
        THE OTHER TWELVE CHANNELS ARE SCANNED BUT NOT HARVESTED. M4.13's
        answer-leakage runtime enumerates 22 learner-visible channels —
        `log`, `url`, `deep_link`, `qti` packages, `export`, `cache`,
        `search`, `error`, `trace` among them — and M10.10.c drove a
        complete 144-artifact sweep across all of them that returns
        `coverage.complete: true`. But every artifact is SUPPLIED BY THE
        CALLER: `LearnerVisibleLeakageArtifact` has no producer in this
        estate, and the workbench page is a textarea somebody pastes a
        capture into. The scanner is proven; nothing walks the running
        system to feed it. That harvest is what this clause still needs.
        FOUR OF THE TWELVE READ CLEAN ON INSPECTION, 2026-09-18 — read
        rather than harvested, so this is a narrower claim than the scanner
        would make, and it is recorded because the alternative is a reader
        assuming the worst about channels that are in fact built right:
        - LOG. `assessment_service` is the only holder of the key that logs
          at all, and its two calls carry ids and a type —
          `assessment_created` and `assessment_submitted`. No payload, so no
          key.
        - SEARCH (the durable index). Already covered by a suite this record
          did not credit: `FIELD_CLASSES` is total over the eight kinds and
          REFUSES an unclassified field rather than indexing or dropping it,
          and `residue` re-scans each built document for any forbidden value.
          `tests/test_search_index.py` runs it over all eight kinds — 62
          tests. The channel has a producer and a scan already.
        - SEARCH (the item bank). `correct_answer` appears exactly ONCE in
          `services/item_bank_search_service.py`, and it is inside the input
          to `source_witness_sha256` — a digest that moves when the answer
          moves, holding no answer. The projection the search serves is built
          separately and does not carry it.
        - TUTOR. `correct_answer` appears nowhere in `tutoring_service` or in
          the retrieval and grounding modules that feed its trust zones, so
          the key never enters the tutor's context to be fenced in the first
          place — which is M6.13's answer again: the strongest refusal is
          having nowhere to put it.
        THE HARVEST IS STILL THE GAP and reading four channels does not
        replace it. `LearnerVisibleLeakageArtifact` still has no producer:
        nothing exercises a route as a learner and hands what came back to
        `scan_answer_leakage`. That producer is the work, and the remaining
        eight channels — ui, cache, client_state, client_bundle, url,
        deep_link, export/qti, analytics — need it rather than another
        reading.
  - [ ] M10.16.b Inject instructions through
        sources/uploads/citations/standards/ learner text/provider
        outputs/media/code/graphs against prompts/tools/ agents/retrieval.
        Evidence: injection suite.
        THIN. `prompt_trust_helpers` carries the right idea — trust
        zones, `fence_untrusted`, `sanitize_prompt_data` and a versioned
        renderer — and it has 3 tests; `agent_safety_helpers` has 6. Nine
        tests is not an injection suite against the ten carriers this item
        names, and none of them drives a source, an upload, a citation or a
        provider output end to end. M6.13's finding still stands as the
        estate's strongest answer here: injection is best refused by having
        nowhere to put it.
        THE CENSUS ABOVE WAS AN UNDERCOUNT, measured 2026-09-18. Ten test
        files exercise the fence, not two, and each of the six model
        boundaries has its own test that inspects the body it would transmit:
        `test_source_claim_provider.py` (4), `test_course_outline_provider.py`
        (4), `test_curriculum_alignment_provider.py` (5),
        `test_item_variant_provider.py` (13), `test_lesson_assist_provider.py`
        (7), `test_source_lesson_model_provider.py` (4), plus
        `test_prompt_trust_helpers.py`, `test_agent_safety_helpers.py`,
        `test_agent_evaluation_helpers.py` and `test_review_evidence_export.py`
        — 101 tests in those files before today. The existing renderer tests
        already drive delimiter forgery and two encoded layers.
        AND THE STRUCTURAL CENSUS IS CLEAN, which this stage rarely gets to
        record: of the ten modules in the service that make an outbound POST,
        SIX SEND A PROMPT AND ALL SIX FENCE IT. The other four —
        `isis_gallery_client`, `lti_services_client`, `lti_trust`,
        `open_badges_proof` — send no prompt at all. `tutoring_service` renders
        a trust prompt too and reaches a model through a provider rather than
        its own client.
        ONE REAL HOLE, MEASURED AND CLOSED. `sanitize_prompt_data` normalised
        with NFKC ONCE, BEFORE its two decode passes, so a homoglyph that
        arrived ENCODED was never normalised: `%EF%BC%9C%EF%BC%9C%EF%BC%9C`
        decoded to U+FF1C FULLWIDTH LESS-THAN after the only normalisation, and
        the collapse — which looks for ASCII `<<<` — left
        `＜＜＜END_UNTRUSTED_DATA＞＞＞` sitting in the data. It could NOT forge
        the delimiter, because `END_UNTRUSTED_CONTENT` is rewritten
        independently of the brackets, so this was hardening and not a break —
        but it left text that reads as a closing marker to anything matching
        loosely. Fixed by normalising again after the decode loop, which is
        idempotent for content that was already normal: every digest of
        ordinary text is unmoved, and 111 tests across the ten fence files
        stayed green. Three attack shapes now assert it.
        AND THE END-TO-END DRIVE THIS ITEM ASKED FOR EXISTS NOW:
        `tests/test_prompt_injection_journey_real_stack.py`, 5 tests. A
        markdown file carrying three payloads — a plain instruction, a forged
        closing delimiter and an encoded homoglyph of one — is ingested through
        the real pipeline into real MinIO, read back out, extracted, and the
        EXTRACTED text is handed to the claim provider as the passage it would
        be. The assertions are on the body the provider actually transmits:
        the payload sits between the markers and nowhere else, the authority
        half is untouched and carries the immunity clause, the forged and
        encoded delimiters are both collapsed inside the fence, instructor
        guidance is fenced as a second zone, and the body offers no tools.
        Fencing is not redaction and the test says so: the sentence is still
        there, because a claim extractor that never saw it could not report it.
        The double is at the HTTP boundary and nowhere else.
        WHAT IS STILL NOT DRIVEN, of the ten carriers this item names: media,
        code, graphs and standards. Learner text and provider outputs are
        fenced by `tutoring_service` and asserted in its own tests but not
        driven from a stored artefact the way a source now is.
  - [ ] M10.16.c Test BYOM endpoint/credential/SSRF/model substitution/supply
        chain/data-use/output/spend/outage/kill-switch paths. Evidence: BYOM
        suite.
        SSRF IS DONE AND FOUND TWO LIVE HOLES. M10.8.e built
        `outbound_targets.refuse_unless_public()` and
        `tests/test_outbound_ssrf.py`, 18 tests over the fetch-site list this
        item points at. It closed SSRF on two LTI fetches and size bombs, and
        the policy refuses multicast, reserved and unspecified addresses as
        well as non-global ones — `224.0.0.1` reports `is_global=True` in
        Python, which is how the first version would have let it through.
        MODEL SUBSTITUTION IS NOW VISIBLE, AND IT WAS NOT. Measured
        2026-09-13: the admission engine carries the right rule —
        `fingerprint_moved`, "the endpoint is answering as a different model
        than the one that was evaluated and approved" — keyed on
        `ServeRequest.observed_fingerprint_sha256`. That field appeared in
        exactly TWO places in `src/`: its own declaration and that
        comparison. NOTHING COMPUTED IT. Every value it had ever held came
        from a request body or from the workbench's fixture constant. The
        same shape as the hole closed in M10.12.d/.e one layer up, and worse,
        because the input had no producer at all.
        ONE OF SIX OBSERVED WHAT ANSWERED; NONE OF SIX CHECKED IT.
        `item_variant_provider` read `model`/`provider` off the response
        envelope; the other five declared `choices` and `usage` only, so the
        served model was dropped unparsed and every record named the model
        that was ASKED FOR. The two readers of `served_model` build a
        provenance string and compare nothing.
        Now: `services/served_model_identity.py` is the one derivation
        (`observe`, `substituted`, and the fingerprint that finally gives
        `observed_fingerprint_sha256` a producer), all six envelopes read the
        answer, all six record `served_model`/`served_provider` on EVERY
        outcome including refusals, and a silent endpoint reads as
        unobserved rather than as agreement. 14 tests in
        `test_served_model_identity.py` — including a parametrized ratchet
        so a seventh provider cannot skip it — plus one driven substitution
        test in each of the six provider suites.
        ONE CAVEAT ON THE ABOVE, recorded rather than discovered later. Four
        of the consumers digest a whole invocation dump and re-derive it on
        read (`source_claim_service` over `canonical_json_bytes`,
        `curriculum_alignment`'s re-dump, and the lesson-assist chain). That
        is symmetric within a run, so a fresh database verifies — but an
        invocation STORED BEFORE these two fields existed would re-parse with
        them present as None and fail its own provenance check. The house
        already solved this once: `item_variants.WITNESS_OPTIONAL_FIELDS`
        leaves such fields out of the witness when unset. Not a live defect,
        because nothing in this estate has stored invocations predating
        today; it becomes one the moment something does.
        AND IT ALREADY BIT ONCE, in a smaller way: `lesson_assist_provider`
        sealed its digest over a DICT that omitted the new keys while the
        model defaulted them, so three lesson-dossier tests failed with an
        invalid digest chain. A DICT THAT OMITS A KEY DIGESTS DIFFERENTLY
        FROM A MODEL THAT DEFAULTS IT. Fixed the same day, along with a
        frozen dataclass whose required new fields turned a model path into
        its deterministic fallback by raising TypeError at every other
        construction site.
        CORRECTION TO THIS RECORD'S OWN EARLIER CLAIM: "Admission and the
        kill switch are M7.11's, already closed" IS WRONG for these paths.
        `provider_model_admission_engine`'s docstring says "Every path that
        could end in 'send this prompt to the tenant's model' runs through
        `check_serve` … There is no second path." There are six.
        `check_serve`'s only callers are the admission module and its own
        route. The kill switch cannot stop any of the six.
        STILL OPEN, AND WHY NOTHING IS CHECKED HERE: the refusal half.
        Closing it needs a store of the last observed fingerprint per
        endpoint, because a pre-flight check cannot have observed the answer
        to the call it is gating. `test_no_provider_consults_admission_yet`
        pins that open rather than letting it pass unrecorded.
        ALSO OPEN: supply chain, data-use, spend and outage. Spend is two of
        six — `item_variant` and `lesson_assist` price usage and can answer
        `budget_exceeded`; the other four have no budget or cost at all.
        The BFF store this item's spec note names
        (`apps/oshun/bff/src/metis/metis-byom-decision-store.ts`) is
        bring-your-own-MATERIAL source-review governance, not a model
        admission path — a different sense of "BYOM". It has its own unit
        and durability specs; it is not exercised from the service suite,
        and it would not answer this item if it were.
  - [ ] M10.16.d Test learner/minor/PNP/grade/integrity/voice/source PII across
        UI/API/events/logs/exports/analytics/training feedback. Evidence:
        privacy suite.
        OPEN, with one narrow suite: `test_sequencing_overlay_privacy.py`,
        5 tests. Against the seven subjects and eight surfaces this item
        names that is a corner of the matrix. What IS proven elsewhere and
        counts toward it: cohort suppression below a floor of 30 with
        complementary suppression (M10.11.e, M10.12), learner-audience
        redaction on the integrity signal surface (M10.12.b), and the
        synthetic fixture's import-closure proof that it cannot reach a
        learner record at all (M10.11.e). Minor, voice and training-feedback
        PII are untouched.
  - [ ] M10.16.e Test grade/event/correction/LTI/credential tamper/replay/race/
        mass assignment and integrity evidence/decision/appeal/interlock bypass.
        Evidence: authority suite.
        PARTLY DONE, AND M10.12 FOUND THE BYPASS THIS ITEM IS ABOUT.
        Replay and race are covered: `test_mutation_concurrency.py`, 10
        tests, after M10.8.c fixed an idempotency key that was being read as
        a uniqueness token rather than a retry token. Mass assignment is
        structurally refused — every intake model forbids unknown fields —
        and M10.8.b closed a caller that could name somebody else's tenant.
        THE CORRECTION BYPASS IS CLOSED 2026-09-13. It was real and measured:
        a reviewer approved their own grade correction by not mentioning that
        they had reviewed it, because `prior_decisions` was read from the
        request body and defaulted to empty. The route now loads prior
        decisions from the correction store and refuses the field outright;
        the same walk answers `reviewer_cannot_approve` (M10.12.d/.e).
        The appeal bypass still cannot be tested at all — M10.12.c, the
        engine has no route.
  - [ ] M10.16.f Test two-tenant IDs/search/counts/blobs/cache/queues/deep
        links/ exports/integrations/timing and close all release blockers.
        Evidence: adversarial report.
        OPEN. `test_tenant_isolation_authoring_api.py` has 9 tests and
        M8.14 built a two-tenant suite and a tenancy ratchet, so IDs and the
        authoring API are covered. Search, counts, blobs, cache, queues, deep
        links, exports, integrations and timing are not.
        AND THE TENANT MODEL ITSELF IS THE FINDING THIS STAGE KEEPS MEETING.
        Every user is their own tenant, which makes cross-tenant leakage
        structurally hard and multi-person work impossible: four instances
        now — no publication candidate can be approved (M10.9.d), no second
        instructor can review an item (M10.10.c), no fairness reviewer can
        exist (M10.11.e), and no integrity lead can see a reviewer's signal
        (M10.12.b). Release blockers cannot be closed while those stand.
- [ ] M10.17 Run backup/restore and late-event replay drills. Evidence:
      gradebook, audit, blobs, and search reconcile.
      OPEN WITH .b DONE FOR REAL. The restore half was run against the
      compose Postgres and verified by hashing 163 tables on both sides
      rather than counting them. The replay half is not a drill yet, blobs
      are untouched because MinIO is not running, and the gradebook cannot
      reconcile because its correction tables have no writer (M10.12.d).
      Two facts the drill established that no document had: there is no
      point-in-time recovery, and nothing schedules a backup.
      ALL FOUR LEGS OF THE EVIDENCE SENTENCE NOW RECONCILE ON A RESTORED
      ESTATE (2026-09-18), and the three gaps the paragraph above names are
      closed or superseded: the replay half was drilled on 2026-09-13 (.c),
      the gradebook's correction tables got their writer the same day
      (M10.12.d), and MinIO is running on this host now.
      `tests/test_estate_restore_drill_real_stack.py` is the drill, and a
      test rather than a script so it can be run again instead of described:
      a real journey through the durable pipeline, `pg_dump -Fc`, the
      originals bucket mirrored through the S3 API (`mc` is not installed
      here, so each object is read and written back with its digest
      recomputed, which is what `BlobStore.put` does), the database restored
      into a second one and the objects wiped and restored, then
      `durable_reconciler.reconcile` run against the restored pair.
      VERIFIED BY HASHING BOTH STORES, never by counting: 172 tables digested
      with `md5(string_agg(row::text ORDER BY row::text))` on both sides, all
      172 equal, 7 of them non-empty; every object compared on length, on the
      sha256 of its bytes and on its content type. The reconciler then
      answered clean on all of it — blobs, search index, outbox, exports,
      grades and M10.17.d's record legs — over 15 restored revisions.
      THE DRILL'S OWN FINDING, and the reason the blob half was worth running
      rather than assuming: a restore into a differently-named bucket leaves
      every receipt pointing at a bucket that does not hold the object, and
      the reconciler says so. The measurement and what it implies for a
      restore are in .e.
      STILL OPEN, WHICH IS WHY THIS STAYS UNCHECKED: .a's DR plan, .d's
      remaining legs, and .e's scheduled backup, encryption, retention and
      runbook. The two facts .b established are unchanged — there is no
      point-in-time recovery and nothing schedules a backup.
      <!-- SPEC 2026-09-11: real stores only — `pg_dump`/`pg_restore` into a
      second database on the compose Postgres, MinIO `mc mirror` into a second
      bucket, outbox replay through the service's own worker. A restore is
      verified by hashing rows and objects on both sides, never by row counts.
      RPO/RTO in .a are declared numbers; .e measures them. -->
  - [ ] M10.17.a Define backup scope/frequency/encryption/key/retention/RPO/RTO
        for database/blobs/audit/search config/standards/integration state and
        document rebuildable stores. Evidence: DR plan.
        OPEN, AND ONE NUMBER IN IT IS NOW DECIDED BY THE SERVER RATHER THAN
        BY A PLAN. `archive_mode` is OFF on the compose Postgres and
        `wal_level` is `replica`, so THERE IS NO POINT-IN-TIME RECOVERY: a
        restore can only go back to the last dump, never to a chosen moment.
        Any RPO written here is therefore bounded below by the backup
        interval, and no interval is configured either — nothing schedules a
        dump.
        RTO HAS A MEASURED FLOOR from .b's drill: 204 ms to dump and 1,981 ms
        to restore, 2.2 seconds total for 1.1 MB. That is a floor for a
        development-sized database and says nothing about production, whose
        volume is unrecorded (the same gap M10.14.b is blocked on).
        Scope, frequency, encryption, key management and retention are
        undefined, and the blob half is untouched — MinIO is a compose
        profile that is not running here.
        THE BLOB HALF IS NO LONGER UNTOUCHED (2026-09-18). MinIO runs on this
        host now, and the drill named in the parent backs up and restores the
        originals bucket beside the database, so the scope this plan has to
        state is two stores and not one. One constraint on it is measured
        rather than open to choice: the objects must come back under the
        bucket name the receipts already name, because `BlobStore.verify`
        reads `receipt.bucket` and the receipts are immutable (see .e, step
        3). Scope, frequency, encryption, key management and retention remain
        undefined, and the RPO any of them implies is still bounded below by
        a backup interval nothing configures.
        SCOPE, FREQUENCY AND RETENTION ARE NOW STATED IN CODE rather than
        owed by this plan (2026-09-18, with .e). `services/estate_backup.py`
        backs up the database and the source bucket, and its docstring says
        why the other four buckets are not: `s3_bucket_exports` holds
        artifacts a release republishes, `s3_bucket_courses` and
        `s3_bucket_assets` hold derivatives of records in the database, and
        `s3_bucket_uploads` is swept daily by `tasks.cleanup`. That is this
        clause's "document rebuildable stores", written where the code that
        acts on it lives. Frequency is `backup_interval_seconds` (default
        86,400) and retention is `backup_retention_count` (default 7) — both
        DEFAULTS, and the decision this plan still owes is what they should be
        for a deployment, which needs the production volume M10.14.b is
        blocked on. ENCRYPTION AND KEY MANAGEMENT ARE STILL UNDEFINED and are
        the clause's real remainder: the dumps land in object storage
        unencrypted, and where a key would live is an operator decision, not a
        default this session can pick.
  - [x] M10.17.b Take/verify backups and restore into isolated environment at
        selected point with tenant/permissions/hash integrity. Evidence: restore
        drill.
        DRILL RUN FOR REAL, against the compose Postgres, on the database
        this session's journeys wrote to. `pg_dump -Fc` of
        `metis_item_journey` (1,123,762 bytes), `createdb` +
        `pg_restore --no-owner --no-privileges` into an isolated
        `metis_restore_drill`, exit 0.
        VERIFIED BY HASHING ROWS, NOT BY COUNTING THEM, which is what this
        item's spec insists on: for each of the 163 public tables on both
        sides, `md5(string_agg(row::text ORDER BY row::text))`. ALL 163
        DIGESTS MATCH. 18 of the tables are non-empty, so this is not a
        comparison of nothing — a count would have said two tables agreed
        whenever they merely had the same number of different rows.
        Tenant integrity follows from the row digests: `tenant_id` is part
        of every row hashed.
        PERMISSION INTEGRITY IS VACUOUS HERE, and measured rather than
        assumed: the restore dropped owners and privileges, and the source
        database has ZERO role grants on `public` beyond the owner and
        PUBLIC, so nothing existed to lose. On a deployment that grants to
        real roles this drill would have to be re-run without those flags.
        "AT SELECTED POINT" IS NOT DEMONSTRATED AND CANNOT BE — see .a.
        The drill databases were dropped afterwards.
  - [ ] M10.17.c Replay outbox/grade/LMS/integrity/revision/events including
        duplicate/out-of-order/late/correction/void cases. Evidence: replay
        drill.
        THE DRILL IS RUN NOW, FOR REAL, and it is `scripts/replay_drill.py`
        so it can be run again rather than described. Against the compose
        Postgres, 2026-09-13:
        SEVEN OUTBOX EVENTS SURVIVED A RESTORE AND THE SERVICE'S OWN WORKER
        DRAINED THEM THERE. Six lesson aggregates plus one revision were
        written through the real `AggregateStore`, leaving 7 undelivered
        events; `pg_dump -Fc` then `pg_restore` into an isolated database
        carried all 7 across; `metis.tasks.durable.drain_outbox` — the task
        beat runs every 10 seconds, executed in its OWN process reading its
        OWN configuration pointed at the restored database, not a function
        handed an engine — reported `delivered=7 failed=0`. Undelivered went
        7 to 0 and `search_index_document` went 0 to 6 (six lessons; the
        seventh event is a second revision of one of them and upserts the
        same document). A SECOND drain delivered nothing and left the index
        at 6, which is the idempotence a re-run after a crash depends on.
        THE NAMED GRADE CASES, replayed on the RESTORED database through the
        ledger's own append path: duplicate (the same source event id
        retried came back `appended=False` rather than refused for carrying
        a stale version — a retry is not a new append), out-of-order/late
        (an event that HAPPENED three hours earlier arriving second was
        appended), correction (naming the event it corrects), and void (no
        value). Five appends, four entries held, and a history derived from
        them. `reconcile` answered `never_projected`, which is correct and
        is M10.17.d's own point: the projection is built lazily on read, so
        a restored grade nobody has read has nothing stored to compare.
        TWO FAIL-LOUD SEAMS PROVED THEMSELVES while building this, both
        worth recording because each looked like a defect first. A revision
        whose payload offers no `index` key is indexed not at all — the
        record decides what search may hold, the relay does not guess from
        the content. And offering `lesson.summary` was REFUSED outright:
        "is not classified; indexing it and dropping it are both a decision
        nobody recorded". That refusal isolated correctly — all 7 events
        stayed undelivered and nothing was half-applied, which is the
        relay's "ONE EVENT'S FAILURE IS THAT EVENT'S" claim, measured.
        A SECOND EVENT TYPE, because everything above is `aggregate.revised`.
        Withdrawing a lesson on the restored database produced
        `aggregate.transitioned`, the worker delivered it, and the document
        went to `state = deleted` while the ROW REMAINED. That is correct and
        deliberate — a delete leaves a tombstone "so a late upsert for" an
        older revision cannot resurrect what was withdrawn — and asserting the
        row COUNT read as a defect until the state column was looked at. Live
        documents went 6 to 5; rows stayed 6.
        THE LMS LEG IS DRIVEN TOO, through the INGRESS road rather than the
        ledger's own API: `ingest_ags_score`, which resolves the binding and
        derives the grade id from the platform triple. A first post appended;
        THE SAME POST REDELIVERED came back `accepted=True appended=False`,
        which is idempotence on the source's own id rather than a refusal; a
        later score appended; and a post out of 10 against a binding out of
        100 was REFUSED with `scale_disagreement` rather than rescaled, which
        is the silent mapping M10.7.f exists to block.
        STILL OPEN, WHICH IS WHY THIS STAYS UNCHECKED: the integrity replay
        this clause names. And the reason is worth more than the gap —
        THERE IS NO INTEGRITY EVENT STREAM TO REPLAY. Only `aggregate_store`
        and `source_graph` write to `store_outbox`; no integrity service
        references an outbox at all. So this leg cannot be driven until
        integrity emits something durable, and recording it as covered
        because nothing broke would be closing a clause over an absence.
        THE RELAY'S OTHER TWO EVENT TYPES ARE DRIVEN NOW TOO, so all four
        are. A source and a lesson that CITES it were written on the restored
        database — the citation is what puts a row in `store_references` —
        and then `source.retracted` moved the citing lesson to `quarantined`
        and `source.restored` lifted it back to `live`, each delivered by the
        worker. So a retraction reaches what depended on the source, through
        a restore, without anybody calling the relay by hand.
        ONE OBSERVATION RECORDED RATHER THAN ASSERTED: `retract` emits an
        outbox event per DEPENDENT, so the retracted source's own index
        document stays `live`. Whether a `factually_wrong` source should also
        stop being served by that act, or only by a revision marking it
        unavailable (which is the path the relay's quarantine branch
        describes), is a design question this drill does not answer and no
        claim is made about it here.
        The earlier reading of this item, still true of what it covered:
        M8.11's grade ledger handles late-event replay over a 24-per-day
        matrix — AN ARRIVAL ORDER IS NOT AN EVENT ORDER — and M10.8.c's
        idempotency work made a retry replay the first attempt's
        acknowledgement rather than answering `stale`. What is missing is
        the drill this item asks for: replay through the service's OWN
        worker against a restored database, which is the only way the
        outbox and the projections are exercised together.
  - [ ] M10.17.d Rebuild/reconcile current grade/course/build/audit/search/blob/
        dependency/integration projections to authoritative histories. Evidence:
        reconciliation report.
        STILL BLOCKED ON .c, BUT ITS SECOND BLOCKER IS GONE AND THE REAL
        GAP IS NOW NAMED. The correction tables have a writer as of
        2026-09-13 (M10.12.d), and the partition defect that would have
        split a grade's history across two tenant ids is fixed, so a grade
        history is now persisted and reachable under one key.
        WHAT THE INPUTS ACTUALLY ARE, MEASURED 2026-09-13 BY DRIVING IT.
        `grade_ledger.project()` takes the entries, a `GradeScale` and a
        `learner_digest`. Bound a line item, ingested one FullyGraded AGS
        score through the real `ingest_ags_score`, then asked which of them
        the projection depends on:
        - the entries are durable;
        - THE SCALE MATTERS AND IS RECOVERABLE. Deriving with a wrong scale
          is REFUSED (`LedgerError`, via `GradeHistory`'s own check that
          every event is on the bound scale). `grade_ingress_binding` stores
          `scale_id` beside `scale_minimum/maximum/decimals` and the entry
          carries `scale_id`, so a join on `(tenant_id, scale_id)` reaches
          it. Nothing performs that join today, but the data is there;
        - THE LEARNER DIGEST AFFECTS NOTHING. Deriving the same entries with
          a completely different digest produced the IDENTICAL
          `history_sha256` and `current_value`. It is a field of
          `GradeHistory` that no validator reads and that `substance()` —
          what the digest is taken over — does not include. It is a required
          parameter that changes no output.
        CORRECTION TO THIS RECORD'S FIRST VERSION, which said the rebuild's
        inputs were not durable and no projection could be rebuilt. That was
        wrong, and it was written from the function's SIGNATURE rather than
        from what the function uses. A grade projection CAN be rebuilt from
        the record — through a join nothing performs, on a `scale_id` that
        is not guaranteed unique within a tenant. The honest gap is smaller
        and is the two items below.
        A SECOND THING THE WALK SHOWED: after a successful ingest,
        `grade_ledger_projection` HAD NO ROW. The projection is built lazily
        on read (`projection_for_ags`), so "reconcile the projection against
        the history" has nothing to compare for any grade nobody has read.
        What the walk did confirm working: given the inputs, `rebuild()`
        derives the history and emits nothing — outbox 1 before, 1 after.
        THE GRADE LEG IS NOW BUILT AND WIRED IN (2026-09-13), and the rest of
        this clause is not. `grade_ledger.reconcile()` re-derives one grade
        from its own entries and compares with the stored projection, WRITING
        NOTHING — which is why it is not `rebuild()`: that calls `project()`,
        which upserts, so it repairs while reporting, and something that
        changes what it measures cannot report that the thing was already
        right. `durable_reconciler.Reconciliation` gains `grades_disagree`
        (counts against `clean`) and `grades_never_projected` (does not — a
        lazily-built projection that nobody has read is an absence, not drift),
        and `reconcile()` now sweeps every grade the tenant holds an entry for.
        Migration 077 + `grade_ledger_derivation` record the scale per grade,
        written with the first entry and never rewritten, because the scale was
        not unambiguously recoverable (see above) and a wrong one is refused.
        8 tests against a real database, including TWO tamper cases. The second
        exists because the first was BELIEVED: `stored_projection` parses
        `document_json`, so moving only the `history_sha256` COLUMN changed
        nothing the comparison reads and the reconciliation cheerfully agreed.
        That is now its own refusal, `projection_row_inconsistent` — a row that
        disagrees with its own document is corrupt in a way the history
        comparison cannot see.
        WHAT IS ACTUALLY MISSING, measured 2026-09-13:
        `services/durable_reconciler.py` (M10.4.d) reconciles blobs, search
        index, outbox, publication and owner queues — `grep -n grade`
        returns ZERO HITS. Grades are not reconciled by the estate's
        reconciler at all. Meanwhile `services/grade_ledger.py` (M8.11)
        ALREADY HAS the machinery this clause wants — `project()`,
        `rebuild()`, `stored_projection()`, `entries()`, and a rebuild that
        deliberately "announces nothing" so recomputing does not wake the
        outbox. So the rebuild exists and NOTHING DRIVES IT AS A
        RECONCILIATION. That is the work, and it is smaller than the
        blocker it replaced.
        The aggregate store's five tables DO hold authoritative revisions and
        an outbox, so the course, audit and dependency projections are
        rebuildable in principle.
        THE COURSE, AUDIT AND DEPENDENCY LEGS ARE BUILT NOW (2026-09-18),
        and the sentence above is what they were built on.
        `services/record_reconciler.py` sweeps one tenant and reports, writing
        nothing; `durable_reconciler.reconcile` carries it, so the scheduled
        `reconcile_estate` task is the caller. Every leg below was missing, and
        `store_aggregates` — the row every other leg of M10.4.d measures
        something against — was never compared with the revisions and moves
        that produced it. The yardstick was unmeasured.
        - THE HISTORY AGAINST ITS OWN BYTES. `content_sha256` is recomputed
          from the stored `content_json`, which is `text` (measured in the
          information schema, so re-hashing the column is hashing the bytes the
          digest covered, not a normalised jsonb round trip). Nothing did this:
          `revision_history.ancestry` walks the `previous_sha256` links and
          never opens the content, so a payload rewritten under the retention
          workflow left two digest columns agreeing with each other. The test
          asserts that `ancestry` still reports that chain intact.
        - THE REVISION SEQUENCE. `ancestry` sets `revision=len(chain)`, so a
          history with revision 2 deleted answers "an intact chain up to
          revision 2" for an aggregate standing at revision 3. Measured, not
          inferred: the test deletes revision 2 under the legal-erasure token
          and reads `walked.revision == 2` back.
        - THE HEAD THE AGGREGATE ROW POINTS AT, read-only. `rebuild_from_
          revisions` compared this already and REPAIRED as it looked, which is
          the same reason the grade leg uses `reconcile` and not `rebuild`.
        - THE OPERATIONAL STATE, REPLAYED FROM TWO WITNESSES. A creation is
          refused in any state but `draft` (`RevisionRequest`'s own validator),
          so the walk has a start, and every move since is recorded twice in
          the transaction that made it. Both must agree with the column and
          with each other. The walk follows the `from`/`to` linkage rather than
          the clock, because transitions are serialised under `FOR UPDATE` —
          two moves cannot leave the same state — while two moves at one pinned
          instant are ordinary in this repo's fixtures.
        - THE AUDIT TRAIL'S COMPLETENESS, for every revision and every move.
        - THE RETRACTION QUEUE AGAINST THE REFERENCE GRAPH. `source_propagation`
          is derived: `retract` walks the citations backwards and writes a row
          per dependent. An open entry must name something a recursive walk of
          `store_references` still reaches from that source, and one that does
          not is an owner being asked about work that never depended on it.
        MIGRATION 082, BECAUSE THE STATE HAD ONE WITNESS AND IT WAS THE WRONG
        ONE. `store_audit` recorded THAT a transition happened and never what
        it moved to: the `from`/`to` pair lived only in the `store_outbox`
        event payload. The outbox carries `published_at`, which the relay
        writes, so 047's immutability trigger cannot be put on it — the
        operational state of every aggregate in this estate was derivable from
        a mutable table and from nothing else. 082 adds `from_state`/`to_state`
        to the trail, nullable and NOT backfilled (a state nobody recorded does
        not get invented into the one table that cannot be corrected), and the
        sweep names the aggregates whose trail predates it instead of reading a
        missing state as `draft`.
        THREE DEFECTS IN ONE FUNCTION, all in `authoring_api.set_archived`,
        found by asking what records `archived_at` (049):
        - IT WROTE NO AUDIT ENTRY AND NO EVENT. An authorised operation —
          `may(role, "archive")` gates it — whose only record was the column it
          wrote. Who shelved a lesson, and when, was unanswerable.
        - IT DISCARDED THE REASON. `ArchiveBody.reason` is required, 8
          characters minimum, and no line of the function read it.
        - ITS STALENESS CHECK READ FROM A PAGE. The expected revision was
          compared against `store.list_aggregates(context, kind=kind)`, which
          is `LIMIT 100 ORDER BY updated_at DESC`, so archiving an aggregate
          outside the hundred most recently touched of its kind was answered
          `not_found` for a record that exists.
        The write is now `AggregateStore.set_archived`: the column and an
        `archived`/`restored` entry in one transaction, under the row's own
        lock, with the caller's reason as the entry's detail. It returns an
        `ArchiveAcknowledgement` and deliberately not a `WriteAcknowledgement`,
        which would have had to name an outbox event id no row answers to — a
        shelf is not a lifecycle state, nothing is served differently for it,
        and the relay refuses an event type it does not know.
        THE REBUILD HALF, which the clause also asks for.
        `rebuild_from_revisions` carried the stored `op_state` forward — it
        repaired the revision pointer and trusted the column for the state,
        including a value somebody had written straight onto the projection —
        and wrote `draft` when the row was missing altogether. It now derives
        the state (trail first, moves second) and REFUSES when neither answers,
        rather than inventing one. An empty move history is not that case and
        must not be treated as one: nothing having moved does derive `draft`.
        The two witnesses are cross-checked before the moves are believed,
        because an empty move list cannot tell "nothing moved" from "the events
        are gone" — the trail's `transitioned` entries can, and they exist even
        in a trail written before 082.
        TWO LEGS OF THE CLAUSE ARE NOT PROJECTIONS, MEASURED RATHER THAN
        SKIPPED. Dependency: both ends of `store_references` are foreign keys
        into `store_revisions` (046), so a dangling reference is refused by the
        database and there is nothing to reconcile; what IS derived from the
        graph is the retraction queue, and that is the leg above. Integration:
        `grade_ingress_binding` is registered and not derived (`grade_ingress`
        says so in its own docstring, and registering it is why M10.7.f can
        refuse a scale disagreement), `integration_workspace` is an aggregate
        saved whole, `roster_grade_identity` holds only what the SIS supplied
        through its single writer, and `lti_line_item` records what this tool
        created. None is computed from another table, so none has a history to
        be rebuilt from, and the drill that covers them is .b's restore.
        STILL OPEN, WHICH IS WHY THIS STAYS UNCHECKED:
        - THE QUEUE RECONCILES IN ONE DIRECTION ONLY. The other — every
          dependent of a retracted source is in the queue — needs to know WHICH
          REVISION of the source was retracted, because the walk is over exact
          revisions. `source_propagation` records `source_id` and the reason and
          not the revision, so the set it ought to contain is not recoverable,
          and a sweep that assumed "any revision" would report every citer of an
          older revision as missing.
        - A LIVE DEFECT THIS SWEEP CANNOT SEE, found while building it and not
          fixed here: `retract` calls `impact_preview(..., limit=1000)` and
          iterates `preview.rows`, never reading `next_cursor` or
          `truncated_at_depth`. A blast radius wider than 1,000 dependents or
          deeper than `MAX_DEPTH = 6` is silently not propagated — no entry, no
          event, no owner told — and `SourceRetractionResult` carries no
          truncation flag, so the caller cannot tell either.
        - `project_id`/`course_id` are still recorded nowhere but the
          projection. They are write-once (a revision that would move an
          aggregate between courses is refused), so the projection is its own
          record; the sweep counts them as an absence it could not check rather
          than calling them consistent.
        - .c's integrity replay, unchanged: no integrity service writes to an
          outbox, so that leg still has nothing to replay.
        EVIDENCE. `services/record_reconciler.py`, migration 082,
        `AggregateStore.set_archived`, `authoring_api.set_archived`,
        `revision_history.rebuild_from_revisions`,
        `durable_reconciler.Reconciliation.record`, `tasks/durable.py`. 13
        tests in `tests/test_record_reconciliation_real_stack.py` (every tamper
        read back before it is judged — the first one did not land, and the
        read-back is what said so), 3 added to
        `tests/test_aggregate_store_real_database.py` and 2 to
        `tests/test_revision_history_real_database.py`; 112 passing across the
        six affected suites against a real PostgreSQL, MinIO and Redis stack.
        `metis:contracts:check` reports no OpenAPI change: nothing here is on a
        route.
  - [ ] M10.17.e Measure RPO/RTO, residual/manual steps, update runbooks/tests,
        and close gaps. Evidence: game-day report.
        PART MEASURED, THE REST OPEN. RTO has a real floor from .b — 2.2
        seconds end to end at development volume — and RPO is unbounded
        because nothing schedules a backup and WAL archiving is off, so the
        honest answer is "everything since whenever somebody last ran
        pg_dump by hand". Every step in .b's drill was manual; none of it is
        a runbook or a scheduled job yet.
        RTO NOW HAS A FLOOR FOR BOTH HALVES, measured 2026-09-18 by the
        drill in `tests/test_estate_restore_drill_real_stack.py` rather than
        by .b's database-only run: dump 0.181 s, restore 1.790 s, object
        mirror 0.029 s, object restore 0.045 s — 2.045 s end to end for a
        938,995-byte dump of 172 tables (7 non-empty, 15 revisions) and two
        objects totalling 1,723 bytes. A development volume still, and
        production volume is still unrecorded (the gap M10.14.b is blocked
        on), so this is a floor and not a projection.
        RPO IS UNCHANGED AND STILL UNBOUNDED: nothing schedules a dump and
        `archive_mode` is off, so the honest answer remains "everything since
        whenever somebody last ran pg_dump by hand".
        THE RESIDUAL MANUAL STEPS ARE NOW ENUMERATED BY HAVING PERFORMED
        THEM, not by imagining the restore:
        1. create the target database (`CREATE DATABASE`) — `pg_restore` does
           not, and the drill fails at this step without it;
        2. `pg_restore --no-owner --no-privileges`, which is only vacuously
           safe here: the source database has no role grants to lose (.b
           measured that), so a deployment with real roles needs the drill
           re-run without those flags;
        3. RESTORE THE OBJECTS INTO THE BUCKET NAME THE RECEIPTS ALREADY
           NAME. This is the step no document had, and the drill establishes
           it by measurement: `BlobReceipt` carries its bucket and
           `BlobStore.verify` reads `receipt.bucket`, not the bucket the store
           was handed. With the objects mirrored into a second bucket and the
           original emptied, the reconciler reported every blob broken — and
           reported them broken again when pointed AT the backup bucket,
           because the receipt decides where it looks. The receipts cannot be
           rewritten to suit: they live in `store_revisions`, which 047 makes
           immutable outside an approved retention or legal workflow. So a
           restore either recreates the bucket under its original name or it
           produces an estate whose every blob is unverifiable;
        4. drain the outbox on the restored database (.c's leg), because a
           restore lands undelivered events that only the worker clears.
        A DESIGN QUESTION THIS DRILL RAISES AND DOES NOT ANSWER, recorded
        rather than quietly settled: a bucket in a receipt is payload
        deciding where the service reads. Making `verify` and `get` look only
        in their own bucket would remove that and make a renamed restore
        verifiable, but it would also fail any legitimate cross-bucket
        receipt (an export read through a store holding the originals), and
        `durable_reconciler` verifies every receipt it finds in a revision
        against one store. It is a boundary change with a security reading on
        both sides and it wants a decision, not a guess.
        STILL OPEN: the scheduled backup that would bound RPO, encryption and
        key management for the dumps, retention, and the runbook itself —
        none of the four steps above is written down anywhere but here and in
        the drill.
        THE SCHEDULED BACKUP IS BUILT (2026-09-18), SO RPO IS A NUMBER NOW.
        `services/estate_backup.py` + migration 083 + the beat entry
        `back-up-estate`: a `pg_dump -Fc` of the configured database every
        `backup_interval_seconds` (default 86,400), the source bucket mirrored
        beside it, and a row in `estate_backup_run` for every attempt.
        RPO ≤ the interval + the run, and `last_successful()` is what makes the
        bound READABLE — the question an incident asks is when the last one
        succeeded, and before this there was nowhere to ask it.
        A DUMP NOTHING HAS READ IS NOT A BACKUP, so a run is not `succeeded`
        until `pg_restore --list` has parsed the archive and its table count is
        recorded; the table's CHECK refuses a succeeded row that does not name
        its artifact, length and digest. The test goes further and RESTORES the
        dump into another database, comparing every table digest on both
        sides: all equal except `estate_backup_run` itself, which differs for
        a reason worth recording — the run row is written AFTER the dump, so a
        dump cannot contain its own record.
        THE TWO FAILURE PATHS ARE DIFFERENT, DELIBERATELY. A missing `pg_dump`
        or a missing backup bucket writes NO row and raises
        `BackupUnavailableError`: a precondition nobody met is not an attempt
        on the database, and recording it as a failed run would say one was
        made. A dump that ran and failed IS an attempt and is recorded as
        `failed` before the error is raised, because a gap in the schedule is
        only visible if the failures are in the table beside the successes. A
        failed run never becomes `last_successful` — asserted, not assumed.
        RETENTION IS CONFIGURED AND PRUNES ARCHIVES, NEVER RECORDS:
        `backup_retention_count` (default 7) dumps are kept, the rest are
        deleted from the bucket and their rows stay, so the history of what was
        taken survives the archive it pointed at. Source objects are never
        pruned — they are content-addressed and a revision names them forever,
        so an object no longer in the sources bucket is exactly what the backup
        exists to return.
        WHAT THE SCHEDULE DOES NOT SETTLE, and .a still owes: the interval is a
        DEFAULT and not a decision; there is still no point-in-time recovery
        (`archive_mode` is off, so the floor is the last dump and never a
        chosen moment); the dumps are not encrypted and key management is
        undecided; and the runbook is still only this record and the drill.
        5 tests in `tests/test_estate_backup_real_stack.py` against a real
        database and a real bucket.
        THE RUNBOOK IS UPDATED TOO, which is the other half of this clause.
        `docs/runbooks/metis-disaster-recovery.md` goes to 2.0.0, and the
        revision corrected a statement as well as adding the new steps: VERSION
        1.0.0 NAMED THE WRONG BUCKETS FOR BLOB RECOVERY. It listed
        `metis-courses`, `metis-assets`, `metis-exports` and `metis-uploads`,
        and the durable bytes a receipt points at are in `metis-sources`
        (`s3_bucket_sources`, M10.4) — the one bucket it did not name. The
        runbook now opens with how to read the recovery point out of
        `estate_backup_run`, carries the bucket-name constraint, and ends with
        drain + reconcile before traffic is switched.
        AND ONE PROCEDURE IS CODE RATHER THAN A SHELL LINE. "Mirror the bucket
        back" has no CLI here — `which aws mc` finds neither — and either could
        be installed, but `estate_backup.restore_sources` is what the service's
        tests exercise, and a restore step only ever run by hand is a step
        nobody has verified. The runbook invokes the function. Rehearsed while
        writing it — `(1, 27)` on the first run and `(0, 0)` on the second,
        which is the idempotence an interrupted restore depends on.
        The runbook was also missing from `docs/runbooks/index.md` entirely,
        which its own version 1.0.0 never added; it has a row now.
        6 tests in `tests/test_estate_backup_real_stack.py` against a real
        database and a real bucket.
- [ ] M10.18 Publish educator, psychometrician, accessibility, integrity,
      institutional, incident, and interoperability runbooks. Evidence: game-day
      results.
      OPEN, WITH ONE RUNBOOK PUBLISHED AND EXERCISED. The rest are held up
      less by writing effort than by the workflows they would describe: an
      appeal with no route, a reconciliation with no destination, a review
      with no second reviewer. Writing those procedures now would document
      steps nobody can take.
      <!-- SPEC 2026-09-11: .a–.e are documents an agent writes from the shipped
      code paths, each command in them executed once while writing; .f's
      cross-role game day needs named people and stays open (§2.5.9). -->
  - [ ] M10.18.a Define runbook template/owners/on-call/prerequisites/roles/
        dashboards/commands/evidence/comms/recovery/verification/version review.
        Evidence: runbook index.
        PARTLY: the template is now demonstrated rather than specified.
        `docs/runbooks/metis-disaster-recovery.md` follows the house
        frontmatter already in `docs/runbooks/` — title, owner, version,
        last_reviewed, next_review, applies_to — and carries prerequisites,
        commands, verification, cleanup, a failure path and a gap table.
        OWNERS AND ON-CALL CANNOT BE DEFINED HERE. There is no owner
        registry in this estate; M10.13.f and M10.14.e record the same
        absence for accessibility and performance. Every runbook written
        now has to name its owner as unassigned, which is honest and is not
        a template problem.
  - [ ] M10.18.b Publish educator authoring/review/source/assessment/publication
        and psychometric calibration/DIF/exposure/item lifecycle procedures.
        Evidence: role runbooks.
        NOT WRITTEN. The material exists and is unusually well verified —
        M10.10 walked authoring and review, M10.11 walked calibration, DIF
        and the item lifecycle — but several of those procedures would have
        to document a step nobody can take: no second instructor can review
        an item, and no fairness reviewer can exist. A role runbook whose
        first instruction is impossible is worse than none.
  - [ ] M10.18.c Publish accessibility remediation/alternative/waiver/escalation
        and integrity signal/case/appeal/grade correction procedures. Evidence:
        governance runbooks.
        NOT WRITTEN, and two of the four procedures cannot be: the appeal
        workflow has no route to describe (M10.12.c), and the grade
        correction procedure would have to tell a reviewer to disclose their
        own prior decision, which the endpoint does not require and cannot
        check (M10.12.e). The accessibility remediation half is writable now
        that M10.13 has the gates and the ceilings.
  - [ ] M10.18.d Publish institution integration/roster/LTI/grade/export/
        credential/version/reconciliation and standards interoperability
        procedures. Evidence: integration runbooks.
        NOT WRITTEN. The reconciliation procedure is blocked on the same
        absence M10.9.d found: the saga's only `Destination` implementation
        is an object store, so there is no LMS to reconcile against.
  - [ ] M10.18.e Publish source withdrawal/answer leak/BYOM/privacy/grade/
        integrity/outage/recall/DR incident procedures. Evidence: incident
        runbooks.
        THE DR PROCEDURE IS PUBLISHED AND EVERY COMMAND IN IT WAS EXECUTED
        WHILE WRITING IT, which is what this item's spec asks for.
        `docs/runbooks/metis-disaster-recovery.md`: take a backup, restore
        into an isolated database, and verify by hashing 163 tables on both
        sides rather than counting rows, with the measured numbers from that
        run — 204 ms to dump, 1,981 ms to restore, all 163 digests identical,
        18 tables non-empty. It also carries what could NOT be executed and
        why: no point-in-time recovery because `archive_mode` is off, no
        schedule, and no blob procedure because MinIO was not running. A
        runbook that described those anyway would be the failure this
        checklist keeps finding.
        It names its own owner as unassigned, because the estate has no
        owner registry and a runbook cannot invent an on-call rota.
        OPEN: source withdrawal, answer leak, BYOM, privacy, grade,
        integrity, outage and recall. The answer-leak procedure is the next
        one worth writing — M10.10.c and M10.16.a have the verified material
        for it, including a fix that is already live.
  - [ ] M10.18.f Execute cross-role game days, record time/gaps/unsafe
        ambiguity, update artifacts, and verify closure. Evidence: game-day
        results.
        STAYS OPEN BY ITS OWN SPEC: a cross-role game day needs named
        people. §S10's finding is the one to keep in view here — every game
        day in that phase reported a result without performing it, and
        `kill -9` on PID 1 exits 0.
- [ ] M10.19 Produce engineering-readiness evidence and reconcile the domain
      registry. Evidence: no planned/live contradiction.
      OPEN, AND ITS EVIDENCE LINE IS CURRENTLY FALSE: there IS a
      planned/live contradiction, measured — three registries answer for
      Metis and none agree. The readiness evidence this item would produce
      also already exists in the estate at 1,262 entries and contains zero
      for this checklist. Both halves are answered by artefacts that point
      somewhere else.
      <!-- SPEC 2026-09-11: the "domain registry" is
      `libs/oshun/navigation/src/release-scope.ts` (Metis scoped to V1.2) plus
      the M0.2 state artifact
      `evidence/v1-workbenches/metis-registry-launch-state.json`; `domains.json`
      is a library-area index, not a status registry. .d compares those two to
      what the `apps/oshun/web` middleware actually serves; .e's ratification is
      human (§2.5.9). -->
  - [ ] M10.19.a Define
        requirement/capability/host/audience/standard/discipline/ integration
        evidence index and freshness/owner rules. Evidence: schema.
        AN EVIDENCE INDEX EXISTS AT SCALE AND IS ABOUT A DIFFERENT
        CHECKLIST. `oshun-readiness-evidence.json` carries 1,262 ledger
        entries over 1,573 checklist cells citing 5,490 artifact paths —
        and `ledgerEntriesForThisChecklist: 0`. Not one entry is for this
        checklist, and the artefact mentions Metis zero times. Its own
        properties: 0 of 6 present, 1 of 7 validations performed, and
        `commitShasThatAreASha: 0` — the commit identifiers it records are
        not shas.
        So the schema half of this item is answered by something that
        already exists; what is missing is an index that points at THIS
        work. Freshness and owner rules are unwritten, and the owner half
        runs into the same absent registry as M10.13.f, M10.14.e and
        M10.18.a.
        THE INDEX AND BOTH RULES EXIST NOW (2026-09-18), and the freshness
        rule turned out to be the one with teeth.
        `scripts/v1-workbenches/generate-metis-evidence-index.mts` →
        `metis-evidence-index.json` beside its own
        `metis-evidence-index.schema.json`, wired into both inventory chains.
        The schema is the deliverable this clause names: one entry per
        artefact, the seven dimensions required as KEYS, and a dimension the
        estate does not record carried as `null` rather than omitted — so an
        absence is counted instead of missing.
        FRESHNESS IS COMPUTABLE WITHOUT THE ARTEFACTS DATING THEMSELVES, which
        matters because M10.19.c measured that only 1 of 20 does. These
        artefacts CITE THEIR OWN SUBJECTS — the threat model names 44 source
        paths, the contract inventory 83, and every one of them exists — so an
        artefact is fresh when its last commit is at or after the newest commit
        touching any source it cites, and stale names the source that moved.
        MEASURED OVER THE DOMAIN'S WHOLE EVIDENCE TREE, 175 files citing 4,146
        sources: **22 fresh, 115 stale, 38 that cite nothing that could date
        them**. The worst is 22 days — `metis-authoring-maturity.json` against
        `apps/metis/service/src/metis/schemas/scorm.py`. Two of the 115 are
        stale because of THIS session: `metis-credential-lifecycle.json` and
        `metis-package-metadata.json` sit behind `credential_service.py`, which
        M10.22's correction touched today. That is the rule working, not noise.
        WHAT STALE MEANS, WRITTEN INTO THE ARTEFACT so nobody reads 115 as 115
        wrong: a cited source changing for an unrelated reason flags its
        artefact too, so stale is "a reviewer has to look again" and the number
        is an upper bound on freshness — the direction a readiness gate should
        be wrong in.
        THE OWNER RULE IS STATED AND UNSATISFIED BY EVERY ROW: 175 of 175 carry
        `owner: null`, because this estate has no owner registry — the same gap
        M10.13.f, M10.14.e and M10.18.a record. The schema requires the field
        so the absence is counted; putting a team name nobody agreed to into
        175 rows would have been the alternative.
        AND THE INDEX POINTS AT THIS WORK, which is what this clause said was
        missing: each entry lists the §M10 cells that cite it. **23 of 175 are
        cited by any cell**, which is the same finding .c reports from the other
        side. Capability, audience and discipline are `null` for all 175 —
        nothing in the estate records them — and 50 artefacts name an
        interoperability standard in their own text.
        8 tests, including the trap this rule could most easily have fallen
        into: an artefact that cites nothing answers `null`, never `fresh`.
        Calling an undatable artefact fresh would have rubber-stamped exactly
        the 38 nobody can check.
        STAYS UNCHECKED. The schema, both rules and the index are here, and the
        three dimensions no registry feeds — capability, audience, discipline —
        are still empty for every row, as is the owner. This clause asks for the
        index AND its rules; the rules now measure how far the estate is from
        satisfying them, which is not the same as satisfying them.
  - [ ] M10.19.b Collect current
        contract/migration/test/evaluation/accessibility/
        performance/security/DR/runbook/approval artifacts by commit/hash.
        Evidence: readiness packet.
        NOT COLLECTED as a packet, though most of the artefacts now exist
        and are addressable: contracts and migrations in the OpenAPI
        document and alembic head 073, tests in the service suite, the
        accessibility gates from M10.13, the DR runbook from M10.18.e, and
        the 190-odd generated inventories under `evidence/v1-workbenches`.
        Collecting them BY COMMIT is the part with no mechanism — the
        existing ledger records commit identifiers that are not shas.
        THE PACKET EXISTS NOW (2026-09-18) AND THE MECHANISM IS THE POINT.
        `scripts/v1-workbenches/generate-metis-readiness-packet.mts` →
        `evidence/v1-workbenches/metis-readiness-packet.json`, wired into both
        `inventory:v1-workbenches` and `verify:inventory:v1-workbenches`,
        because a generator nobody runs is not a gate.
        MEASURED, FIRST RUN: the ten classes this clause names, 39 named paths
        (0 absent, 0 untracked) and 3 collections over 661 files — and
        **42 of 42 commit addresses are a 40-character sha**. That number is
        the whole answer to "the part with no mechanism": the addresses come
        from `git log -1 --format=%H`, which cannot produce `PENDING_COMMIT`,
        and the packet counts the shape rather than checking the field is
        non-empty — which is the check that let the existing ledger's 1,262
        placeholder rows through.
        EVERY ARTEFACT IS ADDRESSED TWICE: the sha256 of its bytes now, and the
        commit that last touched its path. The file lists come from
        `git ls-files`, so an uncommitted artefact is ABSENT from the packet
        rather than present with an invented history.
        ONE THING THE CLAUSE CANNOT HAVE, and the packet says so rather than
        pretending: a packet addressed by commit cannot be drift-checked by its
        commit addresses, because the commit that will carry an artefact does
        not exist while the packet naming it is written. `--check` therefore
        compares the CONTENT projection — paths, digests, counts, citations —
        and reports how many addresses have moved since, as information. A test
        asserts both halves of that: a changed digest moves the projection and a
        changed address does not.
        AND IT ALREADY ANSWERS HALF OF .c: each path is checked against this
        checklist's text, and **14 of 39 are cited by it**. The other 25 are
        artefacts no cell's evidence sentence points at — the orphan direction
        .c asks for, measured here rather than asserted there.
        8 tests in `generate-metis-readiness-packet.test.mts`, including the
        exact trap: the address set must not contain `PENDING_COMMIT`, and the
        citation count must be neither 0 (the checklist was not read) nor all of
        them (a count that proves nothing).
        STAYS UNCHECKED, AND THE DOUBT IS THE REASON RATHER THAN AN ABSENCE OF
        WORK. The ten classes are the clause's own, but WHICH artefacts belong
        to each class is a judgement this session made: the estate holds about
        140 `metis-*` evidence files and the packet names 39 of them. The
        mechanism is complete and the enumeration is an opinion, so the box
        waits on somebody who owns the domain agreeing that these are the
        artefacts — which is also what .a's freshness and owner rules would
        settle.
  - [ ] M10.19.c Validate evidence pass/current/scope/environment/version and
        bidirectional mapping to every supported matrix/ledger row. Evidence:
        zero-orphan report.
        BLOCKED ON .b, and the orphan count is already known to be total
        rather than zero: with `ledgerEntriesForThisChecklist: 0`, every
        row of this checklist is an orphan of that ledger and every one of
        its 1,262 entries is an orphan of this checklist. A bidirectional
        mapping between two sets that share nothing is empty in both
        directions.
        THE REPORT EXISTS NOW (2026-09-18) AND THE ORPHAN COUNT IS NOT TOTAL.
        .b's packet gave this something to map against, so the mapping is over
        §M10's own cells rather than over a ledger for another programme:
        `scripts/v1-workbenches/generate-metis-evidence-orphans.mts` →
        `evidence/v1-workbenches/metis-evidence-orphans.json`, wired into both
        inventory chains.
        MEASURED, BOTH DIRECTIONS, 145 cells (48 checked). The figures below
        are the run that includes this record, because writing a record CHANGES
        THE MEASUREMENT — the first run said 61 cells named nothing and this
        cell was one of them; citing the artefacts here made it 60. An
        observation that moves what it observes is worth stating rather than
        hiding behind a timestamp. And every later edit to any §M10 record moves
        them again, which is why `metis-evidence-orphans.json` is the number and
        this paragraph is only the reading that prompted it — the counts here
        are the 2026-09-18 run and the shape of the finding, not a live total:
        - **60 cells name no artefact at all.** Their evidence lines say
          "Evidence: schema" or "Evidence: reconciliation report" and point at
          nothing a reader can open. This is the orphan direction that matters,
          and it is 41% of the stage;
        - **20 of the packet's 36 artefacts are named by no cell** — evidence
          nobody's evidence sentence points at;
        - 260 citations. 38 are not paths at all (routes, elided paths, build
          outputs, and identifiers that merely contain a slash), 251 resolve,
          7 are a bare filename several files answer to, and 2 do not resolve;
        - **170 of the 251 resolve only after guessing a root**, and 29 more
          only by searching the index for a unique basename. The cells cite
          `tests/test_backpressure.py` and `services/record_reconciler.py`,
          which are relative to `apps/metis/service` and its `src/metis` — so a
          tool reading this checklist cannot follow most of its citations
          without knowing where each record was written from. That is the finding under the finding:
          the traceability this clause asks for is defeated by the citation
          convention before any ledger is involved.
        THE FIVE DIMENSIONS .c NAMES ARE CHECKED AND **0 OF 20 ARTEFACTS CARRY
        ALL FIVE**: version 19, environment 12, pass 10, scope 10, and
        **currency 1**. One artefact of twenty records when it was generated
        (`metis-approval-subject.json`, which carries a commit and a branch), so
        "is this evidence current?" is unanswerable for the other nineteen. The
        check is key presence and is labelled as a lower bound — it says the
        field is there to be read, not that its value is meaningful.
        TWO CORRECTIONS THIS SESSION MADE TO ITS OWN MEASUREMENTS, both worth
        keeping because each was wrong in a direction that flattered the report:
        - the first run reported **30 dead citations**. A citation pattern that
          matches a slash matches `/api/lti/jwks`, `xapi-2.0/statement/void` and
          `.../governance` too. Classified now, and the real figure is 2;
        - and those 2 are not broken links either. Both are deliberate mentions
          of a file RENAMED to `tests/test_backpressure.py`, and each cell names
          the current path in the same breath. So the stale-link count in §M10
          is ZERO, which a generator cannot tell and a reader can — recorded in
          the artefact's own `method` so nobody reads the JSON and concludes
          otherwise.
        AND A THIRD CORRECTION, ON THIS RECORD ITSELF. The first draft of the
        paragraph above illustrated the root problem with two invented example
        paths, in backticks, and the next run counted them: 4 dead citations,
        two of them written by the sentence explaining the other two. Naming
        them here in backticks re-created them a second time, so this sentence
        does not. An illustrative path in a checklist is a citation to anything
        that reads the checklist; the examples above are real files now.
        9 tests in `generate-metis-evidence-orphans.test.mts`, asserting the
        classifier by value rather than by count.
        STAYS UNCHECKED: the report is the deliverable this clause asks for and
        it reports a non-zero orphan count in both directions. "Zero-orphan" is
        the state, not the artefact — closing this needs the 61 cells to cite
        their evidence and the 20 unnamed artefacts to be cited or dropped.
  - [ ] M10.19.d Compare implemented/exposed/deployed state to domain registry
        planned/internal/preview/live flags by host/audience/region. Evidence:
        reconciliation report.
        THE COMPARISON IS DONE AND THE ANSWER IS THAT NOTHING AGREES.
        `evidence/v1-workbenches/metis-registry-launch-state.json`, which
        M0.2 generates and this session regenerated:
        `registriesAnsweringForMetis: 3`, `registriesThatAgree: 0`.
        Three registries answer for Metis and no two of them say the same
        thing. `lifecycleStatesRequired: 7` against
        `lifecycleStatesTheShellCanRepresent: 1` — the shell has one flag
        where the lifecycle has seven states, so most of the distinctions
        this item asks to be compared BY cannot be expressed at all.
        `propertiesMeasured: 5`, `propertiesPresent: 0`.
        What IS clean is the route half: 11 Oshun-hosted Metis routes, all
        11 covered, `routesNotCovered: []`, and the proxy is the middleware,
        calls the resolver, and refuses before auth.
        So the reconciliation exists and reports a contradiction. Closing
        this needs the contradiction resolved, not another comparison.
  - [ ] M10.19.e Block contradictory shell/routes/capabilities/registry and
        ratify exact registry change/rollback. Evidence: approved decision.
        THE CONTRADICTION IS IDENTIFIED AND CANNOT BE RATIFIED HERE. .d
        shows three registries disagreeing and a shell that can express one
        of seven lifecycle states; blocking on that is correct and is what
        this item asks for. The ratification half is human by this stage's
        own spec, and the same authority gap recurs — M10.14.a's budget
        type refuses to exist without a ratifier for the same reason.
- [ ] M10.20 Obtain named education, accessibility, privacy/security, integrity,
      engineering, and product approvals. Evidence: human signoff.
      OPEN BY DESIGN — the evidence line is human signoff and no agent may
      supply it. The packet half is built: .b freezes and hashes the
      subject, and names the five fields this repository cannot answer
      rather than defaulting them. What an approver would be signing is now
      identifiable; who may sign it is not, because the estate has no
      approver registry.
      <!-- SPEC 2026-09-11: §2.5.9 — packet only. Every child stays open until a
      named person acts; .a and .b are the packet. -->
      _2026-09-18: open by design, as its note says: the packet half is built
      and the evidence is human sign-off (rule 9)._ `blocked:human`
  - [ ] M10.20.a Define each named approver’s authority/scope/independence,
        required packet, decision options/conditions/expiry, and invalidation.
        Evidence: approval matrix.
        OPEN ON THE SAME MISSING REGISTRY THIS PHASE KEEPS MEETING.
        Defining "each NAMED approver's authority" needs a register of
        approvers, and this estate has none — the same absence recorded at
        M10.13.f for accessibility, M10.14.e for performance and M10.18.a
        for runbook owners. What the estate does have, and what an approval
        matrix should reuse rather than reinvent, is M7.13's human-signoff
        contract and M7.8's conflict-of-interest engine, both closed, plus
        the independence rule M10.12.c's appeal engine already states by
        actor digest.
        The required-packet half is answerable and .b answers it.
  - [ ] M10.20.b Freeze exact commit/build/config/environment/host/audience/
        standards/integration/capability subject manifest. Evidence: subject
        hash.
        THE COMPUTABLE HALF IS FROZEN AND HASHED.
        `scripts/export_approval_subject.py` (`pnpm run
        metis:approval:subject`) writes
        `evidence/v1-workbenches/metis-approval-subject.json`: the commit,
        the branch, whether the working tree was clean, the migration head,
        the OpenAPI digest, 882 operations, 3,685 published schemas, the
        three applications, and a `subject_sha256` over all of it. An
        approval can now name a digest, and a later reader can tell whether
        what is in front of them is what was approved.
        FIVE FIELDS THIS ITEM ASKS FOR ARE NAMED AS NOT COMPUTABLE RATHER
        THAN DEFAULTED, because a defaulted field in a frozen subject is the
        one that makes the freeze a lie: there is no build identifier, no
        captured runtime configuration, no addressable deployed environment,
        no agreed host/audience (three registries disagree — M10.19.d), and
        no capability manifest scoped to this checklist.
        The manifest also records `working_tree_clean`, because a subject
        hash taken over a dirty tree names a state nobody can check out.
  - [ ] M10.20.c Obtain separate education/pedagogy/psychometrics,
        accessibility, privacy/security, and integrity approvals with
        conditions. Evidence: governance decisions.
        STAYS OPEN BY THIS STAGE'S OWN SPEC: a named person has to act.
        Recording an approval here would be the exact fabrication this
        checklist exists to catch. What CAN be said is what each approver
        would be reading: accessibility has gates and two open ceilings
        (M10.13), integrity has a measured self-approval hole (M10.12.e),
        and psychometrics has a fairness analysis proven sound in both
        directions (M10.11.b and .c).
  - [ ] M10.20.d Obtain engineering/operations/interoperability and per-host/
        audience product launch decisions. Evidence: launch decisions.
        STAYS OPEN, and per-host/audience decisions are additionally
        unanswerable while three registries disagree about what Metis is
        (M10.19.d).
  - [ ] M10.20.e Record approve/reject/conditional/request-changes immutably and
        revalidate on material evidence/scope/subject change. Evidence: approval
        lifecycle.
        OPEN. The revalidation trigger is now mechanical, which is the
        part worth having early: .b's `subject_sha256` changes whenever the
        commit, the migration head or the published contract does, so
        "material subject change" is a digest comparison rather than a
        judgement. The immutable record itself needs the store and the
        approver registry that .a is blocked on.
- [ ] M10.21 Run full-journey Playwright for lecture/agentic-media creation,
      correctness critique, accessible renditions, localization, and learner
      playback. Evidence: real sandbox/render adapters or explicitly hermetic
      dependencies and exact provenance assertions.
      NOT WALKED. This is the same unwalked UI half M10.10 and M10.12
      carry, with an added dependency: the render stage needs Isis and
      Euterpe through their canonical adapters, and this stage's spec is
      explicit that hermetic never means skipping the render. The lecture
      delivery path itself is real and was read closely under M10.13.e,
      which found the image alternative it drops.
      <!-- SPEC 2026-09-11: "explicitly hermetic" means the real service code
      path with the provider stubbed at its HTTP boundary (the seam the M6/M9
      lecture tests already use), a persistent `fixture` badge in the evidence,
      and the stub named in .a's manifest; it never means skipping the render
      stage. Isis and Euterpe dependencies go through their canonical adapters,
      never private imports (X.5 and X.17 later depend on this). -->
  - [ ] M10.21.a Seed exact lesson/objectives/sources/data/presenter consent/
        locales/targets and live or explicitly hermetic render/Isis/Euterpe
        dependencies. Evidence: fixture manifest.
  - [ ] M10.21.b Author script/scene plan/spec/code/media, execute sandbox
        render with progress/cost, and retain all stage/source/tool provenance.
        Evidence: creation journey.
  - [ ] M10.21.c Run visual/layout/source/math-code/correctness/accessibility/
        technical critics, reject one iteration, revise, and require educator
        acceptance. Evidence: critique journey.
  - [ ] M10.21.d Author/review captions/transcript/description/translation/
        pronunciation/renditions and surgical replacement with stable unaffected
        segments. Evidence: localization/replacement journey.
  - [ ] M10.21.e Assemble package, clear gates, publish, play via real learner
        renderer across locale/device/bandwidth/accessibility. Evidence:
        playback journey.
  - [ ] M10.21.f Assert budget/policy/render/verification stop, no self-publish,
        source/consent withdrawal, tenant/role boundaries, and exact lineage.
        Evidence: E2E trace.
- [ ] M10.22 Run full-journey credential tests from pathway authoring through
      evidence review, Open Badges issuance/verification, expiry, revocation,
      replacement, and audit. Evidence: issuer, privacy, and stale-course
      boundaries.
      NOT WALKED. The clause above was spliced in half by the record that
      followed it — "from pathway authoring through" ran straight into a
      paragraph and the rest of the sentence appeared after it. Rejoined
      2026-09-18.
      AND THE BLOCKER THAT RECORD NAMED IS NOT WHAT HAPPENS. It said the
      credential service keeps revocations in process dictionaries "so a
      restart resurrects a revoked badge". The dictionaries are real — nothing
      in `services/credential_service.py` survives a restart — but the
      direction was measured on 2026-09-18 by driving it, and it is the
      opposite: after the class attributes are cleared, the same credential and
      the same document verify as **`not_found`, `valid=False`**.
      `verify_credential` hands anything the registry does not hold to
      `_verify_unregistered_document`, which returns `valid=False` whatever
      else passes — and the canonical hash and the HMAC proof DO still pass,
      because the signing secret is configuration and survives the restart.
      That is why the claim was plausible and it is still wrong.
      THE REAL DEFECT IS THE SAFE DIRECTION AND IS STILL A DEFECT: every issued
      badge becomes unverifiable after a restart, not just the revoked ones, so
      a learner's legitimate badge stops verifying. A journey that restarts the
      process would find that, which is what this item should walk.
      Four tests pin the direction now
      (`tests/test_credential_service.py::CredentialRestartDirectionTest`),
      including the one that matters: a revoked badge must not verify as valid
      when the issuer cannot account for it. A docstring cannot enforce that,
      and the one that tried had it backwards.
      <!-- SPEC 2026-09-11: .d's clock advance requires the credential service's
      clock to be injectable; if it is not, that injection is the first sub-step
      and is recorded in the evidence. Signatures are real Ed25519 over Open
      Badges 3.0 assertions (`services/open_badges_service.py`); .c's
      verification runs the verifier against the published JSON, never against
      the issuing object in memory. -->
  - [ ] M10.22.a Seed credential issuer/roles/keys/template/objectives/course/
        assessment/evidence policies and synthetic recipient. Evidence: fixture.
  - [ ] M10.22.b Author/approve pathway and complete locked criteria with exact
        learning/assessment/human evidence; test missing/stale/conflicting
        cells. Evidence: eligibility journey.
  - [ ] M10.22.c Review independently, issue/sign/deliver Open Badge, and verify
        signature/issuer/recipient/evidence/status through public/private views.
        Evidence: issuance journey.
  - [ ] M10.22.d Advance fake clock through warning/expiry/renew and execute
        revoke/replacement/supersession/status notification. Evidence: lifecycle
        journey.
  - [ ] M10.22.e Assert author/agent/course completion cannot issue, protected
        learner evidence is minimized, stale course invalidates request, and
        audit remains immutable. Evidence: boundary E2E.
- [ ] M10.23 Run known-value and calibrated suites for atomic claims, citation
      OPEN WITH .f DONE. The coverage gate this stage's evidence line asks
      for exists and passes, and it found the one asymmetry worth knowing:
      five of the six enumerated verifiers are scored against a versioned
      corpus and cultural review is not. Building the corpora .a–.e ask for
      is the remaining work, and .c's calibrated panel sets additionally
      need the human judgements no agent can supply.
      sufficiency, faithfulness, contradiction, math, code, worked solutions,
      evaluator panels, media critics, and discipline-specific verification.
      Evidence: no generic "content quality" test substitutes for an enumerated
      verifier.
      <!-- SPEC 2026-09-11: §2.5.4 and §2.5.5. The enumerated verifiers exist as
      services (`claim_decomposition`, `grounding_gate`,
      `mathematics_verification` with pinned sympy/pint/mpmath,
      `code_verification`, `evaluator_panel`, `cultural_review`); .f's matrix
      names each one with its fixture file, and a verifier whose only coverage
      is a generic content-quality test fails the gate. -->
  - [ ] M10.23.a Build versioned labeled corpora and coverage rows for atomic
        claim segmentation/linking and support/sufficiency/faithfulness/
        contradiction/conflict. Evidence: grounding suites.
  - [ ] M10.23.b Build executable math/code/worked-solution goldens for correct/
        wrong/partial/alternative/unsafe/flaky/timeout/unsupported cases.
        Evidence: deterministic suites.
  - [ ] M10.23.c Build calibrated panel/critic gold sets with evaluator
        dependency/correlation, confidence/abstention/manual bands, champion/
        challenger, and subgroup reports. Evidence: panel reports.
  - [ ] M10.23.d Build diagram/chart/lecture/media visual/layout/accessibility/
        source/correctness critic fixtures and renderer/version goldens.
        Evidence: media critic suites.
  - [ ] M10.23.e Build per-discipline proof/lab/data/source/language/code
        specialized verification fixtures and standards export survival tests.
        Evidence: discipline suites.
  - [x] M10.23.f Publish a verifier-to-fixture/report/threshold/owner matrix and
        fail any enumerated verifier covered only by generic content quality.
        Evidence: dedicated coverage gate.
        THE GATE EXISTS AND ALL SIX CLEAR THE BAR THIS ITEM SETS.
        `tests/test_verifier_coverage_matrix.py`, 7 tests over the six
        verifiers this stage enumerates by name. Each has a service of its
        own, each has at least two dedicated test files, no generic
        content-quality suite exists to stand in for any of them, and every
        corpus a verifier names resolves on disk — a corpus path that does
        not resolve is a verifier scored against nothing, and it would fail
        only when somebody ran it.
        THE MATRIX, measured by reading what each verifier's own modules
        load rather than by matching filenames:
        - `claim_decomposition`     → `m5_19_atomic_claim_corpus.json`
        - `grounding_gate`          → `m5_20_grounding_gate_corpus.json`
        - `mathematics_verification`→ `m5_21_executable_math_corpus.json`
        - `code_verification`       → `m5_22_sandboxed_code_corpus.json`
        - `evaluator_panel`         → `m5_24_evaluator_panel_corpus.json`
        - `cultural_review`         → NONE
        FIVE ARE CALIBRATED AND ONE IS MERELY TESTED. The five ship an
        `*_evaluation.py` that scores the corpus and reports matched against
        total per dimension. `cultural_review` has six dedicated test files
        and a fixtures module, so it passes this item's "not generic" bar
        outright — but it has no corpus and no evaluation module, so nothing
        says how much of its own space those six suites reach. Held as a
        ceiling that may shrink and must never grow.
        THRESHOLDS AND OWNERS ARE NOT EXPRESSIBLE and the matrix says so
        rather than inventing them: the estate has no owner registry, the
        same absence recorded at M10.13.f, M10.14.e, M10.18.a and M10.20.a.

---

## Phase Y — Yemaya AAA production, study, and interactive-case authoring

Yemaya is an existing AAA production suite, not a greenfield V1 route. This
phase adds coherent study-material and interactive-case authoring to
`apps/yemaya/studio-web` and its desktop host while completing and governing the
existing project, script/storyboard, character/world, asset, production,
engine-integration, collaboration, rendering, agent, marketplace, canon, case,
and human-override systems. Consumer study experiences remain distinct from
authoring, but the existing AAA suite is in scope for explicit feature
reconciliation rather than being hidden behind the word "integration."

### Y0 — Boundary, host, inventory, and production decisions

- [ ] Y0.1 Inventory the Studio web/desktop route trees and classify live,
      fixture, documentation, duplicate, and missing surfaces. Evidence:
      route-to-capability matrix.
      <!-- partial: deterministic route inventory (a) done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/yemaya-route-inventory.json. b-f (runtime live/fixture
                                                                                                                                                                                                                                                                      classification, capability mapping, divergence report, dispositions, coverage
                                                                                                                                                                                                                                                                      gate) are runtime/design/human and stay unchecked. -->
  - [x] Y0.1.a Enumerate every web/Electron route, nested layout, navigation/
        deep-link entry, IPC/API call, feature flag, consumer, owner, and test.
        Evidence: deterministic route inventory.
        <!-- evidence: .routes — 39 routes (33 studio-web React-Router w/ RouteSuspense
                                                                                                                                                                                                                                                                        pageName + nested Layout, 6 studio-desktop Electron renderer), each with
                                                                                                                                                                                                                                                                        path/element/surface/dynamic/test. Per-route file/API resolves for desktop;
                                                                                                                                                                                                                                                                        web pages lazy-load via the ./lib/lazy barrel so the API surface is captured
                                                                                                                                                                                                                                                                        at app level (.appLevelFootprints: 8 web /api/* endpoints, 18 desktop IPC
                                                                                                                                                                                                                                                                        channels). generate-yemaya-route-inventory.mjs + 6 tests, wired into inventory/verify. -->
  - [ ] Y0.1.b Exercise each surface against backing services and label live,
        partial/degraded, fixture/demo, documentation/mock, unreachable, or
        missing with proof. Evidence: runtime classification.
  - [ ] Y0.1.c Map route operations/fields to atomic capabilities, audience/
        role, project type, owner service/package, data store, and desktop/web
        requirement. Evidence: route-capability matrix.
  - [ ] Y0.1.d Identify duplicate mutation/read facades, stale aliases,
        undocumented live routes, orphan APIs, and asserted capabilities with no
        route. Evidence: divergence report.
  - [ ] Y0.1.e Assign reuse/refactor/redirect/retire/net-new/exclude
        dispositions with owner, compatibility window, and test. Evidence:
        approved ledger.
  - [ ] Y0.1.f Add route/inventory drift checks and honest-state tests.
        Evidence: route coverage gate.
- [ ] Y0.2 Define the shared renderer boundary between Studio web and Electron
      desktop. Evidence: no business rule exists only in a renderer component.
  - [x] Y0.2.a Inventory renderer/shared/domain/service/main-process code,
        imports, state, validation, permissions, API/IPC handlers, and
        duplicated business rules. Evidence: dependency map.
        <!-- evidence: yemaya-desktop-partition.json — 62 studio-desktop files
                                                                                                                                                                                                                                                                        partitioned by process (main 36, renderer 22, preload 1, shared 2,
                                                                                                                                                                                                                                                                        score-editor 1); IPC surface = ~297 ipcMain handler channels ↔ ~102
                                                                                                                                                                                                                                                                        ipcRenderer call channels via 1 contextBridge namespace; renderer→main
                                                                                                                                                                                                                                                                        leakage = 0 (clean boundary — no renderer file imports electron or a
                                                                                                                                                                                                                                                                        main-process module directly). generate-yemaya-desktop-partition.mjs + 5
                                                                                                                                                                                                                                                                        tests. Duplicated-business-rules detection is the Y0.2.b/e semantic half. -->
  - [ ] Y0.2.b Define renderer-agnostic domain contracts/use cases/state
        machines/validation/authorization and shared view-model seams. Evidence:
        boundary interfaces.
  - [ ] Y0.2.c Define web transport and Electron preload IPC adapters with typed
        command/query/events, structured errors, cancellation, and versioning.
        Evidence: adapter contracts.
  - [ ] Y0.2.d Keep native filesystem/device/DCC/render capabilities behind
        main- process service ports; renderer receives no Node/Electron
        privilege. Evidence: security boundary tests.
  - [ ] Y0.2.e Move or disposition renderer-only validation/workflow/policy/
        canon/release rules into canonical domain owners. Evidence: rule ledger.
  - [ ] Y0.2.f Run identical domain contract/consumer tests through web and
        desktop adapters and fail forbidden imports. Evidence: parity gate.
- [ ] Y0.3 Define which native capabilities require desktop: local files, DCC
      bridge, large media, background render, color management, and device
      capture. Evidence: capability matrix and browser fallback.
  - [ ] Y0.3.a Define versioned native-capability registry with host/OS/device/
        permission/security/resource/offline requirements and audience.
        Evidence: capability schema.
  - [ ] Y0.3.b Specify local file/directory/project package operations, path/
        symlink/permission/sandbox rules, watchers, and web upload/download/
        file-system-access fallback. Evidence: file matrix.
  - [ ] Y0.3.c Specify DCC/engine bridge discovery/session/command/transfer/
        version/auth behavior and governed Bellona handoff; web fallback is
        inspect/deep-link/export only. Evidence: bridge matrix.
  - [ ] Y0.3.d Specify large media streaming/proxy/chunk/cache/memory/disk/
        hardware decode behavior and browser limits/degraded states. Evidence:
        media matrix.
  - [ ] Y0.3.e Specify background render/encode/job resume/notifications/power,
        color profile/display/OCIO, and capture device permission/calibration/
        consent behavior. Evidence: render/color/capture matrices.
  - [ ] Y0.3.f Define capability negotiation and honest supported/degraded/
        unavailable/install-desktop/open-desktop states with no data loss.
        Evidence: fallback tests.
- [ ] Y0.4 Define the authoring/consumer boundary for `study-workspace`.
      Evidence: authoring never writes consumer read models directly.
  - [ ] Y0.4.a Inventory authoring/consumer routes, packages, schemas, stores,
        caches/indexes, mutations, events, and current direct writes. Evidence:
        boundary inventory.
  - [ ] Y0.4.b Define canonical study project/content/revision/build/release
        mutation owner and separate consumer delivery/progress/session read
        owners. Evidence: ownership matrix.
  - [ ] Y0.4.c Define versioned compiled study bundle/publication event consumed
        to build delivery read models and no shared mutable content rows.
        Evidence: handoff contracts.
  - [ ] Y0.4.d Define author preview through the real consumer renderer using
        synthetic identity and nonoperational events. Evidence: preview
        boundary.
  - [ ] Y0.4.e Remove/prohibit direct authoring writes to consumer database/
        index/cache/progress and direct consumer edits to authoring revisions.
        Evidence: dependency/schema guards.
  - [ ] Y0.4.f Test publication/supersession/recall/read-back, two-tenant and
        failure/replay behavior. Evidence: boundary integration suite.
- [ ] Y0.5 Define the study and interactive-case content tracks plus their
      shared project, source, asset, review, and release spine. Evidence: domain
      context diagram.
  - [ ] Y0.5.a Define track-specific goals, authors/audiences, content entities,
        authoring journeys, validation, outputs, consumers, and explicit
        exclusions. Evidence: track charters.
  - [ ] Y0.5.b Define shared project/workspace/member/role/revision/maturity/
        schedule/budget/audit contracts without collapsing track semantics.
        Evidence: shared project map.
  - [ ] Y0.5.c Define shared governed source/claim/citation/rights and asset/
        variant/provenance/continuity/canon relationships. Evidence:
        source/asset spine.
  - [ ] Y0.5.d Define shared comment/review/gate/decision/approval/exception/
        localization workflows with track-specific templates. Evidence: review
        spine.
  - [ ] Y0.5.e Define track-specific compiler/bundle/target/publication/catalog/
        consumer handoffs atop shared release candidate/execute/verify/recall.
        Evidence: release spine.
  - [ ] Y0.5.f Draw ownership, context, trust, data, event, and cross-domain
        boundaries plus valid reuse/forbidden coupling. Evidence: ratified
        context diagram.
- [ ] Y0.6 Map all `libs/yemaya/*` packages to the two tracks, shared production
      systems, other Yemaya products, or out-of-scope. Evidence: complete
      package ledger.
  - [x] Y0.6.a Enumerate every package/export/source/test/dependency/consumer/
        build target/owner and detect nested/untracked/dead packages. Evidence:
        <!-- evidence: .domains.yemaya.packages — 59 libs/yemaya/* packages enumerated from the BASE.3 graph (exports/inbound/outbound/disposition) + BASE.1 tests; dead=retire-candidate disposition. evidence/v1-workbenches/domain-package-inventories.json (254 pkgs; per-package exports/consumers/deps/disposition/tests + capability catalog with exact source locations). generate-domain-package-inventories.mjs + 6 tests. -->
        package inventory.
  - [x] Y0.6.b Extract public entities/operations/events/jobs/adapters/UI/
        algorithms and link exact source locations. Evidence: capability
        <!-- evidence: .domains.yemaya.packages[].capabilityCatalog — atomic capabilities (entity/operation/event/job/adapter) extracted per package, each linked to its exact sourceFile + sourceRef (891 caps). UI is captured by the component inventories; "algorithms" is not a discrete capability kind. -->
        catalog.
  - [ ] Y0.6.c Map each capability to study track, interactive-case track,
        shared production, AAA/organization/marketplace/developer/consumer
        product, or explicit out-of-scope. Evidence: classification ledger.
  - [ ] Y0.6.d Assign canonical owner, reuse/refactor/move/merge/retire/exclude/
        net-new disposition and identify duplicate/shadow shapes. Evidence:
        disposition review.
  - [ ] Y0.6.e Map every retained package feature bidirectionally to route/API/
        contract/ledger/evidence and add zero-orphan drift gate. Evidence:
        complete package ledger.
- [ ] Y0.7 Reconcile case contracts, compiler, director, engine, verifier, eval,
      canon, localization, and pipeline ownership. Evidence: dependency
      direction ADR.
  - [x] Y0.7.a Inventory contracts/packages/services/stores/jobs/CLIs/routes/
        events/tests for each named subsystem and current dependency graph.
        Evidence: subsystem inventory.
        <!-- evidence: yemaya-case-inventory.json — the 11 interactive-case pipeline
                                                                                                                                                                                                                                                                        subsystems (case-assets/compiler/contracts/director/engine/eval/localization/
                                                                                                                                                                                                                                                                        pipeline/suspects/verifier/writers-room; 585 public symbols, 111 code files,
                                                                                                                                                                                                                                                                        37 tests) each with exports/inbound/outbound/disposition, plus the current
                                                                                                                                                                                                                                                                        intra-pipeline dependency graph (21 case-*→case-* edges, ACYCLIC —
                                                                                                                                                                                                                                                                        case-contracts is the foundation everyone depends on). The dependency-direction
                                                                                                                                                                                                                                                                        ADR is Y0.7's decision. generate-yemaya-case-inventory.mjs + 3 tests. -->
  - [ ] Y0.7.b Define canonical aggregates/operations/outputs and mutation/read
        ownership for case authoring, compilation, direction/runtime,
        verification evaluation, canon, localization, and pipeline. Evidence:
        ownership map.
  - [ ] Y0.7.c Define permitted dependency direction, public ports/contracts/
        events, versioning, idempotency, failure, and no private-store imports.
        Evidence: target graph.
  - [ ] Y0.7.d Reconcile duplicate case/canon/localization/evaluation shapes and
        migrate consumers with compatibility/retirement plan. Evidence: contract
        disposition.
  - [ ] Y0.7.e Define compiler output as immutable input to engine/director and
        verifier/eval evidence as nonauthoritative inputs to review. Evidence:
        authority invariants.
  - [ ] Y0.7.f Ratify ADR with cycle/forbidden-import/drift tests and rejected
        alternatives. Evidence: approved dependency-direction ADR.
- [ ] Y0.8 Reconcile project, asset-library, dailies, collaboration, rendering,
      orchestration, budget, safety, and human-override systems. Evidence: no
      planned shadow implementation.
  - [ ] Y0.8.a Inventory existing
        services/packages/routes/stores/jobs/UIs/events/ deployments for each
        production system with live/partial/fixture status. Evidence: system
        inventory.
  - [ ] Y0.8.b Map study/case requirements to exact reusable project/asset/
        dailies/collaboration/render/orchestrator/budget/safety/override
        operations and contracts. Evidence: reuse matrix.
  - [ ] Y0.8.c Identify gaps, duplicate proposed stores/queues/workflows/
        dashboards, conflicting state vocabularies, and authority overlap.
        Evidence: gap/duplication report.
  - [ ] Y0.8.d Assign reuse/facade/extend/refactor/migrate/retire/net-new
        disposition with canonical owner and dependencies. Evidence: disposition
        ledger.
  - [ ] Y0.8.e Define governed cross-system identity/version/tenant/project/
        lineage/audit/transaction/event/recovery handoffs. Evidence: seam
        contracts.
  - [ ] Y0.8.f Add architecture/dependency/schema inventory guards proving no
        shadow canonical implementation. Evidence: ownership gate.
- [ ] Y0.9 Define author, researcher, writer, designer, editor, canon reviewer,
      solver, localization reviewer, producer, publisher, and administrator
      roles. Evidence: permission matrix.
  - [ ] Y0.9.a Define each role’s purpose/qualifications, track/project/
        organization scope, eligible actor, delegation, lifecycle, and
        conflicts. Evidence: role definitions.
  - [ ] Y0.9.b Map project/source/story/case/canon/solution/asset/script/
        production/localization/review/release/admin operations per role.
        Evidence: operation matrix.
  - [ ] Y0.9.c Map unpublished story/protected source/spoiler/solution/
        participant/biometric/rights/budget/security fields and actions at
        property level. Evidence: field matrix.
  - [ ] Y0.9.d Define author/reviewer/solver/canon/publisher separation, quorum,
        conflict, substitute, embargo, blind-review, and break-glass rules.
        Evidence: duty policy.
  - [ ] Y0.9.e Define administrator bounds for membership/configuration versus
        content/canon/solution/approval/publication authority. Evidence: admin
        boundary.
  - [ ] Y0.9.f Test route/object/property/function, combined roles, role change,
        direct IDs, embargo/spoiler, and two tenants. Evidence: permission
        suite.
- [ ] Y0.10 Define maturity models for study projects and cases separately.
      Evidence: state vocabularies do not overload project production status.
  - [x] Y0.10.a Inventory current study/case/content/review/production/render/
        release/consumer states, labels, enums, owners, and collisions.
        Evidence: vocabulary inventory.
        <!-- yemaya-state-vocabulary.json: derived from the generated Prisma enums
                                                                                                                                                                                                                                                                        (libs/yemaya/database/src/generated/client/enums.ts). 21 typed status/type
                                                                                                                                                                                                                                                                        enums declaring 115 labels (89 distinct), each with owner = the generated
                                                                                                                                                                                                                                                                        Prisma client. COLLISION detection (the Y0.10.a "collisions" dimension): 21
                                                                                                                                                                                                                                                                        label values appear in >1 enum with unrelated semantics — ACTIVE×3
                                                                                                                                                                                                                                                                        (ProjectStatus/SubscriptionStatus/UserStatus), DRAFT×3 (AssetStatus/
                                                                                                                                                                                                                                                                        InvoiceStatus/ProjectStatus), ADMIN×3, FREE×3, OTHER×3 — a real vocabulary
                                                                                                                                                                                                                                                                        hazard surfaced. Lifecycle-stage coverage (enum→stage by name keyword): typed
                                                                                                                                                                                                                                                                        vocabulary EXISTS for content/production/release/consumer; NO typed state
                                                                                                                                                                                                                                                                        vocabulary for study/case/review/render (verified twice across the whole
                                                                                                                                                                                                                                                                        Yemaya surface — an honest architectural gap, reported not hidden). The
                                                                                                                                                                                                                                                                        study-content state MACHINE (states/transitions/prerequisites, Y0.10.b) and
                                                                                                                                                                                                                                                                        later Y0.10.* DEFINE new structure and stay unchecked.
                                                                                                                                                                                                                                                                        generate-yemaya-state-vocabulary.mjs + 6 tests, wired into inventory/verify + CI. -->
  - [ ] Y0.10.b Define study content maturity states/transitions/prerequisites/
        roles/effects independently from project schedule/asset production.
        Evidence: study state machine.
  - [ ] Y0.10.c Define interactive-case concept/writing/canon/solvability/
        production/review/release maturity independently from runtime play
        state. Evidence: case state machine.
  - [ ] Y0.10.d Define shared project production status and relation/projection
        to each track’s maturity without one mutating the other. Evidence:
        mapping.
  - [ ] Y0.10.e Define transition concurrency, revisions, review invalidation,
        supersession/archive/quarantine/restore, audit, and server enforcement.
        Evidence: transition matrix tests.
  - [ ] Y0.10.f Detect API/UI/analytics/export vocabulary collisions and prove
        no overloaded enum/default mapping. Evidence: collision review.
- [ ] Y0.11 Threat-model source injection, malicious project files, spoiler
      leakage, canon corruption, unfair or harmful case content, unsafe media,
      agent runaway, desktop IPC, local filesystem, and supply chain. Evidence:
      mitigations map to tests.
  - [ ] Y0.11.a Map actors/assets/trust boundaries/data flows/entry points/
        providers/tools/hosts/integrations/high-impact actions. Evidence: threat
        model context.
  - [ ] Y0.11.b Model prompt/source/retrieval/import injection and poisoned/
        retracted/false/protected sources across authoring, agents, compilers,
        critics, and generated media. Evidence: source threats.
  - [ ] Y0.11.c Model spoilers/solutions/unpublished story/canon/embargo leakage
        via UI/API/search/export/logs/caches/collaboration/local files/consumer
        builds. Evidence: confidentiality threats.
  - [ ] Y0.11.d Model canon tamper/conflicts, unfair/unsolvable/manipulative/
        harmful case content, unsafe media/identity/voice/minor/rights use, and
        false automated approval. Evidence: content threats.
  - [ ] Y0.11.e Model agent budget/tool/loop/runaway/injection/approval
        confusion, hidden side effects, and cross-project/tenant data access.
        Evidence: agent threats.
  - [ ] Y0.11.f Model Electron IPC/preload/navigation/deep-link/update/plugin/
        local filesystem/path/symlink/credential/device/DCC/renderer compromise.
        Evidence: desktop threats.
  - [ ] Y0.11.g Model package/model/node/renderer/DCC/engine/marketplace/
        dependency/artifact/build/distribution supply-chain tamper. Evidence:
        supply-chain threats.
  - [ ] Y0.11.h Assign prevention/detection/response/recovery, owner, residual
        risk, test, monitoring, incident runbook, and launch gate per threat.
        Evidence: mitigation traceability map.
- [ ] Y0.12 Define data classification for unpublished story material, protected
      sources, case solutions, participant data, assets, and local caches.
      Evidence: privacy/security decision.
  - [x] Y0.12.a Inventory
        entities/fields/files/blobs/prompts/logs/events/search/
        exports/backups/local caches for each named data category. Evidence:
        data inventory.
        <!-- yemaya-data-inventory.json: derived from the generated Prisma client
                                                                                                                                                                                                                                                                        (libs/yemaya/database). 38 named entities / 406 scalar fields. Of Y0.12.a's
                                                                                                                                                                                                                                                                        11 named data forms, 6 are entity-modeled (entities=38 models; fields=406;
                                                                                                                                                                                                                                                                        files/blobs=Asset storage columns; logs=AuditLog/Activity/UsageRecord/
                                                                                                                                                                                                                                                                        AssetUsage; events=Activity/Notification/Webhook/WebhookDelivery) and 5 are
                                                                                                                                                                                                                                                                        recorded HONESTLY as infrastructure not modeled as Prisma entities (prompts=
                                                                                                                                                                                                                                                                        request/generation-shaped, search=index, exports=export jobs, backups=DR,
                                                                                                                                                                                                                                                                        local-caches=client/edge cache) — not fabricated to look complete. Each
                                                                                                                                                                                                                                                                        entity carries its field count. generate-yemaya-data-inventory.mjs + 5 tests,
                                                                                                                                                                                                                                                                        wired into inventory/verify + CI. Classification labels + per-category owner/
                                                                                                                                                                                                                                                                        purpose/access/isolation/encryption/residency (Y0.12.b) and later Y0.12.*
                                                                                                                                                                                                                                                                        are design/human governance and stay unchecked. -->
  - [ ] Y0.12.b Define classification labels and per-category owner, purpose,
        access roles, tenant/project isolation, encryption/key, residency,
        retention/deletion/legal hold, export, and audit. Evidence: policy map.
  - [ ] Y0.12.c Define spoiler/embargo/need-to-know compartments and protected
        source/solution field-level projections with nonenumerable errors.
        Evidence: confidentiality matrix.
  - [ ] Y0.12.d Define participant/performer/voice/likeness/biometric/consent/
        minor and review-comment/telemetry privacy handling. Evidence:
        participant policy.
  - [ ] Y0.12.e Define asset derivative/proxy/watermark/provenance and desktop
        local cache encryption/OS storage/offline/eviction/logout/device-loss
        behavior. Evidence: asset/cache policy.
  - [ ] Y0.12.f Threat-test UI/API/search/logs/notifications/exports/IPC/local
        files/backups/support/analytics and obtain privacy/security decision.
        Evidence: classification tests and approval.
- [ ] Y0.13 Define web/desktop parity, version-skew, update, offline, and
      project-lock policies. Evidence: compatibility ADR.
  - [ ] Y0.13.a Define versioned web/desktop/shared-contract/native-capability/
        server compatibility matrix and support windows. Evidence: matrix.
  - [ ] Y0.13.b Classify required parity versus desktop-only/web-fallback/
        unsupported behaviors, UI states, data semantics, and test proof.
        Evidence: parity policy.
  - [ ] Y0.13.c Define startup/runtime version negotiation, min/max/expiring/
        unsupported behavior, protocol/schema migration, and no silent skew.
        Evidence: skew tests.
  - [ ] Y0.13.d Define signed update discovery/download/staging/migration/
        restart/rollback/forced-security-update and interrupted update behavior.
        Evidence: update policy.
  - [ ] Y0.13.e Define offline cache/project availability, read/write/queued
        commands, conflict/reconnect/revalidation, secret expiry, and honest
        state. Evidence: offline policy.
  - [ ] Y0.13.f Define project/revision/entity/asset/render locks, lease/
        heartbeat/fencing/expiry/takeover/read-only, desktop crash, and split-
        brain behavior. Evidence: lock policy.
  - [ ] Y0.13.g Ratify compatibility ADR and matrix-generated web/desktop/skew/
        update/offline/lock tests. Evidence: approved ADR.
- [ ] Y0.14 Define success measures for authoring throughput, solvability, canon
      quality, source grounding, collaboration, render reliability, and release
      correctness. Evidence: metric specification.
  - [ ] Y0.14.a Define authoring flow measures by track/stage with denominator,
        wait/rework/block/abandonment and quality/safety countermetrics.
        Evidence: throughput metrics.
  - [ ] Y0.14.b Define solvability/fairness measures for clues/evidence/paths/
        ambiguity/hints/solver outcomes with calibrated sample/uncertainty and
        no “difficulty equals quality” incentive. Evidence: solvability metrics.
  - [ ] Y0.14.c Define canon/source measures for conflicts/drift/coverage/
        authority/currency/citation/faithfulness/corrections and review burden.
        Evidence: trust metrics.
  - [ ] Y0.14.d Define collaboration measures for review cycle/response/
        conflicts/merge/rework/participation with privacy and no worker
        surveillance incentive. Evidence: collaboration metrics.
  - [ ] Y0.14.e Define render/job reliability/SLO/resource/cost/quality and
        release planned-written-verified/partial/rollback/recall metrics.
        Evidence: production metrics.
  - [ ] Y0.14.f Specify source/formula/unit/window/dimensions/privacy/freshness/
        owner/target/guardrail/limitations and ratify against harmful shortcuts.
        Evidence: approved metric catalog.
- [ ] Y0.15 Author and ratify
      `V1/YEMAYA_AUTHORING_WORKBENCH_DESIGN_2026-07-23.md` with current-state,
      web/desktop boundary, study/case tracks, data, APIs, agents, production,
      trust, migration, rollout, and decision records. Evidence: approved doc
      and resolved review comments.
  - [ ] Y0.15.a Establish scope/terminology/requirements traceability/source
        anchors/owners/reviewers/decision log/evidence format. Evidence:
        outline.
  - [ ] Y0.15.b Document current routes/apps/packages/contracts/stores/jobs/
        agents/CLIs/integrations/deployments and live/partial/fixture/proposed
        truth. Evidence: current-state review.
  - [ ] Y0.15.c Document web/desktop/renderer/native boundaries,
        audiences/roles/ product tracks/project profiles,
        data/contracts/APIs/events/jobs, and cross-domain ownership. Evidence:
        architecture sections.
  - [ ] Y0.15.d Document study/case authoring/canon/sources/assets/production/
        agents/compiler/runtime/UI/responsive/accessibility/localization/offline
        journeys. Evidence: workflow/experience sections.
  - [ ] Y0.15.e Document trust/safety/rights/privacy/security/supply chain,
        verification/evaluation/observability/operations/DR. Evidence: trust
        sections.
  - [ ] Y0.15.f Document migration/compatibility/rollout/flags/deprecation/
        rollback/readiness and explicit ADRs/options. Evidence: rollout
        sections.
  - [ ] Y0.15.g Reconcile code/inventory/deep dives, resolve every comment, and
        obtain named domain/security/privacy/accessibility/engineering/product
        ratification. Evidence: approved design.
- [ ] Y0.16 Reconcile the domain feature inventory, all web/desktop pages, 44+
      API route groups, every `libs/yemaya/*` package, worker, CLI, walkthrough,
      and external integration at atomic-feature level. Evidence: the inventory
      explicitly covers projects, assets, scripts/storyboards, characters/
      worlds, production management, collaboration, autonomous pipelines,
      engines/DCCs, gameplay/NPCs, virtual production/XR, marketplaces/search,
      organizations/enterprise, community, study, and cases.
  - [x] Y0.16.a Build deterministic atomic inventories from feature docs, every
        page/route/operation/schema/package/worker/job/CLI/walkthrough/
        integration/test with source anchors. Evidence: source inventories.
        <!-- yemaya-code-census.json: 12 artifact classes, each carrying a SOURCE
                                                                                                                                                                                                                                                                        ANCHOR to its committed evidence. 77 documents; 79 studio pages; 39 app
                                                                                                                                                                                                                                                                        routes (anchor yemaya-route-inventory.json, studio-web 33/desktop 6); 38
                                                                                                                                                                                                                                                                        entities/406 fields (yemaya-data-inventory.json); 21 state enums/115 labels
                                                                                                                                                                                                                                                                        (yemaya-state-vocabulary.json); 27,337 package exports across 59 packages,
                                                                                                                                                                                                                                                                        891 capabilities (domain-package-inventories.json); 67 workers; 1 CLI; 4
                                                                                                                                                                                                                                                                        integration modules (path-scoped, NOT the ~395 content-grep over-count); 11
                                                                                                                                                                                                                                                                        case subsystems (yemaya-case-inventory.json); 62 desktop files (yemaya-
                                                                                                                                                                                                                                                                        desktop-partition.json); 1,225 tests. Consolidated from fresh git counts +
                                                                                                                                                                                                                                                                        committed Y0.1/Y0.6/Y0.7/Y0.10/Y0.12 evidence. generate-yemaya-code-census.mjs
                                                                                                                                                                                                                                                                        + 7 tests, wired into inventory/verify + CI. Alias normalization via reviewed
                                                                                                                                                                                                                                                                        mappings (Y0.16.b) and dispositions (Y0.16.c+) are human-review governance
                                                                                                                                                                                                                                                                        and stay unchecked. -->
  - [ ] Y0.16.b Normalize aliases through reviewed mappings while preserving
        source identity/version/semantics and live/fixture/asserted state.
        Evidence: alias map.
  - [ ] Y0.16.c Map projects/assets/scripts/storyboards/characters/worlds/
        production/collaboration/dailies/rendering and autonomous pipelines.
        Evidence: core production rows.
  - [ ] Y0.16.d Map engines/DCCs/gameplay/NPCs/virtual production/XR/mocap/
        builds/runtime/device operations with Bellona/Aja ownership boundaries.
        Evidence: specialized production rows.
  - [ ] Y0.16.e Map marketplace/search/community/organizations/enterprise/admin/
        developer APIs/SDK/webhooks/plugins and security/monetization authority.
        Evidence: platform rows.
  - [ ] Y0.16.f Map study/case/source/canon/compiler/solver/localization/
        publication/consumer delivery features. Evidence: track rows.
  - [ ] Y0.16.g Assign reuse/refactor/move/merge/retire/net-new/defer/reject,
        canonical owner, task, dependencies, and evidence per atomic feature.
        Evidence: disposition ledger.
  - [ ] Y0.16.h Review collisions/orphans/unsupported assertions and add a
        bidirectional zero-orphan drift gate. Evidence: approved reconciliation.
- [ ] Y0.17 Define the product boundary among AAA creation, production
      operations, organization administration, marketplace/community, developer
      tooling, study/case authoring, and consumer delivery. Evidence: each
      capability has an authorized audience/host and governed handoff or
      explicit exclusion.
  - [ ] Y0.17.a Define purpose/actors/hosts/routes/owner services/data/actions
        for AAA creation, production ops, org admin, marketplace/community,
        developer, study/case, and consumer delivery. Evidence: surface matrix.
  - [ ] Y0.17.b Assign every atomic capability to exactly one mutation owner and
        authorized audiences, with any read-only facade/deep link. Evidence:
        ownership ledger.
  - [ ] Y0.17.c Define governed signed/versioned project/asset/run/build/review/
        release/entitlement handoffs between products and external domains.
        Evidence: handoff contracts.
  - [ ] Y0.17.d Keep organization/billing/security, marketplace/payout, operator
        fleet/incident, and consumer progress/session authority out of authoring
        convenience routes. Evidence: boundary tests.
  - [ ] Y0.17.e Define excluded/deferred/unsupported cells and safe UI/API/
        capability behavior without hidden routes or generic fallback. Evidence:
        exclusion matrix.
  - [ ] Y0.17.f Test direct URL/API/property/function, role changes, deep links,
        search/export/logs/cache, and tenant/product boundary leakage. Evidence:
        adversarial suite.
- [ ] Y0.18 Define supported project types and production profiles for film,
      episodic, animation, game, virtual production, XR, study, interactive
      case, podcast/AV, and other ratified types. Evidence: every profile maps
      required entities, pipeline stages, roles, gates, assets, exports, and
      target hosts without inheriting unsupported defaults.
  - [ ] Y0.18.a Define versioned project-type/profile registry with owners,
        audience, lifecycle, inheritance, target platforms, and code floors.
        Evidence: registry schema.
  - [ ] Y0.18.b Define film/episodic/animation profiles for script/scene/shot/
        sequence/editorial/VFX/audio/color/delivery stages, roles, assets,
        gates, schedules, and targets. Evidence: screen profile rows.
  - [ ] Y0.18.c Define game profiles for world/level/character/gameplay/NPC/UI/
        audio/cinematics/build/test/performance/engine/platform stages and
        assets. Evidence: game rows.
  - [ ] Y0.18.d Define virtual-production/XR profiles for stage/device/tracking/
        camera/render/mocap/engine/session/safety/calibration/build/deployment.
        Evidence: VP/XR rows.
  - [ ] Y0.18.e Define study/interactive-case profiles for sources/content/
        canon/clues/solver/compiler/localization/accessibility/review/bundle/
        consumer target. Evidence: track rows.
  - [ ] Y0.18.f Define podcast/AV and other ratified profiles for script/record/
        edit/mix/video/captions/distribution/rights/loudness/target
        requirements. Evidence: AV rows.
  - [ ] Y0.18.g Map exact required/optional/unavailable entities/pipeline
        stages/ roles/gates/assets/exports/host capabilities/dependencies per
        profile. Evidence: profile matrix.
  - [ ] Y0.18.h Fail missing/unsupported cells instead of inheriting defaults
        and test profile resolution/overlays/migrations. Evidence: no-default
        gate.

### Y1 — Project, content, revision, and source contracts

- [ ] Y1.1 Define a canonical authoring project with track, workspace, tenant,
      ownership, locale, status, production links, and retention policy.
      Evidence: contract invariants.
  - [ ] Y1.1.a Define stable project identity/revision, track/project type/
        production profile, title/description, tenant/organization/workspace,
        and lifecycle. Evidence: base schema.
  - [ ] Y1.1.b Define owner/member/team/role assignments, visibility/
        compartment/embargo, created/imported/forked provenance, and audit.
        Evidence: ownership tests.
  - [ ] Y1.1.c Define source/default/supported locale, fallback, translation
        policy, time zone, units/color profile, and accessibility targets.
        Evidence: locale/config tests.
  - [ ] Y1.1.d Define authoring maturity separately from project production/
        release/consumer state and valid transitions/guards. Evidence: status
        invariants.
  - [ ] Y1.1.e Define links to assets/production schedule/budget/runs/builds/
        targets/external projects by exact version/owner contract. Evidence:
        production link tests.
  - [ ] Y1.1.f Define classification/retention/archive/delete/legal hold/export/
        local-cache policy and tenant-scoped key/index invariants. Evidence:
        lifecycle tests.
- [ ] Y1.2 Define study project, collection, module, note, source node, anchor,
      concept, reading order, activity, and published artifact entities.
      Evidence: schema fixtures.
  - [ ] Y1.2.a Define study-project specialization and versioned collection/
        membership/order/source-scope entities. Evidence: project/collection
        fixtures.
  - [ ] Y1.2.b Define module
        hierarchy/order/objectives/summary/content/activity/
        prerequisite/publication state with cycle/depth rules. Evidence: module
        fixtures.
  - [ ] Y1.2.c Define note/block/annotation/tag/author/source/citation/locale/
        visibility/revision and protected-source fields. Evidence: note
        fixtures.
  - [ ] Y1.2.d Define source node/version/kind/authority/rights/checksum/
        extraction plus typed anchor and note/canon links. Evidence: source
        fixtures.
  - [ ] Y1.2.e Define concept identity/version/definition/aliases/relationships/
        evidence and a separate reading-order/prerequisite graph. Evidence:
        concept/order fixtures.
  - [ ] Y1.2.f Define activities with
        type/instructions/inputs/outputs/resources/ time/accessibility/consumer
        behavior and no progress authority. Evidence: activity fixtures.
  - [ ] Y1.2.g Define published study artifact/build/manifest/locale/target/
        constituent revisions/hashes/provenance/release state. Evidence: full
        schema goldens.
- [ ] Y1.3 Define case project, premise, mystery model, suspect, relationship,
      motive, means, opportunity, clue, red herring, scene, timeline event,
      location, reveal, solution, and ending entities. Evidence: complete case
      fixture.
  - [ ] Y1.3.a Define case-project specialization, premise/theme/tone/setting/
        audience/format/duration/difficulty/safety/locale and exact source/canon
        scope. Evidence: case/premise schema.
  - [ ] Y1.3.b Define mystery model/question/crime-event/truth state/solution
        constraints/allowed interpretations and solver-visible separation.
        Evidence: mystery schema.
  - [ ] Y1.3.c Define character/suspect identity, public/private knowledge,
        background/traits/goals/arc/alibi plus motive/means/opportunity as typed
        evidence-backed dimensions. Evidence: suspect fixtures.
  - [ ] Y1.3.d Define directed typed relationship revisions with source/canon,
        temporal interval, visibility/spoiler, confidence, and contradictions.
        Evidence: relationship fixtures.
  - [ ] Y1.3.e Define clue/red-herring identity/kind/content/source/truth link/
        discovery prerequisites/placement/salience/interpretations/fairness/
        payoff and player-visible state. Evidence: clue fixtures.
  - [ ] Y1.3.f Define scene/location/timeline event/order/branch/participants/
        actions/dialogue/clue transitions/asset/production/continuity links.
        Evidence: scene/timeline fixtures.
  - [ ] Y1.3.g Define reveal sequence, solution reasoning graph/evidence/
        alternatives, ending conditions/outcomes/epilogue and embargoed
        authority. Evidence: solution/ending fixtures.
  - [ ] Y1.3.h Publish minimal/full/branching/multisuspect/multilocale/
        intentionally ambiguous/invalid complete-case goldens. Evidence: fixture
        suite.
- [ ] Y1.4 Define canon assertions with namespace, subject, predicate, object,
      temporal scope, source, confidence, status, and contradiction links.
      Evidence: canon contract tests.
  - [ ] Y1.4.a Define assertion logical/revision identity, tenant/project/
        franchise namespace, subject/predicate/object typed refs/literals,
        author, and lifecycle. Evidence: base schema.
  - [ ] Y1.4.b Define temporal instant/interval/sequence/era/unknown scope,
        effective story-world versus publication chronology, and uncertainty.
        Evidence: temporal tests.
  - [ ] Y1.4.c Define exact source/citation/creator decision/production artifact
        evidence, authority, confidence basis, rights, and freshness. Evidence:
        provenance tests.
  - [ ] Y1.4.d Define proposed/under-review/canonical/conditional/disputed/
        deprecated/retconned/invalid status and role/guard transitions.
        Evidence: state tests.
  - [ ] Y1.4.e Define supports/contradicts/refines/supersedes/scoped-exception/
        alternate-continuity links with exact revisions and no auto-resolution.
        Evidence: relation tests.
  - [ ] Y1.4.f Validate namespace/entity/predicate vocabulary/units/cardinality/
        temporal logic/cycles/conflicts and preserve unknown/N/A. Evidence:
        contract suite.
- [ ] Y1.5 Define typed stable anchors for source pages, study notes, case
      scenes, dialogue, clues, timeline events, assets, frames, and timecodes.
      Evidence: anchor round-trip tests.
  - [ ] Y1.5.a Define common anchor identity, exact subject/revision/kind,
        locator payload, context/hash/fingerprint, author, and resolution
        status. Evidence: anchor schema.
  - [ ] Y1.5.b Define page/region/section/paragraph/character source and note
        block/text-range anchors with extraction/revision context. Evidence:
        text/source fixtures.
  - [ ] Y1.5.c Define case scene/entity, dialogue speaker/line/range, clue/
        placement, timeline event/interval, and graph entity anchors by stable
        IDs. Evidence: case fixtures.
  - [ ] Y1.5.d Define asset/object/component/region, image/video frame/region,
        audio/video timecode/sample/range, shot/sequence anchors with units/
        timebase. Evidence: media fixtures.
  - [ ] Y1.5.e Validate kind/subject/bounds/permissions and round-trip through
        API/database/export/localization/build without screen-coordinate-only
        identity. Evidence: round-trip tests.
  - [ ] Y1.5.f Map across revisions as exact/shifted/ambiguous/orphaned/invalid
        with original preserved and human reattachment. Evidence: survival
        tests.
- [ ] Y1.6 Define immutable revisions and semantic diffs for text, tree, graph,
      timeline, and structured case data. Evidence: golden diff fixtures.
  - [ ] Y1.6.a Define logical versus revision ID, parent/base/branch, schema
        version, author/reason/time, payload/manifest hash, state, and
        immutability. Evidence: revision schema.
  - [ ] Y1.6.b Define text/block semantic diff for normalized content/format/
        source/citation/locale/accessibility without line-only noise. Evidence:
        text goldens.
  - [ ] Y1.6.c Define tree diff for stable node add/remove/edit/move/reorder/
        reparent and descendant impact. Evidence: tree goldens.
  - [ ] Y1.6.d Define graph diff for node/edge/type/property/add/remove/change/
        merge/split and cycle/reachability/canon impact. Evidence: graph
        goldens.
  - [ ] Y1.6.e Define timeline diff for event/duration/interval/order/branch/
        participant/location/causality/continuity changes. Evidence: timeline
        goldens.
  - [ ] Y1.6.f Define structured case diff for premise/suspect/motive/means/
        opportunity/clue/red-herring/reveal/solution/ending with spoiler
        redaction. Evidence: case goldens.
  - [ ] Y1.6.g Represent added/removed/changed/moved/unchanged/redacted/unknown
        and pin schema/comparator versions. Evidence: diff contract tests.
- [ ] Y1.7 Define branch, merge, conflict, and restore rules. Evidence:
      concurrent authoring known-value tests.
  - [ ] Y1.7.a Define branch identity/base/head/purpose/owner/scope/visibility/
        lock/lifecycle and branchable entity granularity. Evidence: branch
        schema.
  - [ ] Y1.7.b Define three-way merge by stable IDs for scalar/text/list/tree/
        graph/timeline/structured data with exact automatic-safe rules.
        Evidence: merge fixtures.
  - [ ] Y1.7.c Define content/order/delete-edit/reparent/graph/canon/solution/
        localization/asset conflicts and mine/theirs/base projections. Evidence:
        conflict fixtures.
  - [ ] Y1.7.d Require human resolution for semantic/canon/spoiler/solution/
        source/rights conflicts; preserve both inputs and resolution rationale.
        Evidence: resolution tests.
  - [ ] Y1.7.e Create merged/restored state as a new immutable revision with all
        parents/provenance; never overwrite branch history. Evidence: lineage
        tests.
  - [ ] Y1.7.f Test simultaneous edits/offline/reconnect/locks/rebase/merge/
        abandon/restore and downstream gate invalidation. Evidence: concurrency
        suite.
- [ ] Y1.8 Define decision records for outlines, case solutions, clue plans,
      canon resolutions, and production choices. Evidence: schema and UI
      projection.
  - [ ] Y1.8.a Define immutable decision identity/type, actor/authority, exact
        candidate revisions, chosen/rejected/deferred/superseded outcome,
        rationale/evidence/conditions/time. Evidence: base schema.
  - [ ] Y1.8.b Define outline selection/merge/scope/schedule decision fields and
        source/constraint/coverage comparisons. Evidence: outline fixtures.
  - [ ] Y1.8.c Define solution and clue-plan decisions with
        solver/fairness/canon/ spoiler evidence, alternatives,
        reveal/order/placement consequences. Evidence: case decision fixtures.
  - [ ] Y1.8.d Define canon resolution with conflicting assertions/sources/
        continuity scope, accept/reject/retcon/alternate outcome, and approver.
        Evidence: canon fixtures.
  - [ ] Y1.8.e Define production choice with asset/shot/tool/provider/format/
        cost/schedule/quality/rights alternatives and impact. Evidence:
        production fixtures.
  - [ ] Y1.8.f Define role-redacted UI/API projection and supersession without
        deleting prior decisions or exposing spoilers. Evidence: projection
        tests.
- [ ] Y1.9 Define source authority, rights, locale, checksum, extraction, and
      citation metadata. Evidence: source-kind validation.
  - [ ] Y1.9.a Define source identity/version/kind/title/creator/publisher/
        external IDs/locators/edition/status and provenance. Evidence: source
        schema.
  - [ ] Y1.9.b Define authority/role/expertise/official/primary-secondary/
        reliability/review evidence, jurisdiction, currency, and conflicts.
        Evidence: authority tests.
  - [ ] Y1.9.c Define ownership/license/access/use/adapt/train/quote/distribute/
        attribution/territory/term/embargo/protected-source evidence. Evidence:
        rights tests.
  - [ ] Y1.9.d Define language/script/region/translation/source locale,
        audience/ sensitivity/spoiler classification, and locale variants.
        Evidence: locale tests.
  - [ ] Y1.9.e Define original bytes/content hash/length/type/storage,
        retrieval/ connector receipt, scans, and extraction
        run/tool/config/output/anchors. Evidence: integrity/extraction tests.
  - [ ] Y1.9.f Define citation target/source revision/typed
        locator/quote/context/ role/verification and validate per source kind.
        Evidence: source-kind validation.
- [ ] Y1.10 Define release bundles for study content and compiled cases.
      Evidence: schema/version profiles and compatibility policy.
  - [ ] Y1.10.a Define common bundle identity/version/track/project/release
        candidate/manifest/entry points/files/dependencies/hashes/provenance/
        rights/locale/target/profile. Evidence: base schema.
  - [ ] Y1.10.b Define study bundle hierarchy/content/notes/concepts/reading
        order/activities/media/sources/citations/accessibility/search/navigation
        and consumer configuration. Evidence: study goldens.
  - [ ] Y1.10.c Define compiled-case world/entities/scenes/dialogue/timeline/
        clues/runtime graph/rules/hints/solution partitions/assets/locales/
        save-state compatibility. Evidence: case goldens.
  - [ ] Y1.10.d Separate player-visible, progressive/reveal, solver/reviewer,
        protected-source, and operator diagnostics with encryption/access.
        Evidence: spoiler partition tests.
  - [ ] Y1.10.e Define format/schema/runtime/consumer min-max versions,
        extensions, migration, degradation/loss, and unsupported behavior.
        Evidence: compatibility policy.
  - [ ] Y1.10.f Produce deterministic manifests/packages and
        validate/import/read- back minimal/full/edge/multilocale fixtures.
        Evidence: bundle suite.
- [ ] Y1.11 Define consumer read-model derivation. Evidence: projections are
      reproducible from approved authoring revisions.
  - [ ] Y1.11.a Define each consumer projection’s exact approved bundle/
        authoring revision inputs, schema/version, audience/locale/entitlement,
        owner store/index/cache, and rebuild policy. Evidence: projection map.
  - [ ] Y1.11.b Define deterministic transforms for study navigation/content/
        search/activity and case
        runtime/entity/clue/dialogue/timeline/save-state models. Evidence:
        transform goldens.
  - [ ] Y1.11.c Exclude drafts/comments/decisions/protected sources/solutions/
        reviewer fields/production secrets according to consumer audience.
        Evidence: field boundary tests.
  - [ ] Y1.11.d Build idempotently from release events/bundles with version/
        ordering/checkpoint/retry/dead-letter and no authoring direct writes.
        Evidence: projection job tests.
  - [ ] Y1.11.e Rebuild from scratch and compare hashes/semantics to incremental
        state; reconcile supersession/unpublish/recall. Evidence:
        reproducibility tests.
- [ ] Y1.12 Define backward compatibility with existing case and study
      contracts. Evidence: old-version fixture migration and unsupported version
      refusal.
  - [ ] Y1.12.a Inventory existing schema/protocol/bundle/database/client/
        consumer versions, fixtures, deployments, producers, and support
        windows. Evidence: version inventory.
  - [ ] Y1.12.b Map old-to-canonical fields/enums/IDs/semantics/defaults/loss/
        unsupported states separately for study and case. Evidence: migration
        mappings.
  - [ ] Y1.12.c Implement explicit version detection, parse validation, staged
        pure migrations, provenance, warnings, and immutable original retention.
        Evidence: migration tests.
  - [ ] Y1.12.d Define read-old/write-new/dual-read/shim/client-server/runtime
        negotiation and expiration/telemetry. Evidence: compatibility tests.
  - [ ] Y1.12.e Migrate minimal/full/edge old fixtures and semantic round-trip
        them through current consumers. Evidence: golden suite.
  - [ ] Y1.12.f Reject unknown/future/expired/incompatible versions with typed
        safe diagnostics and no partial import/runtime fallback. Evidence:
        refusal tests.

### Y2 — Study source and material authoring

- [ ] Y2.1 Build study-source intake from upload, URL, reference, import, and
      existing Sophia/Yemaya records. Evidence: provenance round-trip.
  - [ ] Y2.1.a Define
        source/intake/version/provenance/rights/checksum/connector/
        project-link/status contracts common to every path. Evidence: schemas.
  - [ ] Y2.1.b Implement bounded streamed upload with content sniff/hash/
        temporary quarantine/tenant isolation. Evidence: upload tests.
  - [ ] Y2.1.c Implement allowlisted URL/reference retrieval with SSRF/redirect/
        DNS/size/type/time controls and immutable receipt. Evidence: network
        tests.
  - [ ] Y2.1.d Implement safe package/reference import with version/profile/
        manifest/path/dependency validation and preview. Evidence: import tests.
  - [ ] Y2.1.e Resolve existing Sophia/Yemaya records through public owner
        contracts, exact IDs/revisions/authorization, and no data copy by URL.
        Evidence: integration tests.
  - [ ] Y2.1.f Normalize paths to one immutable source version and round-trip
        original locator/bytes/hash/rights/connector/version/audit. Evidence:
        provenance suite.
- [ ] Y2.2 Quarantine and scan documents, archives, media, and project imports.
      Evidence: malicious corpus.
  - [ ] Y2.2.a Store new content in nonpublic nonexecutable quarantine with
        opaque names, encryption, tenant scope, limits, and no downstream use.
        Evidence: quarantine tests.
  - [ ] Y2.2.b Scan malware/signature/polyglot/macros/scripts/active content and
        source/project supply-chain metadata. Evidence: malicious fixtures.
  - [ ] Y2.2.c Enforce archive traversal/symlink/nesting/bomb/file-count/
        compressed-uncompressed size/password rules. Evidence: archive tests.
  - [ ] Y2.2.d Enforce document/page/object/parser/OCR and media codec/duration/
        dimension/sample/decode/resource limits. Evidence: resource tests.
  - [ ] Y2.2.e Validate project manifest/schema/dependencies/plugins/scripts/
        paths/versions/signatures and block executable or unknown constructs.
        Evidence: project import tests.
  - [ ] Y2.2.f Persist scanner/version/config/hash/results and admit exact bytes
        only after required fresh scans; preserve quarantine/manual/unavailable.
        Evidence: admission tests.
- [ ] Y2.3 Extract source hierarchy, text, media, tables, headings, pages, and
      stable anchors. Evidence: golden source corpus.
  - [ ] Y2.3.a Define extraction run/tree/node/content/anchor/confidence/error
        contracts bound to source hash and extractor/config versions. Evidence:
        schemas.
  - [ ] Y2.3.b Extract document/book/site/package hierarchy, order, headings/
        sections/pages, reading flow, IDs, and cross-references. Evidence:
        hierarchy goldens.
  - [ ] Y2.3.c Extract normalized text/paragraph/list/footnote/quote/citation
        with source offsets/page/region and language/direction. Evidence: text
        goldens.
  - [ ] Y2.3.d Extract images/audio/video/files with bytes/hash/caption/alt/
        technical metadata/time/page anchors and rights. Evidence: media
        goldens.
  - [ ] Y2.3.e Extract tables with cell/header/span/caption/units/footnotes/
        region and semantic/text fallback. Evidence: table goldens.
  - [ ] Y2.3.f Generate stable anchor identities/fingerprints/context and test
        multilingual/RTL/OCR/complex/malformed/partial fixtures. Evidence:
        golden source corpus.
- [ ] Y2.4 Build source-tree editing with cycle prevention, keyboard reorder,
      bulk move, and impact preview. Evidence: accessible tree journey.
  - [ ] Y2.4.a Define editable collection/source/section/node hierarchy
        operations, stable IDs/order keys, locks, depth/cardinality, and
        revision semantics. Evidence: tree contract.
  - [ ] Y2.4.b Build tree and synchronized list/outline with create/rename/
        reorder/reparent/group/remove/restore by pointer and keyboard/non-drag.
        Evidence: interaction tests.
  - [ ] Y2.4.c Validate self/descendant cycles, duplicates, missing parent,
        protected roots, cross-project/tenant moves, and stale versions
        server-side. Evidence: negative tests.
  - [ ] Y2.4.d Build selected/all/filter-descendant bulk move with count/scope/
        destination validation, atomic/partial policy, and confirmation.
        Evidence: bulk tests.
  - [ ] Y2.4.e Preview affected anchors/notes/claims/materials/reading order/
        cases/translations/builds/permissions before commit. Evidence: impact
        tests.
  - [ ] Y2.4.f Verify accessible focus/announcement/zoom/large-tree/concurrent
        journey and immutable history. Evidence: Playwright E2E.
- [ ] Y2.5 Build note and excerpt capture anchored to exact source locations.
      Evidence: source update/re-extraction preserves or explicitly orphans the
      anchor.
  - [ ] Y2.5.a Define note/excerpt logical/revision identity, author/project/
        visibility/locale/tags/concepts/claims/status and typed exact source
        anchor. Evidence: schema.
  - [ ] Y2.5.b Capture selected text/region/page/time/table slice/URI fragment
        with exact source/extraction revision, quote/context/hash, and bounds.
        Evidence: capture tests.
  - [ ] Y2.5.c Author note blocks/annotations/commentary separately from
        immutable excerpt and retain source rights/attribution/spoiler
        classification. Evidence: editing tests.
  - [ ] Y2.5.d Validate source/anchor/access and render citation trail/side-by-
        side context accessibly without protected-source leakage. Evidence:
        viewer tests.
  - [ ] Y2.5.e On source update/re-extraction map anchor
        exact/shifted/ambiguous/ orphaned, preserve original, and require
        reattachment/review. Evidence: survival tests.
- [ ] Y2.6 Build concept and claim extraction behind the fenced provider seam.
      Evidence: human confirmation before persistence.
  - [ ] Y2.6.a Define concept/claim proposal with exact source/extraction/span,
        normalized term/proposition, aliases/type/qualifiers/relations,
        confidence, provider/model/prompt/config, and status. Evidence: schemas.
  - [ ] Y2.6.b Send only authorized bounded source spans fenced as untrusted
        data with no tools/project secrets/solution/canon authority. Evidence:
        injection tests.
  - [ ] Y2.6.c Validate proposal schema/locators/source support/duplicates/
        conflicts/size and preserve not-configured/refusal/malformed/partial.
        Evidence: seam tests.
  - [ ] Y2.6.d Present source context, similar concepts/claims, uncertainty,
        suggested relations, and accept/edit/reject/defer per proposal.
        Evidence: review tests.
  - [ ] Y2.6.e Create canonical revisions only from authorized human decisions,
        retaining original proposal/provider/source lineage. Evidence: authority
        tests.
- [ ] Y2.7 Build citation attachment and source trails. Evidence: unsupported or
      missing locators fail validation.
  - [ ] Y2.7.a Define citation/relationship contract linking exact note/claim/
        block/case/canon target to source revision, typed locator,
        quote/context, support role, and verification. Evidence: schema.
  - [ ] Y2.7.b Implement page/section/paragraph/text range/region/timecode/
        dataset/table/URI/entity locators with source-kind compatibility.
        Evidence: locator fixtures.
  - [ ] Y2.7.c Validate syntax/bounds/hash/revision/rights/access and reject
        unsupported/missing/mutable-only locators. Evidence: validation tests.
  - [ ] Y2.7.d Build forward target-to-source and reverse source-to-target
        trails with intermediate claims/extractions/translations/decisions and
        stable pagination. Evidence: trail tests.
  - [ ] Y2.7.e Render/export accessible citations/trails with authorized
        excerpts/redactions and update/orphan state. Evidence: UI/export tests.
- [ ] Y2.8 Build outline divergence and compare. Evidence: scores and provider
      provenance are preserved.
  - [ ] Y2.8.a Define outline/variant-set/module/node/order/source/concept/
        objective/constraint contracts with exact brief/source revision and
        manual/generated discriminator. Evidence: schema.
  - [ ] Y2.8.b Generate bounded N variants with diversity strategy, seed/
        controls, provider/model/prompt/config, estimate/budget, and unique IDs.
        Evidence: generation tests.
  - [ ] Y2.8.c Validate hierarchy/reading order/prerequisites/source coverage/
        workload/rights/accessibility and retain invalid candidates. Evidence:
        validation tests.
  - [ ] Y2.8.d Record rule/provider/evaluator/human scores separately with
        version/rubric/uncertainty/N/A and no score invention for manual
        outlines. Evidence: score tests.
  - [ ] Y2.8.e Build side-by-side semantic structure/content/source/coverage/
        cost diff and select/reject/merge/fork decisions preserving all
        candidates. Evidence: compare journey.
- [ ] Y2.9 Build a structured study-material editor for explanation, excerpt,
      comparison, question, activity, reflection, media, glossary, and case
      link. Evidence: block registry tests.
  - [ ] Y2.9.a Define versioned block registry with shared
        identity/order/locale/ source/concept/accessibility fields and per-kind
        schema/renderer/ validator/export mappings. Evidence: registry.
  - [ ] Y2.9.b Implement explanation/excerpt/comparison typed editors with
        claim/ citation/source-pane and contrasted-dimension semantics.
        Evidence: content block tests.
  - [ ] Y2.9.c Implement question/activity/reflection editors with instructions/
        expected engagement/resources/time/response/noninteractive alternative
        and no learner-state mutation. Evidence: activity tests.
  - [ ] Y2.9.d Implement governed media attachment and glossary term/definition/
        locale/source links plus exact interactive-case deep link/context.
        Evidence: media/glossary/case tests.
  - [ ] Y2.9.e Implement insert/reorder/nest/convert/duplicate/reference/delete/
        restore with stable IDs, depth rules, dependency invalidation, and
        non-drag controls. Evidence: editing tests.
  - [ ] Y2.9.f Fail registry coverage for missing editor/consumer renderer/
        validation/accessibility/localization/import-export/preview/test.
        Evidence: block coverage gate.
- [ ] Y2.10 Build reading-order and prerequisite editing. Evidence: cycle,
      unreachable node, and missing prerequisite gates.
  - [ ] Y2.10.a Define separate ordered sequence/branch and prerequisite DAG
        edge kinds, optional/required/choice/remedial semantics, conditions, and
        exact node revisions. Evidence: graph schema.
  - [ ] Y2.10.b Build accessible visual plus synchronized list/table create/
        connect/disconnect/reorder/group/branch operations. Evidence: UI tests.
  - [ ] Y2.10.c Detect self/cycles/duplicates/conflicting order, missing roots/
        ends/prerequisites, orphans/unreachable/dead branches. Evidence:
        validation fixtures.
  - [ ] Y2.10.d Validate prerequisite availability before node and reading flow/
        locale/accessibility/consumer compatibility. Evidence: sequence tests.
  - [ ] Y2.10.e Preview impact on notes/materials/activities/cases/builds and
        recompute stale reviews/gates after revision. Evidence: impact tests.
- [ ] Y2.11 Build source-hierarchy completeness, anchor coverage, claim
      grounding, reading-order coherence, accessibility, and rights gates.
      Evidence: known-value fixtures.
  - [ ] Y2.11.a Resolve exact project/material/source/tree/anchor/claim/order/
        block/asset/locale/release revisions and applicable gate/profile
        versions. Evidence: input tests.
  - [ ] Y2.11.b Measure required source-node hierarchy/metadata/extraction/
        authority/currency completeness and note/block anchor coverage/orphan
        rate. Evidence: hierarchy/anchor fixtures.
  - [ ] Y2.11.c Evaluate atomic claim support/sufficiency/faithfulness/conflict/
        citation locators and high-risk source floors. Evidence: grounding
        fixtures.
  - [ ] Y2.11.d Evaluate order/prerequisite cycle/reachability/coherence/
        workload and content/asset semantic accessibility/localization parity.
        Evidence: order/accessibility fixtures.
  - [ ] Y2.11.e Evaluate source/content/media rights/attribution/embargo/term
        and downstream target compatibility. Evidence: rights fixtures.
  - [ ] Y2.11.f Persist raw results separately from policy verdicts/uncertainty/
        N/A/manual review and block mandatory failures. Evidence: gate suite.
- [ ] Y2.12 Build consumer preview through real `study-workspace` read models.
      Evidence: preview parity without writing learner state.
  - [ ] Y2.12.a Compile exact draft/approved authoring revisions into an
        isolated preview bundle and derive the same versioned read models as
        consumer. Evidence: preview pipeline tests.
  - [ ] Y2.12.b Render through real consumer route/components/runtime with
        synthetic identity, selected locale/device/theme/accessibility/offline
        profile, and prominent preview state. Evidence: renderer parity tests.
  - [ ] Y2.12.c Exercise navigation/reading order/concepts/search/media/
        activities/case links and compare semantics to published bundle.
        Evidence: interaction parity.
  - [ ] Y2.12.d Isolate preview events/cache/progress/bookmarks/analytics/
        notifications from real learner/consumer systems. Evidence: side-effect
        tests.
  - [ ] Y2.12.e Verify permission/spoiler/protected-source/failed build/stale/
        offline and accessibility behavior. Evidence: preview E2E.
- [ ] Y2.13 Build localization with citations, glossary, directionality,
      source-locale trace, media parity, and review. Evidence: locale bundle
      golden.
  - [ ] Y2.13.a Define locale job/unit/variant/source revision/translation/
        reviewer/status/fallback contracts and stable IDs. Evidence: schema.
  - [ ] Y2.13.b Translate structured material blocks/concepts/glossary/metadata/
        instructions with TM/glossary suggestions and human acceptance.
        Evidence: content tests.
  - [ ] Y2.13.c Preserve citation source locale/exact locator, translated quote/
        paraphrase relation, source authority, and no false translated source.
        Evidence: citation tests.
  - [ ] Y2.13.d Handle RTL/bidi/Unicode/numerals/dates/units/layout plus
        localized media/captions/transcripts/descriptions/voice/assets with
        parity. Evidence: locale/media tests.
  - [ ] Y2.13.e Run meaning/claim/key-term/citation/order/accessibility/media/
        target parity gates and required locale/cultural review. Evidence:
        review tests.
  - [ ] Y2.13.f Build deterministic locale bundle and consumer round-trip for
        RTL/long/CJK/missing-fallback cases. Evidence: golden.
- [ ] Y2.14 Build source update/retraction blast radius across notes, claims,
      materials, cases, translations, and published items. Evidence: propagation
      test.
  - [ ] Y2.14.a Maintain immutable reverse dependency edges from source/
        extraction/anchor/citation/claim into every named consumer and bundle/
        read model. Evidence: graph tests.
  - [ ] Y2.14.b Ingest verified update/supersession/retraction/rights change
        with authority/scope/effective time/source matching. Evidence: intake
        tests.
  - [ ] Y2.14.c Traverse direct/transitive impacts with reason/path/revision/
        current state/owner/unknown and tenant/property authorization. Evidence:
        blast-radius tests.
  - [ ] Y2.14.d Mark affected draft/review/canon/translation/build/publication/
        consumer eligibility stale/blocked without rewriting history. Evidence:
        propagation tests.
  - [ ] Y2.14.e Route re-extract/reattach/re-source/rewrite/review/relocalize/
        rebuild/unpublish/recall actions and verify downstream read-back.
        Evidence: end-to-end propagation.

### Y3 — Interactive-case concept, structure, and writing

- [ ] Y3.1 Build zero-form case-premise capture with genre, audience,
      constraints, safety, setting, duration, difficulty, locale, and format.
      Evidence: keyboard-first capture.
  - [ ] Y3.1.a Define minimal capture identity/project/author/raw idea/source/
        provenance/draft status and typed eventual premise requirements.
        Evidence: schema.
  - [ ] Y3.1.b Implement keyboard-first freeform/paste/voice-safe capture with
        genre/audience/setting/format hints, autosave/offline, and accessible
        shortcuts. Evidence: capture tests.
  - [ ] Y3.1.c Collect structured audience/age, genre/tone, setting/time,
        duration/pacing/difficulty, locale/language, format/target constraints.
        Evidence: completion tests.
  - [ ] Y3.1.d Collect safety/sensitivity/identity/rights/source/production/
        accessibility/budget/code-floor constraints and explicit unknowns.
        Evidence: constraint tests.
  - [ ] Y3.1.e Create immutable premise draft retaining raw capture and
        validation/provenance; never treat capture as approved framing.
        Evidence: lineage tests.
- [ ] Y3.2 Build case-framing divergence through `case-writers-room` and
      `case-director`. Evidence: real provider seam and exact returned variants.
  - [ ] Y3.2.a Define framing request/plan/variant-set with exact
        premise/source/ canon/constraints, requested count/diversity, budget,
        provider/tool versions, and output schema. Evidence: contracts.
  - [ ] Y3.2.b Route writers-room ideation and case-director structural planning
        through their canonical injected operations, not duplicate prompts or
        private imports. Evidence: seam tests.
  - [ ] Y3.2.c Fence untrusted sources/premise, enforce tool grants/budget/stop/
        timeout/safety/spoiler boundaries, and preserve not-configured/refusal/
        malformed/partial states. Evidence: provider tests.
  - [ ] Y3.2.d Create exact unique returned premise/solution/suspects/clue/
        structure/tone/scope variants with full step/model/prompt/config/cost
        lineage. Evidence: variant fixtures.
  - [ ] Y3.2.e Validate schema/canon/safety/fairness/production feasibility
        without automatically accepting or hiding invalid variants. Evidence:
        validation tests.
- [ ] Y3.3 Build variant compare for premise, solution, suspect set, clue
      topology, tone, production scope, and gate estimates. Evidence: comparison
      fixture.
  - [ ] Y3.3.a Resolve authorized exact manual/generated variant revisions and
        define comparison session/layout/selection contracts. Evidence: schema.
  - [ ] Y3.3.b Diff premise/question/theme/setting/audience/format/difficulty
        and tone/style/safety/source/canon assumptions. Evidence: premise diff.
  - [ ] Y3.3.c Diff solution/truth/proof chain/ambiguity, suspects/motive/means/
        opportunity/alibis/relationships, and alternative hypotheses. Evidence:
        solution/cast diff.
  - [ ] Y3.3.d Diff clue/red-herring topology, dependencies/discovery/reveal/
        evidentiary coverage/fairness and scene/timeline branch structure.
        Evidence: topology diff.
  - [ ] Y3.3.e Diff asset/scene/dialogue/locale/voice/render/engine/target/
        schedule/resource/cost production scope. Evidence: scope diff.
  - [ ] Y3.3.f Present per-gate raw estimate/availability/uncertainty/provider
        provenance and accessible table/graph alternatives. Evidence: comparison
        fixture.
- [ ] Y3.4 Build decision records for selected and rejected case framings.
      Evidence: downstream case links to the decision.
  - [ ] Y3.4.a Define immutable decision with exact variant set/revisions,
        actor/ authority, select/reject/defer/merge outcome, reasons/evidence/
        conditions/time. Evidence: schema.
  - [ ] Y3.4.b Record one or multiple selected contributions and every rejected
        variant/reason without deleting or rewriting them. Evidence: decision
        tests.
  - [ ] Y3.4.c Create a new case/premise revision from the selected or
        explicitly merged framing with source contribution lineage. Evidence:
        creation tests.
  - [ ] Y3.4.d Link all downstream mystery/cast/clue/scene/production decisions
        to the framing decision and exact premise revision. Evidence: lineage.
  - [ ] Y3.4.e Supersede through a new decision after impact review while
        preserving historical current/old projections. Evidence: lifecycle
        tests.
- [ ] Y3.5 Build the case dossier with premise, solution, cast, clues, scenes,
      timeline, locations, canon, assets, gates, review, and history. Evidence:
      live browser journey.
  - [ ] Y3.5.a Define one permission/spoiler-filtered live projection joining
        exact case revision and every named relationship/freshness state.
        Evidence: dossier contract.
  - [ ] Y3.5.b Present premise/mystery/solution, cast/relationships/knowledge/
        motives, clue/red-herring/proof topology, and authorized differences.
        Evidence: story panels.
  - [ ] Y3.5.c Present scene/timeline/location/branch/state/dialogue and canon/
        source/contradiction/continuity state. Evidence: structure panels.
  - [ ] Y3.5.d Present assets/production/jobs/budget/schedule/localization/
        consumer-build dependencies and failure state. Evidence: production
        panels.
  - [ ] Y3.5.e Present solvability/fairness/safety/canon/technical gates,
        comments/reviews/decisions/approvals/remediation/maturity. Evidence:
        review panels.
  - [ ] Y3.5.f Present semantic revision/branch/merge/decision/audit/playtest/
        build/release history with stable links. Evidence: history tests.
  - [ ] Y3.5.g Run live edit-review-playtest-remediate journey with
        role/spoiler/ tenant/accessibility/responsive/large-case proof.
        Evidence: Playwright.
- [ ] Y3.6 Build suspect editor with identity, role, motive, means, opportunity,
      alibi, secrets, relationships, knowledge state, and reveal constraints.
      Evidence: contract validation.
  - [ ] Y3.6.a Author stable character/suspect identity, names/aliases/pronouns/
        locale, role/archetype, background/traits/goals/arc, source/canon, and
        asset refs. Evidence: identity tests.
  - [ ] Y3.6.b Author typed motive/means/opportunity with temporal/location/
        capability/resource/evidence/strength/contradiction and truth status.
        Evidence: MMO tests.
  - [ ] Y3.6.c Author alibi claim/events/witness/source/reliability/timeline/
        verification/contradictions and resolved/false/partial status. Evidence:
        alibi tests.
  - [ ] Y3.6.d Author secret/knowledge fact, learned-at-event, knows/does-not-
        know/believes/lies/uncertain, visibility/spoiler, and evolution.
        Evidence: knowledge tests.
  - [ ] Y3.6.e Author typed directed relationship history and reveal earliest/
        latest/condition/audience/order/forbidden disclosure constraints.
        Evidence: relationship/reveal tests.
  - [ ] Y3.6.f Validate stable IDs, required fields, timeline/canon consistency,
        circular secrets, impossible knowledge, clue/solution links, and
        protected fields. Evidence: contract suite.
- [ ] Y3.7 Build clue editor with type, source, discovery condition, timing,
      visibility, interpretation, dependencies, evidentiary weight, and truth
      status. Evidence: invariant tests.
  - [ ] Y3.7.a Author clue identity/kind/content/representation/source/citation/
        asset/locale/rights/canon and intended solver role. Evidence: clue
        schema.
  - [ ] Y3.7.b Author discovery actor/location/scene/action/tool/prerequisite/
        state/time/window/branch/repeat/miss/fallback conditions as typed logic.
        Evidence: discovery tests.
  - [ ] Y3.7.c Author visibility/salience/presentation/spoiler/knowledge and
        interpretation list with supporting/refuting facts, ambiguity, and
        hints. Evidence: interpretation tests.
  - [ ] Y3.7.d Author clue-to-clue/scene/suspect/timeline/claim dependencies,
        evidentiary dimension/weight/confidence, and proof-chain contribution.
        Evidence: dependency tests.
  - [ ] Y3.7.e Author true/false/misleading/ambiguous/fabricated/altered/
        incomplete status with truth/canon/source and reveal/payoff resolution.
        Evidence: truth tests.
  - [ ] Y3.7.f Validate reachable discovery, dependency cycles, chronology,
        source/asset rights, sufficient resolution, and no unsupported freeform
        conditions. Evidence: invariants.
- [ ] Y3.8 Build red-herring editor with fairness justification and resolution.
      Evidence: unresolved or indistinguishable red herrings fail review.
  - [ ] Y3.8.a Author red-herring clue/suspect/hypothesis linkage, intended
        inference, source/canon/truth basis, placement/salience, and scope.
        Evidence: schema.
  - [ ] Y3.8.b Author fairness justification showing why it is plausible yet
        distinguishable through available evidence, logic, timing, or source.
        Evidence: fairness tests.
  - [ ] Y3.8.c Author required contradiction/correction/disproof/reveal/payoff,
        discovery dependencies, earliest resolution, and player feedback.
        Evidence: resolution tests.
  - [ ] Y3.8.d Compare to true clue/path for unique signals and detect
        identical/ strictly
        indistinguishable/unresolvable/misleading-without-payoff cases.
        Evidence: comparison fixtures.
  - [ ] Y3.8.e Block review on missing justification/resolution/unreachable
        disproof or harmful/unfair manipulation; preserve manual evidence.
        Evidence: gate tests.
- [ ] Y3.9 Build scene editor with participants, location, time, objectives,
      dialogue/actions, clues, state effects, branches, assets, and canon refs.
      Evidence: structured-block tests.
  - [ ] Y3.9.a Define scene identity/revision/order/sequence/branch, title/
        summary/purpose/pacing/duration/status/owner/locale and source refs.
        Evidence: scene schema.
  - [ ] Y3.9.b Author participants/entry-exit/presence/role/knowledge/wardrobe/
        prop/continuity and location/set/subarea/time/weather/light. Evidence:
        context tests.
  - [ ] Y3.9.c Author objectives/beats/conflicts/outcomes and typed ordered
        dialogue/action/direction/choice/interaction/transition blocks.
        Evidence: block tests.
  - [ ] Y3.9.d Place clues/red herrings/discovery/reveal/knowledge changes and
        state preconditions/effects with exact entity refs. Evidence: state
        tests.
  - [ ] Y3.9.e Author branch choices/conditions/targets/merge/fallback/dead-end
        semantics and sequence/timeline/causal links. Evidence: branch tests.
  - [ ] Y3.9.f Link assets/shots/storyboards/audio/locales/production tasks and
        exact canon assertions/exceptions/continuity profiles. Evidence:
        production/canon tests.
  - [ ] Y3.9.g Validate presence/knowledge/timeline/location/state/canon/clue/
        branch/asset constraints and structured block registry coverage.
        Evidence: validation suite.
- [ ] Y3.10 Build timeline editor with absolute/relative time, uncertainty,
      witness perspective, evidence refs, and contradiction detection. Evidence:
      keyboard and semantic-list parity.
  - [ ] Y3.10.a Define event/interval identity/revision, absolute instant/range/
        time zone/calendar/story era or relative-before/after/during/offset/
        sequence constraints. Evidence: temporal schema.
  - [ ] Y3.10.b Author duration/uncertainty/earliest-latest/confidence and
        participants/location/actions/state/clue/scene/canon/source links.
        Evidence: event tests.
  - [ ] Y3.10.c Author separate witness/source perspectives for
        observed/reported/ inferred/remembered/believed times with
        reliability/contradictions. Evidence: perspective tests.
  - [ ] Y3.10.d Build visual lane/scale/order/zoom/filter editor and
        synchronized keyboard-operable semantic list/table/forms. Evidence:
        parity tests.
  - [ ] Y3.10.e Solve temporal constraints and flag impossible intervals/order/
        travel/presence/alibi/knowledge/age/duration, source/canon conflicts
        with explanation. Evidence: contradiction fixtures.
  - [ ] Y3.10.f Preserve ambiguous/alternate chronologies and require human
        resolution rather than autochanging events. Evidence: review tests.
- [ ] Y3.11 Build relationship and clue graphs with semantic table parity.
      Evidence: accessible navigation and graph/list consistency.
  - [ ] Y3.11.a Define graph view contracts with stable entity/edge IDs,
        revisions/types/properties/visibility/evidence/validation and role/
        spoiler projection. Evidence: schema.
  - [ ] Y3.11.b Build relationship graph filters/layout/time/point-of-view/
        knowledge/canon/strength/status and typed edit actions. Evidence:
        relationship UI tests.
  - [ ] Y3.11.c Build clue graph for clues/hypotheses/suspects/scenes/discovery/
        dependencies/support/refute/reveal/proof paths/fairness. Evidence: clue
        UI tests.
  - [ ] Y3.11.d Provide synchronized table/tree/list/forms for inspect/filter/
        select/add/edit/link/unlink/navigate/reorder with non-drag keyboard
        parity. Evidence: semantic tests.
  - [ ] Y3.11.e Keep visual/table selection/filter/edit/validation consistent
        and surface cycles/orphans/unreachable/conflicts/unknown. Evidence:
        consistency tests.
  - [ ] Y3.11.f Verify screen reader/keyboard/zoom/reflow/reduced motion/large
        graph and direct authorization. Evidence: accessible E2E.
- [ ] Y3.12 Build solution editor with proof chain, required clue set,
      alternative-hypothesis disposition, reveal sequence, and confidence.
      Evidence: schema invariants.
  - [ ] Y3.12.a Define protected solution identity/revision, truth outcome/
        culprit/cause/mechanism/timeline/motive/means/opportunity and source/
        canon basis. Evidence: solution schema.
  - [ ] Y3.12.b Author proof graph steps/claims/inferences/rules/assumptions and
        support/refute clue/evidence refs with ordering/dependencies. Evidence:
        proof-chain tests.
  - [ ] Y3.12.c Define required/sufficient/optional/corroborating/exculpatory/
        misleading clue sets and minimum path/coverage/fallback. Evidence:
        clue-set tests.
  - [ ] Y3.12.d Enumerate plausible alternative hypotheses/suspects and
        disposition as refuted/less supported/equivalent/unresolved with exact
        evidence. Evidence: alternative tests.
  - [ ] Y3.12.e Author reveal
        sequence/timing/conditions/presentation/explanation/ payoff and ending
        linkage without premature spoiler exposure. Evidence: reveal tests.
  - [ ] Y3.12.f Record author/solver/calibrated confidence/uncertainty
        separately and validate proof acyclicity/soundness/clue
        reachability/uniqueness/ canon/timeline. Evidence: invariants.
- [ ] Y3.13 Build branch and state-condition editor. Evidence: unreachable,
      dead-end, cyclic, and impossible-condition analysis.
  - [ ] Y3.13.a Define typed state variables/domains/defaults/ownership/
        visibility, condition expression AST, effects, branch/choice/target/
        merge/end contracts. Evidence: schema.
  - [ ] Y3.13.b Build allowlisted visual/form expression editor for boolean/
        comparison/set/count/temporal/knowledge/clue/inventory flags with no
        arbitrary code. Evidence: condition tests.
  - [ ] Y3.13.c Build branch create/order/condition/priority/fallback/target/
        effect/merge authoring with semantic list and graph views. Evidence:
        editor tests.
  - [ ] Y3.13.d Analyze reachability, dead ends, unintended cycles/loops,
        mutually exclusive/exhaustive conditions, impossible state, shadowed/
        nondeterministic branches, and orphan content. Evidence: analysis
        fixtures.
  - [ ] Y3.13.e Simulate bounded seeded paths and show counterexample trace/
        assumptions without claiming exhaustive proof when limits are reached.
        Evidence: simulation tests.
- [ ] Y3.14 Build character/scene dialogue assist with canon and spoiler fences.
      Evidence: explicit accept and AI attribution.
  - [ ] Y3.14.a Define assist request/proposal with exact scene/character/
        dialogue/locale/canon/knowledge/spoiler/style revisions, operation,
        provider provenance, and cost. Evidence: schema.
  - [ ] Y3.14.b Build only authorized context from character voice/relationship/
        current knowledge/scene objective/canon/source, excluding future
        reveals/ solutions/private sources outside scope. Evidence: context
        tests.
  - [ ] Y3.14.c Fence content, enforce no tool/approval authority,
        provider/budget/ timeout/safety/identity/rights controls, and honest
        failure states. Evidence: seam tests.
  - [ ] Y3.14.d Validate speaker/scene/knowledge/canon/timeline/spoiler/style/
        locale/safety/accessibility and flag conflicts. Evidence: proposal
        tests.
  - [ ] Y3.14.e Present per-line/block diff/context/attribution and require
        explicit accept/edit/reject; acceptance creates new revision and reruns
        gates. Evidence: review tests.
- [ ] Y3.15 Build script/table-read preview. Evidence: roles, timing, missing
      lines, and accessibility output.
  - [ ] Y3.15.a Compile selected case/scenes/branches/dialogue revisions into
        script format with stable scene/beat/line/speaker IDs and spoiler scope.
        Evidence: script tests.
  - [ ] Y3.15.b Validate cast/role assignments, missing/duplicate/unknown
        speaker, dialogue order, directions, scene coverage, branch variants,
        and participant access. Evidence: role tests.
  - [ ] Y3.15.c Estimate/read timed scene/line/pause/action totals and flag
        missing lines/overlap/pacing with assumptions. Evidence: timing tests.
  - [ ] Y3.15.d Render text table read and optional consented human/approved TTS
        voices with pronunciation, playback controls, transcript/captions.
        Evidence: playback tests.
  - [ ] Y3.15.e Export accessible script with semantic headings/speaker labels/
        directions/alt text, large print/locale/bidi and no unauthorized
        solutions. Evidence: accessibility tests.
- [ ] Y3.16 Build playtest configuration with spoiler-protected participants,
      build version, consent, telemetry scope, and retention. Evidence:
      privacy-safe test.
  - [ ] Y3.16.a Define playtest/session/cohort/participant opaque identity,
        exact case/build/locale/device/accessibility version, purpose, schedule,
        owner, and lifecycle. Evidence: schema.
  - [ ] Y3.16.b Configure participant eligibility/invite/auth/role/embargo/
        spoiler compartment, conflict/prior knowledge, guardian/age rules, and
        compensation. Evidence: participant tests.
  - [ ] Y3.16.c Capture consent for participation/recording/voice/video/
        telemetry/quotes/recontact/use/term/withdrawal and provide accessible
        information. Evidence: consent tests.
  - [ ] Y3.16.d Allowlist telemetry events/fields/sampling/purpose, exclude raw
        secrets/solutions/PII where unnecessary, and define offline upload/
        encryption/access. Evidence: telemetry tests.
  - [ ] Y3.16.e Define session data/comment/replay/media retention/deletion/
        legal hold/export and participant withdrawal propagation. Evidence:
        lifecycle tests.
  - [ ] Y3.16.f Validate build availability/integrity, no admin/debug/solution
        data, participant entitlements, and privacy review before launch.
        Evidence: privacy-safe gate.
- [ ] Y3.17 Build playtest feedback and session replay linked to immutable case
      revision. Evidence: no replay leaks solution to unauthorized users.
  - [ ] Y3.17.a Define feedback/event/replay artifact with exact case/build/
        participant/session/locale/device/config revisions, timestamps, source,
        consent, visibility, and integrity. Evidence: schema.
  - [ ] Y3.17.b Capture structured difficulty/fairness/confusion/enjoyment/
        accessibility/bug/clue/scene feedback plus optional consented
        text/media. Evidence: feedback tests.
  - [ ] Y3.17.c Build deterministic replay from allowlisted ordered events/state
        checkpoints/input/media refs with versioned runtime and gaps/unknown.
        Evidence: replay tests.
  - [ ] Y3.17.d Apply participant/author/solver/reviewer spoiler/solution/
        protected-source/property permissions to timelines, events, state,
        feedback, search, exports, and URLs. Evidence: leak tests.
  - [ ] Y3.17.e Anchor comments/findings to session time/event/scene/clue/build
        and route aggregated/redacted proposals to authoring without auto-edit.
        Evidence: feedback-to-revision tests.
  - [ ] Y3.17.f Test revoked consent/retention expiry/deleted participant/case
        supersession/corrupt replay/cross-tenant access. Evidence: lifecycle
        E2E.
- [ ] Y3.18 Build authoring templates for genre and format with code-owned
      fairness/safety floors. Evidence: template property tests.
  - [ ] Y3.18.a Define versioned template identity/scope/genre/format/audience/
        project profile, typed premise/cast/clue/scene/solution/production
        defaults/requirements, parent, and lifecycle. Evidence: schema.
  - [ ] Y3.18.b Define genre-specific
        conventions/tropes/tone/pacing/clue/fairness/ sensitivity and
        format-specific duration/branch/interaction/asset/target constraints as
        guidance or typed requirements. Evidence: fixtures.
  - [ ] Y3.18.c Resolve inheritance with exact versions/cycle/depth and field/
        collection/graph merge/replace/append/remove/lock semantics. Evidence:
        inheritance tests.
  - [ ] Y3.18.d Apply code/region/platform
        fairness/safety/rights/consent/privacy/ accessibility/spoiler/authority
        floors monotonically. Evidence: property tests.
  - [ ] Y3.18.e Preview inherited/defaulted/overridden/locked/conflicts and
        impact, then version/publish/deprecate/migrate consumers explicitly.
        Evidence: lifecycle tests.

### Y4 — Canon, solvability, fairness, and safety gates

- [ ] Y4.1 Implement canon graph validation for missing entities, conflicting
      assertions, temporal impossibility, and namespace mismatch. Evidence:
      known-value canon fixtures.
  - [ ] Y4.1.a Resolve exact canon namespace/entity/predicate/assertion/source/
        status/version graph and validation profile. Evidence: input tests.
  - [ ] Y4.1.b Detect missing/deleted/unresolved/unauthorized entity refs,
        invalid predicate/cardinality/type/unit/literal, dangling relations, and
        required assertion gaps. Evidence: entity fixtures.
  - [ ] Y4.1.c Detect simultaneously applicable contradicting values/relations/
        knowledge/identity/location/state with confidence/status/source scope.
        Evidence: conflict fixtures.
  - [ ] Y4.1.d Solve temporal interval/order/age/duration/presence/state
        constraints and report minimal impossible sets/counterexamples.
        Evidence: temporal fixtures.
  - [ ] Y4.1.e Detect cross-namespace refs, alias/version ambiguity, continuity/
        alternate-universe leakage, and unapproved scoped exceptions. Evidence:
        namespace fixtures.
  - [ ] Y4.1.f Persist per-assertion/path raw findings and human resolution
        state separately from canon gate verdict. Evidence: known-value suite.
- [ ] Y4.2 Implement canon update and retraction blast radius. Evidence:
      affected scenes, dialogue, clues, solutions, assets, and releases become
      stale.
  - [ ] Y4.2.a Maintain immutable forward/reverse edges from canon assertions/
        entities/sources/decisions to every named consumer, build, and release.
        Evidence: dependency tests.
  - [ ] Y4.2.b Ingest authorized new revision/supersession/retcon/deprecation/
        retraction/scoped exception with effective continuity/time and evidence.
        Evidence: update tests.
  - [ ] Y4.2.c Traverse direct/transitive impact with path/reason/revision/
        current state/owner/unknown and spoiler/tenant authorization. Evidence:
        query tests.
  - [ ] Y4.2.d Mark affected scenes/dialogue/clues/solutions/assets/locales/
        reviews/gates/builds/releases stale or blocked without rewriting
        history. Evidence: propagation tests.
  - [ ] Y4.2.e Route
        reconcile/rewrite/relink/regenerate/review/rebuild/unpublish/ recall and
        verify downstream read-back. Evidence: blast-radius E2E.
- [ ] Y4.3 Implement clue reachability from every supported play path. Evidence:
      graph fixtures with reachable and hidden required clues.
  - [ ] Y4.3.a Compile exact case revision into typed state/branch/scene/clue/
        discovery graph with supported start
        states/modes/difficulty/accessibility profiles. Evidence: compiler
        tests.
  - [ ] Y4.3.b Enumerate/explore all paths for bounded cases and use sound
        symbolic/model checking for larger graphs with declared limits.
        Evidence: traversal tests.
  - [ ] Y4.3.c Verify every required clue is discoverable before needed on every
        supported path, including miss/revisit/fallback/branch merge/reset.
        Evidence: reachable/hidden fixtures.
  - [ ] Y4.3.d Return unreachable clue/path/state counterexample traces,
        unsupported/timeout/coverage bounds, and no false exhaustive claim.
        Evidence: result tests.
  - [ ] Y4.3.e Separate optional/bonus/alternative-path clues and
        profile-specific requirements from universal requirements. Evidence:
        applicability tests.
- [ ] Y4.4 Implement solution existence. Evidence: at least one evidence-backed
      solution is provable.
  - [ ] Y4.4.a Define solution rule system, hypothesis space, truth/canon facts,
        required evidence/inference rules, allowed assumptions, and exact case
        revision. Evidence: solver contract.
  - [ ] Y4.4.b Compile clue discovery/interpretations/proof steps/alternative
        paths into solver inputs without consuming protected author assertions
        as proof automatically. Evidence: input tests.
  - [ ] Y4.4.c Find at least one satisfying hypothesis/proof chain using only
        reachable admissible evidence and valid inferences under each supported
        profile. Evidence: positive fixtures.
  - [ ] Y4.4.d Return zero-solution unsatisfied core/counterexample, timeout/
        unsupported/unknown separately and persist trace. Evidence: negative
        fixtures.
  - [ ] Y4.4.e Verify solution matches locked author truth/canon or explicitly
        declared open outcome without circular/self-revealing clues. Evidence:
        existence gate.
- [ ] Y4.5 Implement solution uniqueness under the declared rules. Evidence:
      zero-, one-, and multi-solution fixtures.
  - [ ] Y4.5.a Define what counts as materially distinct solution/hypothesis/
        culprit/mechanism/timeline versus equivalent representation. Evidence:
        equivalence rules.
  - [ ] Y4.5.b Enumerate or prove bounded count of satisfying solutions from
        reachable evidence/rules with solver version/limits. Evidence:
        cardinality tests.
  - [ ] Y4.5.c Validate exactly one when uniqueness is required, at least one/
        bounded alternatives when ambiguity is intentional, and allowed outcome
        constraints. Evidence: policy tests.
  - [ ] Y4.5.d Return representative multiple solutions and discriminating/
        missing evidence plus zero/unknown/timeout states. Evidence: fixtures.
  - [ ] Y4.5.e Distinguish true logical uniqueness from only tested path/sample
        coverage. Evidence: proof-quality tests.
- [ ] Y4.6 Implement alternative-hypothesis testing. Evidence: each plausible
      suspect is eliminated or retained by explicit evidence.
  - [ ] Y4.6.a Generate/resolve the declared plausible suspect/hypothesis set
        from exact case entities/rules without hidden arbitrary omissions.
        Evidence: hypothesis tests.
  - [ ] Y4.6.b Evaluate motive/means/opportunity/alibi/timeline/clue/canon/
        relationship/knowledge evidence for and against each hypothesis.
        Evidence: evidence matrix.
  - [ ] Y4.6.c Build explicit proof/refutation/insufficient chain with source/
        clue/inference refs, contradictions, assumptions, confidence, and
        reachable timing. Evidence: chain tests.
  - [ ] Y4.6.d Classify eliminated/retained/equivalent/unresolved/unsupported
        and require every plausible hypothesis to have a disposition. Evidence:
        coverage gate.
  - [ ] Y4.6.e Detect unjustified elimination, absence-as-proof, circular clues,
        hidden solution facts, and post-answer evidence. Evidence: adversarial
        fixtures.
- [ ] Y4.7 Implement fair-clue timing. Evidence: required evidence appears
      before the answer is demanded.
  - [ ] Y4.7.a Define answer-demand/reveal/commit points and
        required/sufficient/ disambiguating evidence sets per supported
        path/profile. Evidence: timing schema.
  - [ ] Y4.7.b Compute earliest/latest possible discovery and guaranteed
        availability for each clue under branches, state, miss/revisit,
        fallback, pacing, and accessibility. Evidence: timing tests.
  - [ ] Y4.7.c Verify required evidence is reachable/perceivable/interpretable
        before commitment with adequate review time and no future/hidden source.
        Evidence: fairness fixtures.
  - [ ] Y4.7.d Report late/missable/branch-locked/too-early-spoiler/
        inaccessible evidence and counterexample path. Evidence: result tests.
  - [ ] Y4.7.e Apply profile/difficulty policy to raw timing and require human
        fairness review for contextual salience/pacing. Evidence: gate tests.
- [ ] Y4.8 Implement suspect coverage and opportunity coherence. Evidence:
      crafted omissions and contradictions fail.
  - [ ] Y4.8.a Resolve all in-scope suspects/hypotheses and required motive/
        means/opportunity/alibi/relationship/clue/timeline dimensions. Evidence:
        coverage schema.
  - [ ] Y4.8.b Detect missing/placeholder/unsupported dimensions, omitted
        plausible suspects, overprivileged culprit evidence, and incomplete
        alternative dispositions. Evidence: omission fixtures.
  - [ ] Y4.8.c Verify opportunity intervals against presence/location/travel/
        capability/resources/access/knowledge and event timing. Evidence:
        coherence fixtures.
  - [ ] Y4.8.d Verify alibi/witness/source/canon/scene/clue consistency and
        surface contradictions/unknowns separately. Evidence: contradiction
        tests.
  - [ ] Y4.8.e Preserve intentional unknown/false alibis with evidence and apply
        fairness policy separately. Evidence: gate tests.
- [ ] Y4.9 Implement timeline consistency and travel-time constraints. Evidence:
      independently derived fixture values.
  - [ ] Y4.9.a Normalize absolute/relative/uncertain event constraints,
        duration, time zones/calendars/story era, participant/location and
        source perspective from exact revision. Evidence: input tests.
  - [ ] Y4.9.b Solve ordering/overlap/duration/age/state/knowledge/presence/
        simultaneous-action constraints and report minimal conflicts. Evidence:
        timeline fixtures.
  - [ ] Y4.9.c Resolve versioned location graph/transport/mode/speed/range/
        accessibility/transition overhead and compute minimum/expected travel
        times independently. Evidence: travel goldens.
  - [ ] Y4.9.d Detect impossible/implausible/unknown travel, teleportation,
        double presence, missed transition, and alibi inconsistencies with
        units. Evidence: edge fixtures.
  - [ ] Y4.9.e Keep author estimates, calculated values, uncertainty, policy
        thresholds, and intentional canon exceptions separate. Evidence: result
        tests.
- [ ] Y4.10 Implement scene reachability, dead-end, soft-lock, and state reset
      checks. Evidence: exhaustive small-case traversal.
  - [ ] Y4.10.a Compile typed start states, scenes/branches/conditions/effects/
        choices/inventory/knowledge/clues/checkpoints/resets/endings and
        supported profiles. Evidence: state model tests.
  - [ ] Y4.10.b Exhaustively traverse bounded cases and use bounded symbolic/
        property exploration for larger cases with declared state/path limits.
        Evidence: traversal tests.
  - [ ] Y4.10.c Detect unreachable scenes/choices/endings, terminal dead ends
        without intended ending, and orphan branches. Evidence: reachability
        fixtures.
  - [ ] Y4.10.d Detect soft locks where required progress becomes impossible
        while runtime remains active, with minimal path/state trace. Evidence:
        soft-lock fixtures.
  - [ ] Y4.10.e Verify restart/checkpoint/scene reset/branch merge/replay
        clears/ preserves exact state and cannot duplicate/lose required clues.
        Evidence: reset fixtures.
  - [ ] Y4.10.f Return exhaustive/partial/timeout/unsupported coverage and never
        label bounded exploration complete. Evidence: result tests.
- [ ] Y4.11 Implement spoiler-boundary analysis for UI labels, assets, logs,
      URLs, notifications, localization, and exports. Evidence: leak corpus.
  - [ ] Y4.11.a Classify solution/reveal/clue truth/suspect secret/canon/branch/
        protected-source fields by spoiler tier, audience, reveal condition, and
        embargo. Evidence: data map.
  - [ ] Y4.11.b Inventory author/reviewer/solver/playtester/consumer/operator
        UI/API/search/client/cache/assets/accessibility/telemetry/output
        channels. Evidence: channel manifest.
  - [ ] Y4.11.c Scan labels/tooltips/alt text/transcripts/captions/filenames/
        thumbnails/metadata/client state/bundles for direct/encoded/indirect
        leaks. Evidence: UI/asset corpus.
  - [ ] Y4.11.d Scan logs/errors/traces/URLs/deep links/notifications/email/
        webhooks/localization TM/glossary/export/debug/save files. Evidence:
        operational leak tests.
  - [ ] Y4.11.e Test role/reveal progression/back navigation/offline cache/
        locale fallback/search/count/timing/cross-tenant behavior. Evidence:
        adversarial suite.
  - [ ] Y4.11.f Fail release on unresolved spoiler exposure and preserve
        authorized evidence with redaction. Evidence: leak gate.
- [ ] Y4.12 Implement harmful-content, representation, age-rating, and
      accessibility gates. Evidence: policy/version and human-review states.
  - [ ] Y4.12.a Define separate versioned gate taxonomies/applicability/
        evaluator/rule/human evidence for harmful content, representation,
        regional age rating, and accessibility. Evidence: registries.
  - [ ] Y4.12.b Evaluate text/dialogue/scene/choice/consequence/assets/media/
        gameplay for violence/abuse/hate/sexual/self-harm/child/identity/
        manipulation and contextual uncertainty. Evidence: safety fixtures.
  - [ ] Y4.12.c Evaluate stereotype/tokenism/agency/power/cultural/linguistic/
        protected-trait portrayal and harmful mechanics as review evidence, not
        a truth score. Evidence: representation corpus.
  - [ ] Y4.12.d Map content descriptors/intensity/context/interactivity/user
        content/region/platform to rating evidence and required qualified
        review. Evidence: age-rating matrix.
  - [ ] Y4.12.e Evaluate semantic/keyboard/non-drag/color/motion/cognitive/
        captions/descriptions/audio/controls/timing alternatives for every
        content/runtime path. Evidence: accessibility matrix.
  - [ ] Y4.12.f Persist raw results/version/uncertainty/N/A separately, route
        human review/exception, and block mandatory failures. Evidence: gate
        tests.
- [ ] Y4.13 Implement study grounding and case canon gates as distinct
      registries. Evidence: no false mapping between evidence and fiction canon.
  - [ ] Y4.13.a Define separate gate identities/contracts/vocabularies/owners/
        policies/applicability for real-world source grounding and fictional
        canon consistency. Evidence: registry schemas.
  - [ ] Y4.13.b Map study claims/citations/source
        authority/currency/faithfulness/ conflict only to grounding
        measurements/verdicts. Evidence: grounding tests.
  - [ ] Y4.13.c Map case entity/assertion/timeline/knowledge/continuity/
        contradiction only to canon measurements/verdicts. Evidence: canon
        tests.
  - [ ] Y4.13.d Define explicit “fictional claim informed by source” relation
        retaining both source evidence and canon assertion without equivalence.
        Evidence: mixed-content fixtures.
  - [ ] Y4.13.e Fail cross-registry ID/enum/threshold/result substitution and
        consumer aggregation that labels canon as factual support or vice versa.
        Evidence: false-mapping tests.
- [ ] Y4.14 Persist raw solver traces and measurements separately from policy
      verdicts. Evidence: threshold/policy recomputation.
  - [ ] Y4.14.a Define immutable solver run/trace with subject revision,
        compiled model, solver/engine/config/environment versions,
        inputs/assumptions, explored states/paths/proofs/counterexamples/limits,
        and hash. Evidence: trace schema.
  - [ ] Y4.14.b Define immutable measurements for reachability/solution count/
        timing/coverage/conflicts/resources with
        units/uncertainty/applicability. Evidence: measurement schema.
  - [ ] Y4.14.c Define verdict records referencing exact traces/measurements
        plus gate/profile/policy/threshold versions. Evidence: verdict schema.
  - [ ] Y4.14.d Recompute new verdict identities on policy/threshold/profile
        change without rerunning or modifying valid raw traces. Evidence:
        recompute tests.
  - [ ] Y4.14.e Require new solver run when
        case/compiler/solver/input/assumption changes and preserve all history.
        Evidence: lifecycle tests.
- [ ] Y4.15 Add insufficient-data, timeout, unsupported, and evaluator-failure
      states. Evidence: none renders as pass.
  - [ ] Y4.15.a Define
        pass/fail/advisory/manual-review/N/A/insufficient/timeout/
        unsupported/evaluator-failure/unavailable/stale/unknown states and
        transitions. Evidence: vocabulary.
  - [ ] Y4.15.b Require typed reason/coverage/limit/missing input/dependency/
        evaluator/error/correlation/remediation/evidence per nonresult state.
        Evidence: schema tests.
  - [ ] Y4.15.c Map exceptions/timeouts/provider errors/partial solver coverage/
        invalid input to exact states without generic catch-as-pass/fail.
        Evidence: mapping tests.
  - [ ] Y4.15.d Aggregate mandatory gates so none of these states can satisfy
        review/release, and preserve valid partial measurements. Evidence:
        property tests.
  - [ ] Y4.15.e Render/export text/semantic state with no green/color-only/
        zero-value assumption. Evidence: UI/API tests.
- [ ] Y4.16 Version solver, engine, canon, evaluator, corpus, and policy.
      Evidence: every gate is reproducible or explicitly not reproducible.
  - [ ] Y4.16.a Register solver/engine/compiler/code/config/runtime/dependency
        version/hash/source/build/lifecycle/supported constructs. Evidence: tool
        registry.
  - [ ] Y4.16.b Register canon schema/namespace/predicate/entity/mapping/
        assertion-source and validation rule versions. Evidence: canon registry.
  - [ ] Y4.16.c Register evaluator/model/prompt/config/provider/training/
        calibration and corpus manifest/labels/splits/rights/coverage versions.
        Evidence: evaluator/corpus registry.
  - [ ] Y4.16.d Register gate/policy/threshold/profile/rubric/aggregation/
        manual-band versions with approvals/effective dates. Evidence: policy
        registry.
  - [ ] Y4.16.e Bind exact versions/environment/hardware/nondeterminism controls
        to every trace/measurement/verdict and verify goldens. Evidence:
        reproducibility tests.
  - [ ] Y4.16.f Label nonreproducible with exact uncontrolled dependency and
        prohibit retired/quarantined versions from new runs while retaining
        history. Evidence: lifecycle tests.

### Y5 — Production, assets, rendering, and agent orchestration

- [ ] Y5.1 Link authoring projects to existing Yemaya projects and
      organizations. Evidence: no duplicate project identity.
  - [ ] Y5.1.a Resolve exact canonical project/organization/workspace/member/
        profile IDs/versions through public owner contracts and current actor
        access. Evidence: resolver tests.
  - [ ] Y5.1.b Define authoring-track extension/link record with canonical
        project identity, selected content roots, config, and no copied owner/
        membership/status fields. Evidence: linkage schema.
  - [ ] Y5.1.c Create/open/migrate links idempotently and reject cross-tenant,
        incompatible profile, archived/deleted, or duplicate identity. Evidence:
        lifecycle tests.
  - [ ] Y5.1.d Project organization/project changes and permissions dynamically,
        preserving authoring revisions without shadow synchronization authority.
        Evidence: update tests.
  - [ ] Y5.1.e Add schema/search/import guards and end-to-end proof that one
        project appears under one stable identity across surfaces. Evidence:
        duplication gate.
- [ ] Y5.2 Link characters, locations, scenes, clues, and study materials to
      asset-library records. Evidence: stable lineage.
  - [ ] Y5.2.a Define typed entity-to-asset-revision link roles for portrait/
        reference/model/texture/audio/video/scene/proxy/render/evidence and
        ordering/primary status. Evidence: link schema.
  - [ ] Y5.2.b Select only authorized exact asset revisions compatible with
        entity kind/project/profile/locale/target/status. Evidence: selector
        tests.
  - [ ] Y5.2.c Persist forward/reverse links with source/generation/import/
        transformation provenance, rights/consent/canon/continuity refs and
        author decision. Evidence: lineage tests.
  - [ ] Y5.2.d Version link add/remove/reorder/replace without changing asset or
        entity history and compute downstream scene/build impacts. Evidence:
        lifecycle tests.
  - [ ] Y5.2.e Propagate asset supersession/quarantine/recall/rights expiry and
        entity/canon revision to eligibility/freshness. Evidence: propagation
        tests.
- [ ] Y5.3 Build rights/provenance status into asset selection. Evidence:
      ineligible assets cannot enter a release candidate.
  - [ ] Y5.3.a Define asset-selector projection for owner/source/license/use/
        adaptation/training/distribution/attribution/territory/term/consent/
        provenance/gates/target and exact versions. Evidence: schema.
  - [ ] Y5.3.b Derive eligible/review/expired/revoked/incompatible/incomplete/
        quarantined/unknown per project use/audience/locale/target rather than a
        generic asset approval. Evidence: policy tests.
  - [ ] Y5.3.c Present status/reasons/evidence/freshness/required attribution/
        restrictions without exposing protected source/identity fields.
        Evidence: UI tests.
  - [ ] Y5.3.d Revalidate on attach, candidate lock, build, and release; reject
        direct foreign/stale asset IDs server-side. Evidence: TOCTOU tests.
  - [ ] Y5.3.e Scan every release-candidate assembly path and prove ineligible
        assets/derivatives cannot enter. Evidence: release boundary suite.
- [ ] Y5.4 Build brief-to-Isis generation handoff for approved assets. Evidence:
      project/entity context and return lineage.
  - [ ] Y5.4.a Define handoff request for exact project/entity/scene/shot/
        continuity/canon/source/asset revisions, approved asset kind/operation,
        audience, return intent, expiry, nonce, and signature. Evidence: schema.
  - [ ] Y5.4.b Validate project/actor/use/right/consent/canon/source/target and
        minimize authorized context before signed handoff. Evidence: issuance
        tests.
  - [ ] Y5.4.c Reauthorize in Isis, resolve canonical inputs, show plan/cost/
        capability/substitution preview, and create governed run. Evidence:
        acceptance tests.
  - [ ] Y5.4.d Return generated proposal/output revision with
        run/model/workflow/ input/rights/provenance/gates and immutable source
        context linkage. Evidence: return tests.
  - [ ] Y5.4.e Require Yemaya accept/reject/link decision; no automatic asset/
        canon/scene promotion. Evidence: authority tests.
  - [ ] Y5.4.f Test failure/cancel/multiple output/stale context/expired link/
        cross-tenant/back navigation. Evidence: handoff E2E.
- [ ] Y5.5 Build dailies review for rendered case scenes and study media.
      Evidence: timecode/frame annotations survive revisions.
  - [ ] Y5.5.a Define dailies session/item/render revision/shot-scene-material/
        participant/reviewer/status/timebase/color/audio/rights projection.
        Evidence: schema.
  - [ ] Y5.5.b Build permission-filtered list/detail/playlist with synchronized
        image/video/audio/3D playback, color proxy, versions, gates, source/
        lineage, and live/partial states. Evidence: viewer tests.
  - [ ] Y5.5.c Create typed frame/timecode/range/region/object/track anchored
        comments, drawings/notes, replies, mentions, resolve/reopen, and
        accessible anchor list. Evidence: annotation tests.
  - [ ] Y5.5.d Map anchors across rerender/trim/rate/crop/replacement as exact/
        shifted/ambiguous/orphaned and retain original evidence. Evidence:
        survival tests.
  - [ ] Y5.5.e Record approve/reject/changes/hold/compare/promote decisions
        against exact render revision with stale/concurrency/role checks.
        Evidence: review tests.
  - [ ] Y5.5.f Run case scene and study media render-to-notes-to-rerender
        journey with accessibility/responsive/tenant proof. Evidence: E2E.
- [ ] Y5.6 Build render and compilation job views with dependency, capacity,
      cost, progress, cancellation, and failure detail. Evidence: live fault
      journey.
  - [ ] Y5.6.a Define audience-safe projections for request/plan/DAG/job/stage/
        attempt/worker-provider/reservation/budget/cost/output/error/events.
        Evidence: view contracts.
  - [ ] Y5.6.b Build job list/detail with stable filters/URLs, dependency graph,
        queue/capacity/reservations, inputs/versions, and bounded live updates.
        Evidence: UI tests.
  - [ ] Y5.6.c Present estimate/reserved/actual
        compute/storage/egress/time/cost, pending/unbilled/variance and
        attribution by stage/artifact. Evidence: cost tests.
  - [ ] Y5.6.d Present typed progress/checkpoints/logs/outputs/gates/refusal/
        policy/capacity/timeout/malformed/worker-loss/internal failures with
        redaction. Evidence: failure view tests.
  - [ ] Y5.6.e Implement authorized cancel/retry/replay/redirect with state/
        impact/cost/reason/confirmation and immutable attempt/run lineage.
        Evidence: action tests.
  - [ ] Y5.6.f Run live injected dependency/provider/worker/storage failure,
        reconnect/recovery/late output/tenant/accessibility journey. Evidence:
        Playwright fault E2E.
- [ ] Y5.7 Build production verification before asset promotion. Evidence:
      checksum, technical, rights, and content gates.
  - [ ] Y5.7.a Resolve exact source/output/asset/proxy/rendition/dependency/
        target/profile revisions and required verification registry. Evidence:
        plan tests.
  - [ ] Y5.7.b Verify bytes/manifest/dependencies/checksums/signatures/content
        type/storage integrity and quarantine corruption. Evidence: integrity
        tests.
  - [ ] Y5.7.c Run asset-kind/target technical format/dimensions/color/audio/
        video/3D/engine/DCC/performance gates. Evidence: technical fixtures.
  - [ ] Y5.7.d Verify source/license/consent/attribution/provenance/watermark/
        canon/continuity and content/safety/age/accessibility gates. Evidence:
        trust fixtures.
  - [ ] Y5.7.e Bind raw measurements/verdicts/freshness/overrides/reviews to
        exact candidate and block promotion on missing/stale/failed mandatory
        gate. Evidence: promotion tests.
  - [ ] Y5.7.f Promote as a new asset status/revision with decision/audit and
        revalidate on downstream release. Evidence: lifecycle tests.
- [ ] Y5.8 Build project-Obsidian or external-project import/export only through
      versioned safe adapters. Evidence: path, conflict, and round-trip tests.
  - [ ] Y5.8.a Define adapter interface/version/capabilities/source-target
        profile, manifest/mapping/loss/diagnostic/security contracts and
        approved adapter registry. Evidence: adapter schema.
  - [ ] Y5.8.b Parse imports in quarantine with archive/path/symlink/absolute/
        traversal/hidden/script/plugin/size/depth/version protections. Evidence:
        hostile path tests.
  - [ ] Y5.8.c Map project/entities/tree/graph/timeline/notes/assets/links/
        metadata/locales/IDs/versions to canonical contracts with source
        provenance. Evidence: mapping tests.
  - [ ] Y5.8.d Preview create/update/merge/skip/conflict/duplicate/loss/
        unsupported/external dependency and require authorized resolution.
        Evidence: conflict tests.
  - [ ] Y5.8.e Export deterministic safe package with exact revisions, relative
        paths, manifest/hashes/standards metadata/rights and no secrets/local
        paths. Evidence: export tests.
  - [ ] Y5.8.f Round-trip every supported adapter/version/edge profile and
        reject unknown/unsupported versions explicitly. Evidence: adapter suite.
- [ ] Y5.9 Build writers-room/director agent run plans with tool grants,
      budgets, sources, canon snapshot, and stop conditions. Evidence: plan
      validation.
  - [ ] Y5.9.a Define distinct writers-room/director plan/step/output contracts
        for ideation/dialogue/structure/continuity/production planning.
        Evidence: schemas.
  - [ ] Y5.9.b Bind exact project/premise/case/scenes/sources/citations/canon/
        continuity/assets/profile/policy revisions and spoiler compartments.
        Evidence: input tests.
  - [ ] Y5.9.c Resolve allowlisted versioned tool/model/provider grants by
        operation/data/project/tenant/network/resource/output and expiry.
        Evidence: grant tests.
  - [ ] Y5.9.d Allocate per-step/total token/tool/compute/cost/time budgets,
        dependencies/retries/approvals and hard stops for safety/canon/source/
        budget/timeout/verification. Evidence: budget/stop tests.
  - [ ] Y5.9.e Validate DAG/types/grants/inputs/freshness/policy/budget/
        destinations before dispatch; reject injected/arbitrary plans. Evidence:
        zero-dispatch tests.
  - [ ] Y5.9.f Ensure plans grant no maturity/canon/approval/promotion/publish/
        budget exception authority. Evidence: authority tests.
- [ ] Y5.10 Build run/queue/budget/provenance views scoped to authoring project.
      Evidence: cross-project access test.
  - [ ] Y5.10.a Define project-filtered
        request/plan/step/queue/run/attempt/tool/
        event/budget/evidence/output/decision/lineage projections. Evidence:
        contracts.
  - [ ] Y5.10.b Build list/detail/DAG/timeline/live reconnect with exact
        versions, queue/reservation/status/stops/approvals/failures. Evidence:
        run UI tests.
  - [ ] Y5.10.c Present tool purpose/status/timing and redacted inputs/outputs,
        allocated/consumed/remaining budgets,
        sources/canon/provenance/proposals. Evidence: detail tests.
  - [ ] Y5.10.d Enforce
        tenant/organization/project/role/spoiler/object/property/ function
        authorization on query/search/count/URL/stream/export/action. Evidence:
        cross-project tests.
  - [ ] Y5.10.e Verify long-run performance, accessibility/responsive modes and
        project switch/back/role revoke/cache behavior. Evidence: E2E.
- [ ] Y5.11 Build human-override pause, redirect, accept, reject, and abort.
      Evidence: overrides are audited and cannot rewrite history.
  - [ ] Y5.11.a Define separate command eligibility/role/scope/reason/evidence/
        idempotency/expected version/outcome and human-only operations.
        Evidence: schemas.
  - [ ] Y5.11.b Pause at safe boundary/checkpoint and resume only after full
        source/canon/grant/policy/budget/dependency revalidation. Evidence:
        pause tests.
  - [ ] Y5.11.c Redirect through a new plan revision with changed objective/
        steps/tools/inputs/budget and preserved completed outputs/history.
        Evidence: redirect tests.
  - [ ] Y5.11.d Accept/reject exact attributed proposals into review/new
        authoring revisions without approving canon/gates/maturity/publish.
        Evidence: decision tests.
  - [ ] Y5.11.e Abort/cancel queued/running/downstream calls with late-result
        fencing, partial evidence/cost, resource cleanup, and explicit outcome.
        Evidence: abort race tests.
  - [ ] Y5.11.f Record append-only commands/events/actor/authority/plan before-
        after links and prohibit state/history rewriting. Evidence: audit tests.
- [ ] Y5.12 Build agent-output intake as proposals. Evidence: no agent directly
      advances maturity, approves gates, or publishes.
  - [ ] Y5.12.a Define signed/hashed result envelope with agent/run/step/tool/
        model/config/input versions, output kind/schema, evidence/sources/canon,
        confidence/cost/status. Evidence: schema.
  - [ ] Y5.12.b Validate signature/hash/schema/size/type/expected
        output/project/ tenant/source locators and scan files/content. Evidence:
        hostile tests.
  - [ ] Y5.12.c Reject/redact forbidden secrets/protected sources/solutions/
        unrelated project data and unsupported canon/approval claims. Evidence:
        property tests.
  - [ ] Y5.12.d Store text/structure/dialogue/asset/production/review results
        only as attributed proposals with exact authoring target linkage.
        Evidence: persistence tests.
  - [ ] Y5.12.e Require human inspect/accept/edit/reject/defer; acceptance
        creates a new revision and reruns applicable gates. Evidence: review
        tests.
  - [ ] Y5.12.f Prove no payload/property/event can advance maturity, resolve
        canon, pass/waive gates, promote assets, approve budget, or publish.
        Evidence: authority suite.
- [ ] Y5.13 Build cost and capacity estimates for writing, generation,
      rendering, compilation, localization, and review. Evidence: planned versus
      actual reconciliation.
  - [ ] Y5.13.a Define versioned price/rate/productivity/resource/capacity
        models and units for
        labor/tool/provider/compute/storage/egress/vendor/reviewer work.
        Evidence: model schemas.
  - [ ] Y5.13.b Estimate writing/editorial/research/canon/solver/review labor by
        volume/complexity/locale/risk/role/rates/availability. Evidence: labor
        fixtures.
  - [ ] Y5.13.c Estimate generation/render/compilation/postproduction/storage/
        egress by
        plan/variants/assets/passes/targets/provider/resources/retries.
        Evidence: technical fixtures.
  - [ ] Y5.13.d Estimate localization/translation/media adaptation/locale
        review/ rendition/build and named reviewer capacity/schedule. Evidence:
        locale fixtures.
  - [ ] Y5.13.e Return low/expected/high, confidence/unknowns/assumptions/
        bottlenecks/calendars/currencies/rate versions and threshold
        confirmation. Evidence: estimate tests.
  - [ ] Y5.13.f Ingest actual labor/jobs/providers/storage/reviews, allocate by
        project/stage/artifact/locale/target, and report pending/unbilled/
        variance/cause without double count. Evidence: reconciliation tests.
- [ ] Y5.14 Build web/desktop background-job handoff and reconnect. Evidence:
      closing a client never corrupts server job state.
  - [ ] Y5.14.a Define server-owned durable job identity/state/event cursor/
        subscription/cancellation/action contracts independent of client
        process/window/session. Evidence: job contract.
  - [ ] Y5.14.b Submit idempotently with exact project/input/plan/version and
        return durable job ref before client background tracking. Evidence:
        submission tests.
  - [ ] Y5.14.c Handoff between web/desktop/window/process/device by signed
        project-scoped link/notification and current reauthorization. Evidence:
        handoff tests.
  - [ ] Y5.14.d Reconnect from last cursor/snapshot with dedup/out-of-order/gap/
        terminal handling; refresh expired credentials without resubmission.
        Evidence: reconnect tests.
  - [ ] Y5.14.e Close/crash/update/offline/reopen clients during queued/running/
        callback/finalizing and prove job/checkpoints/outputs unaffected.
        Evidence: lifecycle tests.
  - [ ] Y5.14.f Make cancel/retry/replay explicit server commands and never
        infer cancellation from client disconnect. Evidence: authority tests.

### Y5A — Existing AAA production-suite feature completion

- [ ] Y5A.1 Complete project creation, template, workspace, membership,
      favorites/recent, duplicate, import/export, archive, restore, retention,
      and project-type migration. Evidence: every supported project profile has
      a durable lifecycle journey and no hidden local-only state.
  - [ ] Y5A.1.a Build typed project creation for profile/type/template,
        organization/workspace, title/code/locale/timezone/units/color, owner,
        classification/retention, targets, and validation. Evidence: creation
        tests.
  - [ ] Y5A.1.b Build versioned project templates with inheritance/defaults/
        required entities/pipeline/roles/gates/folders/config, preview, review,
        code floors, and lifecycle. Evidence: template tests.
  - [ ] Y5A.1.c Build project workspace configuration for navigation/layout/
        saved views/default tools/capabilities/integrations without local-only
        canonical state. Evidence: workspace sync tests.
  - [ ] Y5A.1.d Build owner/member/team/service-account/guest invite/accept/
        revoke/role/scope/expiry/conflict and project leave/transfer workflows.
        Evidence: membership tests.
  - [ ] Y5A.1.e Build per-user favorite/unfavorite/reorder and derived recent/
        last-opened state across web/desktop/devices with privacy/retention.
        Evidence: favorites/recent tests.
  - [ ] Y5A.1.f Duplicate/fork exact project revision with selective content/
        assets/config/history/member/link options, new IDs, rights/canon/source
        checks, progress, and provenance. Evidence: duplication tests.
  - [ ] Y5A.1.g Import through approved versioned adapters with quarantine,
        preview/conflicts/mappings/loss/authorization/idempotency and source
        package retention. Evidence: import tests.
  - [ ] Y5A.1.h Export deterministic project package with exact revisions/
        manifests/dependencies/hashes/rights/redactions and no local paths/
        secrets, then round-trip. Evidence: export tests.
  - [ ] Y5A.1.i Archive project from normal discovery/mutation with active job/
        lock/build/release/dependency/member impact, and preserve authorized
        read/history. Evidence: archive tests.
  - [ ] Y5A.1.j Restore archive as explicit state transition after policy/
        retention/dependency/capability checks without resurrecting revoked
        rights/secrets/jobs. Evidence: restore tests.
  - [ ] Y5A.1.k Enforce retention/deletion/legal hold/export/backup/local-cache
        schedules and verified downstream cleanup/irrecoverable copies.
        Evidence: retention tests.
  - [ ] Y5A.1.l Migrate project type/profile through versioned impact preview,
        entity/pipeline/gate/asset/role/target mappings, conflicts, new
        revision, rollback, and no silent defaults. Evidence: migration tests.
  - [ ] Y5A.1.m Run durable create-to-archive/restore/migrate journey for every
        supported profile across web/desktop/restart/two tenants. Evidence:
        lifecycle matrix.
- [ ] Y5A.2 Complete the asset library across the ratified asset-type registry,
      including upload/capture/generation/import, folders/collections, tags,
      search, exact/perceptual dedupe, versions, relationships, dependencies,
      rights, provenance, proxy/rendition, approval, promotion, quarantine,
      archive, and restore. Evidence: per-asset-kind capability and gate matrix.
  - [ ] Y5A.2.a Define ratified asset-kind/version registry with schemas/
        formats/roles/technical metadata/preview/editor/gates/targets/rights/
        provenance and explicit unsupported cells. Evidence: registry.
  - [ ] Y5A.2.b Implement upload with streaming hash/sniff/quarantine/scan/
        dedupe/metadata/rights/progress/cancel/resume and asset revision
        creation. Evidence: upload tests.
  - [ ] Y5A.2.c Implement governed device/file/desktop capture with permission,
        calibration/timecode/color/audio, consent, local temp security, ingest,
        and failure cleanup. Evidence: capture tests.
  - [ ] Y5A.2.d Implement Isis generation handoff/return and manual/external
        import with exact provenance, safe adapters, quarantine, and proposal
        acceptance. Evidence: generation/import tests.
  - [ ] Y5A.2.e Implement versioned folder hierarchy and
        collection/static-smart/ ordered membership with cycle, permissions,
        bulk move, and no ownership ambiguity. Evidence: organization tests.
  - [ ] Y5A.2.f Implement governed tag/taxonomy/alias/locale authoring and
        permission-safe search/filter/facet/sort/pagination/autocomplete.
        Evidence: metadata/search tests.
  - [ ] Y5A.2.g Implement exact cryptographic/content duplicate and calibrated
        modality-specific perceptual near-duplicate detection with candidate
        evidence/uncertainty and human merge/keep decisions. Evidence: dedupe
        tests.
  - [ ] Y5A.2.h Implement immutable logical/revision history, semantic/media
        diff, compare/select/supersede/restore and current projection without
        overwrites. Evidence: version tests.
  - [ ] Y5A.2.i Implement typed asset-to-entity/asset/dependency/ingredient/
        derivative/rendition/variant/continuity/canon/production relationships
        with impact queries. Evidence: relationship tests.
  - [ ] Y5A.2.j Implement rights/license/consent/attribution/embargo/provenance/
        source/generation/model/workflow/watermark/C2PA status and blast radius.
        Evidence: trust tests.
  - [ ] Y5A.2.k Implement
        original/proxy/thumbnail/streaming/edit/review/delivery/
        locale/LOD/mip/stem/rendition manifests, jobs, hashes, compatibility,
        and source linkage. Evidence: rendition tests.
  - [ ] Y5A.2.l Implement asset-kind/target quality/safety/rights/canon/
        continuity/accessibility/technical review, decisions, and stale
        evidence. Evidence: approval tests.
  - [ ] Y5A.2.m Implement promote/link/use only for eligible exact revisions and
        revalidate in build/release; reject direct stale IDs. Evidence:
        promotion tests.
  - [ ] Y5A.2.n Implement quarantine/recall with admission block/dependency/
        project/build/release propagation and restricted evidence. Evidence:
        quarantine tests.
  - [ ] Y5A.2.o Implement archive/restore/retention/delete/legal hold while
        preserving history/lineage and rejecting active dependencies where
        required. Evidence: lifecycle tests.
  - [ ] Y5A.2.p Run per-asset-kind source-to-approved-to-target journey and fail
        every registry cell without current capability/gate proof. Evidence:
        matrix gate.
- [ ] Y5A.3 Complete screenplay authoring for structured scenes, slug lines,
      action, dialogue, characters, revisions, colored pages, locks, notes,
      breakdowns, reports, compare, table read, and interchange. Evidence:
      production-script golden and collaborative editing journey.
  - [ ] Y5A.3.a Define screenplay/project/logical/revision/page/scene/block/
        character/dialogue/production metadata contracts and supported
        interchange profiles. Evidence: schemas.
  - [ ] Y5A.3.b Build structured scene/act/sequence order and slug line INT/EXT/
        location/time/continuity/special qualifier editor with stable IDs.
        Evidence: structure tests.
  - [ ] Y5A.3.c Build typed action/description/transition/shot/text/lyrics/
        centered/page-break and format rules with keyboard-first editing.
        Evidence: action block tests.
  - [ ] Y5A.3.d Build character cues/dialogue/parentheticals/dual dialogue/
        extensions/voiceover/offscreen/language/pronunciation and canonical
        character links. Evidence: dialogue tests.
  - [ ] Y5A.3.e Implement pagination/layout/font/margins/widow-orphan/continued/
        page numbering/title pages and accessible semantic alternative.
        Evidence: render goldens.
  - [ ] Y5A.3.f Implement revisions/sets/colored pages/date/version marks/
        changed-line indicators and compare/diff/report without changing content
        identity. Evidence: revision tests.
  - [ ] Y5A.3.g Implement scene/page/line locks, production revisions, omitted/
        added scenes, numbering/revisions, conflict/override authority and
        audit. Evidence: lock tests.
  - [ ] Y5A.3.h Implement anchored notes/comments/suggestions/assignments/
        mentions/resolve and stable anchor migration. Evidence: note tests.
  - [ ] Y5A.3.i Build breakdown categories/items/characters/props/wardrobe/
        locations/VFX/SFX/stunts/extras/safety with entity/asset/task links.
        Evidence: breakdown tests.
  - [ ] Y5A.3.j Build scene/character/dialogue/location/page/revision/breakdown/
        duration/cast/day-night reports with permission-safe exports. Evidence:
        report tests.
  - [ ] Y5A.3.k Build semantic side-by-side compare/merge and consented
        table-read cast/voice/timing/missing-lines/captions/script output.
        Evidence: compare/table-read tests.
  - [ ] Y5A.3.l Implement Final Draft/FDX and other ratified import/export with
        source retention, mapping/loss diagnostics, deterministic round-trip,
        and no answer/spoiler leak. Evidence: interchange tests.
  - [ ] Y5A.3.m Run collaborative two-editor/offline/reconnect/lock/revision/
        conflict/restore production-script journey and compare golden. Evidence:
        E2E.
- [ ] Y5A.4 Complete storyboard and animatic authoring for panels, shots,
      framing, lens/camera, timing, dialogue/audio, continuity, versions,
      generated assets, sequence playback, exports, and production links.
      Evidence: script→storyboard→animatic lineage and round-trip.
  - [ ] Y5A.4.a Define storyboard/sequence/shot/panel/version/timeline/track/
        media/annotation contracts with exact script/scene/beat links. Evidence:
        schemas.
  - [ ] Y5A.4.b Build panel create/import/draw/generate/replace/crop/annotate/
        caption/action/dialogue/shot-number/order with asset provenance.
        Evidence: panel tests.
  - [ ] Y5A.4.c Build stable shot identity/scene/sequence/order/type/coverage/
        transition/status/owner/production links and conflict-safe reorder.
        Evidence: shot tests.
  - [ ] Y5A.4.d Author composition/framing/aspect/safe area/subject placement/
        eyeline/screen direction/axis/depth/scale and visual annotations.
        Evidence: framing tests.
  - [ ] Y5A.4.e Author camera/lens/sensor/focal/aperture/focus/height/distance/
        pose/movement/path/shake in explicit units with preview. Evidence:
        camera tests.
  - [ ] Y5A.4.f Author frame/timecode/seconds duration/handles/rate/transition/
        retime and dialogue/SFX/music/ambience/guide audio tracks with sync.
        Evidence: timing/audio tests.
  - [ ] Y5A.4.g Apply
        character/wardrobe/prop/location/light/palette/lens/camera/ motion/canon
        continuity profiles and drift/conflict annotations. Evidence: continuity
        tests.
  - [ ] Y5A.4.h Version/diff/branch/merge/restore panels/shots/timeline/tracks
        and preserve stable unaffected identities. Evidence: revision tests.
  - [ ] Y5A.4.i Attach generated panels/audio through governed Isis/Euterpe
        handoffs with run/input/model/workflow/rights/provenance and human
        acceptance. Evidence: generation tests.
  - [ ] Y5A.4.j Build synchronized sequence animatic playback/scrub/loop/
        thumbnails/waveforms/captions/comments/comparison with accessible lists.
        Evidence: playback tests.
  - [ ] Y5A.4.k Export image boards/PDF/video/editorial EDL/XML/AAF or ratified
        profiles with stable IDs/timebases/media/metadata/loss reports and
        round-trip. Evidence: export tests.
  - [ ] Y5A.4.l Link shots to schedule/tasks/assets/renders/dailies/editorial/
        engine/DCC and propagate script/panel/timing/continuity changes.
        Evidence: production linkage tests.
  - [ ] Y5A.4.m Run script-scene-to-board-to-generated-panel-to-animatic-to-
        editorial round-trip with exact lineage. Evidence: E2E.
- [ ] Y5A.5 Complete character, creature, prop, world, location, environment,
      lore, relationship, visual-development, reference, canon, and
      continuity-design workspaces. Evidence: source and generated assets remain
      linked to approved entity revisions and downstream scenes.
  - [ ] Y5A.5.a Define versioned entity-kind registry/common identity/locale/
        source/canon/asset/continuity/production fields and per-kind schemas.
        Evidence: registry.
  - [ ] Y5A.5.b Build character/creature anatomy/appearance/identity/age/
        personality/goals/arc/voice/performance/wardrobe/rig/consent authoring.
        Evidence: character tests.
  - [ ] Y5A.5.c Build prop/item identity/function/dimensions/materials/state/
        ownership/interaction/handedness/variants/continuity authoring.
        Evidence: prop tests.
  - [ ] Y5A.5.d Build world/setting geography/history/cultures/politics/economy/
        technology/rules/time/canon/regions and hierarchy authoring. Evidence:
        world tests.
  - [ ] Y5A.5.e Build location/environment layout/scale/architecture/biome/
        weather/time/light/set-dressing/accessibility/production constraints.
        Evidence: location tests.
  - [ ] Y5A.5.f Build lore assertion/event/faction/object/ritual/language/
        chronology/source/authority/contradiction/retcon/continuity authoring.
        Evidence: lore tests.
  - [ ] Y5A.5.g Build directed relationship
        type/history/strength/state/knowledge/ conflict/secret/temporal/canon
        and accessible graph/table views. Evidence: relationship tests.
  - [ ] Y5A.5.h Build visual-development brief/mood/palette/silhouette/shape/
        material/scale/style/reference explorations/variants/decisions.
        Evidence: visual development tests.
  - [ ] Y5A.5.i Build source/reference ingest/boards/roles/crop/annotation/
        rights/attribution/consent/authority/avoidance/conditioning links.
        Evidence: reference tests.
  - [ ] Y5A.5.j Build canon assertion/conflict/source/decision/status/namespace/
        temporal review and blast-radius within each entity workspace. Evidence:
        canon tests.
  - [ ] Y5A.5.k Build continuity profiles/revisions for identity/appearance/
        scale/state/material/wardrobe/props/environment/lore/relationships
        across scenes/shots/assets. Evidence: continuity tests.
  - [ ] Y5A.5.l Attach source/manual/generated asset revisions with role/
        provenance/rights/gates/decision, compare/select/promote, and no auto-
        canon. Evidence: asset linkage tests.
  - [ ] Y5A.5.m Link approved exact entity revisions to script/scenes/shots/
        gameplay/NPCs/schedule/tasks/builds and propagate changes/stale state.
        Evidence: downstream linkage tests.
  - [ ] Y5A.5.n Run representative per-entity
        source-to-design-to-approved-asset- to-scene journey with
        graph/accessibility/tenant proof. Evidence: E2E.
- [ ] Y5A.6 Complete production scheduling for breakdown items, shots/tasks,
      dependencies, milestones, calendars, call sheets, locations, crew,
      availability, time zones, schedule optimization/sharing, conflicts, and
      actuals. Evidence: schedule-to-call-sheet and change-impact journeys.
  - [ ] Y5A.6.a Define schedule/project/version/task/shot/breakdown/resource/
        dependency/milestone/calendar/booking/call-sheet/actual contracts and
        stable IDs. Evidence: schemas.
  - [ ] Y5A.6.b Convert approved script/entity/scene/shot/breakdown revisions
        into estimable schedulable items with
        quantities/durations/skills/resources/ locations/constraints and change
        links. Evidence: breakdown tests.
  - [ ] Y5A.6.c Build task/shot create/assign/status/priority/estimate/duration/
        dates/owner/crew/resource/budget/asset/gate/version and bulk operations.
        Evidence: task tests.
  - [ ] Y5A.6.d Build finish-start/start-start/finish-finish/lag/lead/required/
        soft dependency DAG, cycle/reachability/critical-path checks, and
        impact. Evidence: dependency tests.
  - [ ] Y5A.6.e Build milestones/deliverables/reviews/release windows/embargo/
        approval criteria and slip/escalation behavior. Evidence: milestone
        tests.
  - [ ] Y5A.6.f Build production/institution/crew/location/stage/device/provider
        calendars, work rules/holidays/quiet hours/recurrence/availability/
        holds/bookings in IANA time zones. Evidence: calendar tests.
  - [ ] Y5A.6.g Build location/permit/weather/travel/setup/strike/capacity/
        accessibility/safety constraints and crew role/rate/union/rest/
        qualification/consent/contact projections. Evidence: resource tests.
  - [ ] Y5A.6.h Detect double-booking, impossible dependencies, insufficient
        turnaround/rest/travel/resources/capacity/skills, milestone misses, and
        timezone/DST conflicts. Evidence: conflict tests.
  - [ ] Y5A.6.i Build deterministic optimization suggestions for critical path,
        resource leveling, grouping/location moves, cost/quality/risk tradeoffs
        with assumptions and no automatic apply. Evidence: optimization tests.
  - [ ] Y5A.6.j Build schedule
        version/diff/what-if/approval/publish/share/export/ subscriber access
        with role redaction and stable links. Evidence: schedule lifecycle
        tests.
  - [ ] Y5A.6.k Generate versioned call sheets from committed scene/shot/
        location/crew/cast/equipment/safety/weather/travel/contact/access/
        schedule refs with acknowledgements. Evidence: call-sheet tests.
  - [ ] Y5A.6.l Capture start/end/progress/completion/actual labor/resource/
        incident/cost/delay reasons separately from plan and reconcile.
        Evidence: actuals tests.
  - [ ] Y5A.6.m Run script change→breakdown impact→optimized committed schedule→
        call sheet→actuals/replan journey across zones. Evidence: E2E.
- [ ] Y5A.7 Complete budgeting for templates, line items, versions,
      multi-currency/rates, estimates, approvals, commitments, actuals,
      variance, cost prediction, contingency, and audit. Evidence: exact
      arithmetic, rate-version, permission, and reconciliation tests.
  - [ ] Y5A.7.a Define budget/template/version/category/account/line-item/rate/
        estimate/approval/commitment/actual/contingency/forecast/audit contracts
        with currencies/units/tax/time. Evidence: schemas.
  - [ ] Y5A.7.b Build versioned budget templates/mappings/formulas/required
        categories/assumptions/floors by project profile/region and inheritance.
        Evidence: template tests.
  - [ ] Y5A.7.c Build line items for quantity/unit/rate/currency/tax/markup/
        discount/vendor/crew/resource/task/asset/shot/date/cost center/funding
        with decimal arithmetic. Evidence: line-item tests.
  - [ ] Y5A.7.d Build immutable exchange/pay/union/provider/vendor/tax rate
        versions with source/effective period/rounding and exact consumed rate.
        Evidence: rate tests.
  - [ ] Y5A.7.e Build low/expected/high estimates/scenarios/quotes/assumptions/
        confidence, import, compare, freeze/baseline, and budget revision diff.
        Evidence: estimate tests.
  - [ ] Y5A.7.f Build submit/review/request-changes/approve/reject with
        thresholds, role/quorum/separation, exact version, evidence, conditions,
        and expiry. Evidence: approval tests.
  - [ ] Y5A.7.g Build purchase order/contract/booking/reservation/commitment,
        encumbrance, status, vendor, approval, cancellation, and remaining
        available balance. Evidence: commitment tests.
  - [ ] Y5A.7.h Ingest verified invoice/timecard/provider/job/storage/expense/
        payment actuals with source/idempotency/allocation/status/corrections.
        Evidence: actual tests.
  - [ ] Y5A.7.i Compute planned/baseline/current/committed/actual/forecast/
        remaining variance by amount/percentage/currency/category/task/period
        with typed cause. Evidence: variance goldens.
  - [ ] Y5A.7.j Build cost prediction from versioned measurements/model/
        assumptions/uncertainty and compare champion/challenger; never overwrite
        estimates. Evidence: prediction tests.
  - [ ] Y5A.7.k Build contingency reserve/allowance/risk trigger/draw/replenish/
        approval/release and prevent hidden negative budget. Evidence:
        contingency tests.
  - [ ] Y5A.7.l Enforce project/org/role/property/spend-limit/separation/
        concurrency/idempotency and immutable correction/audit/export. Evidence:
        permission/audit tests.
  - [ ] Y5A.7.m Reconcile budget/commitments/actuals/provider bills/currency/
        refunds/credits and run exact arithmetic/rate-version journey. Evidence:
        reconciliation E2E.
- [ ] Y5A.8 Complete dailies, editorial, timing, cleanup, object removal,
      relighting/lighting, HDR trim, localization, foley, score, portrait,
      motion-camera, AV narrative, and video-edit workspaces by integrating
      existing pages and libraries. Evidence: each page has live backing,
      immutable review media, per-frame/timecode notes, gates, and handoff.
  - [ ] Y5A.8.a Inventory every existing page/library/route/API/store/job/
        capability/owner/live-fixture state and assign reuse/refactor/merge/
        retire/gap disposition. Evidence: workspace ledger.
  - [ ] Y5A.8.b Define shared exact project/sequence/shot/asset/source/render/
        edit/timeline/track/version/timebase/color/audio/review/job/gate/handoff
        contracts. Evidence: shared schemas.
  - [ ] Y5A.8.c Complete dailies ingest/playlist/proxy/color/audio/playback/
        compare/frame-time notes/decisions/rerender/promotion. Evidence: dailies
        journey.
  - [ ] Y5A.8.d Complete editorial sequence/track/clip/source-timecode/record-
        timecode/trim/transition/retime/multicam/audio/captions/version/conform/
        interchange. Evidence: editorial journey.
  - [ ] Y5A.8.e Complete animation/action/audio timing/exposure/beat/marker/
        duration/rate/sync/retime comparison and accessible timeline/list.
        Evidence: timing journey.
  - [ ] Y5A.8.f Complete cleanup/paint/roto/mask/stabilize/denoise/artifact/
        beauty/fix layers with source frames, versioned patches, preview, gates,
        and revert. Evidence: cleanup journey.
  - [ ] Y5A.8.g Complete object-removal
        target/mask/tracking/occlusion/background/ temporal
        consistency/generation provenance/review and non-destructive
        replacement. Evidence: removal journey.
  - [ ] Y5A.8.h Complete relighting/lighting source/environment/light/camera/
        geometry/material/exposure/shadow/reflection/render-pass/HDR/color
        workflows. Evidence: lighting journey.
  - [ ] Y5A.8.i Complete HDR trim target display/profile/master/shot controls/
        metadata/measurement/compare/version/signoff/export. Evidence: HDR
        journey.
  - [ ] Y5A.8.j Complete localization script/dialogue/text/captions/dub/voice/
        timing/lip-sync/graphics/locale review/rendition/package parity.
        Evidence: localization journey.
  - [ ] Y5A.8.k Complete foley cue/object/surface/action/performer/take/timing/
        sync/edit/mix/rights/asset handoff and review. Evidence: foley journey.
  - [ ] Y5A.8.l Complete score spotting/cue/tempo/meter/key/hit points/stems/
        mockup/recording/license/timeline/mix/delivery through Euterpe seams.
        Evidence: score journey.
  - [ ] Y5A.8.m Complete portrait identity/consent/reference/selection/retouch/
        crop/color/background/renditions/rights/quality/promotion. Evidence:
        portrait journey.
  - [ ] Y5A.8.n Complete motion-camera tracking/solve/path/lens/distortion/
        coordinates/scale/confidence/cleanup/preview/DCC-engine export.
        Evidence: motion-camera journey.
  - [ ] Y5A.8.o Complete AV narrative script/story/sequence/picture/dialogue/
        narration/music/SFX/captions/renditions/review/package authoring.
        Evidence: AV narrative journey.
  - [ ] Y5A.8.p Complete video-edit source/sequence/clip/track/effects/color/
        audio/titles/captions/version/render/conform/export workflows. Evidence:
        video-edit journey.
  - [ ] Y5A.8.q For every workspace, require live owner backing, immutable
        source/ review media, stable frame/timecode/object anchors, typed gates,
        job progress/cancel/failure, and governed upstream/downstream handoff.
        Evidence: per-page invariant matrix.
  - [ ] Y5A.8.r Verify cross-workspace edit/rerender/review/handoff lineage,
        web/desktop/accessibility/role/tenant and no duplicated mutation owner.
        Evidence: integration E2E.
- [ ] Y5A.9 Complete real-time collaboration for CRDT document sync, presence,
      cursor/selection, comments/suggestions, edit locks, voice/video,
      recording/transcript, reconnect, guest access, and approved production
      management/video-conference integrations. Evidence: degraded and
      cross-version collaboration journeys.
  - [ ] Y5A.9.a Define collaboration document/room/session/participant/client/
        protocol/version/operation/vector/awareness/event/snapshot contracts per
        editor/entity type. Evidence: schemas.
  - [ ] Y5A.9.b Implement CRDT operations/schema validation/stable IDs/order/
        transactions/undo/redo/snapshots/compaction/persistence/audit and reject
        unauthorized fields. Evidence: sync tests.
  - [ ] Y5A.9.c Implement online/offline/multi-device concurrent create/edit/
        reorder/delete/merge with deterministic convergence and semantic
        conflict escalation where CRDT cannot decide meaning. Evidence:
        convergence tests.
  - [ ] Y5A.9.d Implement permission-filtered ephemeral presence, identity/
        status/active tool/viewport, heartbeat/expiry/privacy, and no hidden
        participant leakage. Evidence: presence tests.
  - [ ] Y5A.9.e Implement cursor/text range/block/entity/graph/timeline/asset/
        frame/time selection awareness with stable anchor mapping and accessible
        participant list. Evidence: cursor tests.
  - [ ] Y5A.9.f Implement durable comments/threads/replies/mentions/attachments/
        suggestions/accept-reject/resolve/reopen/notifications with exact
        revision anchors. Evidence: review collaboration tests.
  - [ ] Y5A.9.g Implement edit/production/asset/scene/shot locks with lease/
        fencing/heartbeat/expiry/read-only/takeover/override/audit and no split-
        brain commit. Evidence: lock tests.
  - [ ] Y5A.9.h Implement voice/video room permission/device selection/mute/
        screen share/quality/network/encryption/participant/accessibility/
        captions and honest degraded state. Evidence: conference tests.
  - [ ] Y5A.9.i Implement explicit consented recording/stop/indicator/storage/
        access/retention/export/delete plus attributed transcript/speaker/time/
        correction/caption and no covert capture. Evidence: recording tests.
  - [ ] Y5A.9.j Implement reconnect from vector/cursor/snapshot with auth
        refresh, replay/dedup/gap/schema migration/conflict and no duplicated
        operations. Evidence: reconnect tests.
  - [ ] Y5A.9.k Implement guest invite/identity/role/project/entity/spoiler/
        duration/download/recording limits, revocation, watermark, and audit.
        Evidence: guest tests.
  - [ ] Y5A.9.l Integrate approved production-management/video-conference
        systems through versioned public adapters for identity/calendar/task/
        meeting/recording/links/events with source-of-truth directionality.
        Evidence: integration tests.
  - [ ] Y5A.9.m Run mixed web/desktop/old-new
        client/offline/partition/reconnect/ revoked guest/degraded media journey
        with convergence/accessibility/ tenant proof. Evidence: cross-version
        E2E.
- [ ] Y5A.10 Complete pipeline-template and step authoring, configuration,
      validation, dry run, versioning, scheduling, budget/resource policy, human
      checkpoints, autonomous film/game execution, pause/redirect/cancel, retry,
      rollback, and output review. Evidence: agents never bypass safety, budget,
      production-verification, or human-override floors.
  - [ ] Y5A.10.a Define immutable pipeline/template/version/step/port/edge/
        parameter/dependency/tool/model/host/resource/checkpoint/schedule/run/
        output contracts and canonical serialization. Evidence: schemas.
  - [ ] Y5A.10.b Build versioned template
        metadata/profile/operation/input-output/
        default/extension/inheritance/owner/lifecycle authoring. Evidence:
        template tests.
  - [ ] Y5A.10.c Build typed step catalog and graph authoring for
        services/tools/ agents/providers/renderers/DCC-engine/human tasks with
        stable ports/ dependencies and no arbitrary code. Evidence: editor
        tests.
  - [ ] Y5A.10.d Build parameter/config/environment/secret-ref/target/condition/
        retry/timeout/idempotency/concurrency/artifact/resource policies with
        effective-value provenance. Evidence: configuration tests.
  - [ ] Y5A.10.e Validate graph types/cycles/reachability/dead outputs/
        dependencies/versions/licenses/security/rights/capabilities/resources/
        targets/policy/authority. Evidence: validation suite.
  - [ ] Y5A.10.f Dry-run resolution/authorization/dependency availability/
        estimates/plan/checkpoints/outputs without external side effects.
        Evidence: zero-side-effect tests.
  - [ ] Y5A.10.g
        Version/diff/review/approve/promote/deprecate/quarantine/rollback exact
        signed pipelines and preserve historical run bindings. Evidence:
        lifecycle tests.
  - [ ] Y5A.10.h Schedule one-time/recurring/event/dependency-triggered runs
        with timezone, capacity/reservation, priority/fairness, concurrency,
        embargo, and missed-run behavior. Evidence: schedule tests.
  - [ ] Y5A.10.i Allocate/enforce quota/budget/token/tool/compute/GPU/CPU/VRAM/
        storage/egress/time per step/total with estimates, reservations, stops,
        and reconciliation. Evidence: resource tests.
  - [ ] Y5A.10.j Insert human-only review/approval/decision checkpoints with
        eligible roles/quorum/evidence/unchanged plan/expiry and no system actor
        completion. Evidence: checkpoint tests.
  - [ ] Y5A.10.k Execute bounded autonomous film pipelines for script/board/
        asset/shot/render/edit/audio/localize/build outputs and game pipelines
        for design/asset/level/gameplay/NPC/build/test. Evidence: execution
        tests.
  - [ ] Y5A.10.l Implement pause/resume/redirect/cancel/abort/budget kill with
        safe boundaries/new plan revisions/provider propagation/late-result
        fencing/partial evidence. Evidence: command race tests.
  - [ ] Y5A.10.m Implement stage-scoped retry/new attempt and rollback/
        compensation to prior admitted versions without rewriting runs or
        deleting outputs. Evidence: recovery tests.
  - [ ] Y5A.10.n Route every attributed output through integrity/technical/
        safety/rights/canon/continuity/production verification and human
        accept/reject/revise before promotion. Evidence: output review tests.
  - [ ] Y5A.10.o Adversarially prove agents/flags/imports/API payloads cannot
        bypass code floors, reservations, checkpoints, verification, or human
        override. Evidence: authority boundary suite.
- [ ] Y5A.11 Complete Blender, Godot, Unreal, Houdini, and DaVinci workflows
      through the canonical Bellona/public adapter boundaries, including host
      capability, project/asset handoff, command status, version compatibility,
      round-trip loss, and recovery. Evidence: no private bridge duplication in
      Yemaya Studio.
  - [ ] Y5A.11.a Define Yemaya-to-Bellona public
        handoff/session/command/artifact/
        progress/error/capability/compatibility/loss/recovery contracts and
        ownership rules. Evidence: boundary schema.
  - [ ] Y5A.11.b Build host capability discovery for installed/connected host/
        plugin/version/OS/features/commands/formats/targets and supported/
        degraded/unavailable reasons. Evidence: capability tests.
  - [ ] Y5A.11.c Implement Blender project/scene/asset/geometry/material/rig/
        animation/camera/render/export handoffs and status through Bellona.
        Evidence: Blender journey.
  - [ ] Y5A.11.d Implement Godot project/scene/resource/gameplay/build/import/
        export handoffs and status through Bellona. Evidence: Godot journey.
  - [ ] Y5A.11.e Implement Unreal project/content/level/asset/sequence/gameplay/
        build/import/export handoffs and status through Bellona. Evidence:
        Unreal journey.
  - [ ] Y5A.11.f Implement Houdini project/hip/network/asset/cache/simulation/
        cook/export handoffs and status through Bellona. Evidence: Houdini
        journey.
  - [ ] Y5A.11.g Implement DaVinci project/timeline/media/color/audio/caption/
        conform/render/interchange handoffs and status through Bellona.
        Evidence: DaVinci journey.
  - [ ] Y5A.11.h Preserve exact Yemaya project/entity/asset/shot/sequence/build
        context, revisions/hashes/units/axes/color/timebase/rights/provenance on
        outbound and returned artifacts. Evidence: lineage tests.
  - [ ] Y5A.11.i Present typed queued/running/progress/output/partial/cancelled/
        failed/unknown command state, logs/receipts/cost and authorized cancel/
        retry/replay without raw shell. Evidence: status tests.
  - [ ] Y5A.11.j Enforce host/plugin/server/adapter/project/format/feature
        version compatibility, migration window, and explicit unsupported
        behavior. Evidence: version matrix.
  - [ ] Y5A.11.k Compute/report semantic round-trip losses/substitutions for
        hierarchy/transforms/units/materials/rig/animation/camera/timeline/
        metadata and require review. Evidence: loss tests.
  - [ ] Y5A.11.l Recover from disconnect/host crash/plugin restart/partial
        transfer/command timeout/artifact mismatch with reconciliation and no
        duplicate mutation. Evidence: recovery tests.
  - [ ] Y5A.11.m Add forbidden-import/IPC/API/schema checks proving no private
        bridge/session/command implementation in Yemaya. Evidence: ownership
        gate.
- [ ] Y5A.12 Complete gameplay and NPC design for mechanics, rules, states,
      behaviors, dialogue, navigation, encounters, inventory/combat hooks,
      simulation, playtest, balance, safety, and engine export. Evidence:
      design-to-supported-engine bundles and deterministic simulation fixtures.
  - [ ] Y5A.12.a Define versioned gameplay project/system/mechanic/rule/state/
        event/action/parameter/entity/component/behavior/encounter/test/export
        contracts and registries. Evidence: schemas.
  - [ ] Y5A.12.b Author mechanics with player/NPC actions, inputs,
        preconditions, effects, resources, feedback, failure/success, timing,
        tuning, objective, dependencies, and accessibility. Evidence: mechanic
        tests.
  - [ ] Y5A.12.c Author rule sets with typed
        conditions/effects/priorities/scope/
        conflicts/invariants/cooldowns/probability/seed and no arbitrary code.
        Evidence: rule tests.
  - [ ] Y5A.12.d Author finite/hierarchical state machines and event flows with
        initial/final states, transitions/guards/effects, parallel regions,
        resets, cycle/dead/unreachable analysis. Evidence: state tests.
  - [ ] Y5A.12.e Author NPC identity/role/goals/personality/knowledge/memory/
        perception/needs/emotion/relationship/consent/canon and gameplay links.
        Evidence: NPC tests.
  - [ ] Y5A.12.f Author behavior tree/utility/planner/task/condition/blackboard/
        interrupt/fallback/timing/resource/safety contracts and accessible
        graph/table parity. Evidence: behavior tests.
  - [ ] Y5A.12.g Author branching dialogue with speaker/knowledge/relationship/
        condition/choice/consequence/voice/localization/spoiler/canon and
        integrity constraints. Evidence: dialogue tests.
  - [ ] Y5A.12.h Author navigation spaces/agents/links/areas/costs/obstacles/
        paths/avoidance/traversal/off-mesh/crowds and reachability validation.
        Evidence: navigation tests.
  - [ ] Y5A.12.i Author encounters with participants/waves/spawns/objectives/
        triggers/space/resources/difficulty/rewards/fail/reset/pacing/safety and
        dependency state. Evidence: encounter tests.
  - [ ] Y5A.12.j Author inventory/item/equipment/crafting/economy and combat
        stats/actions/damage/status/targeting/AI/animation/VFX/audio hooks as
        typed public interfaces. Evidence: hook tests.
  - [ ] Y5A.12.k Build deterministic seeded headless simulation with exact
        design/runtime versions, scenarios/bots, progress/cancel/traces/metrics/
        counterexamples and bounded resources. Evidence: simulation fixtures.
  - [ ] Y5A.12.l Build playtest config/build/participant/consent/telemetry/
        feedback/replay/privacy linkage and compare synthetic versus human
        evidence. Evidence: playtest tests.
  - [ ] Y5A.12.m Build balance analysis for win/loss/time/resources/difficulty/
        economy/encounter/NPC outcomes with uncertainty/cohort privacy and
        explainable human tuning decisions. Evidence: balance tests.
  - [ ] Y5A.12.n Run harmful mechanic/dark pattern/age/violence/chat/NPC
        manipulation/bias/accessibility/content safety and human review gates.
        Evidence: safety matrix.
  - [ ] Y5A.12.o Map canonical design to supported Godot/Unreal/other engine
        profiles through Bellona with stable IDs/versioned adapters/loss report/
        import/read-back. Evidence: engine bundle suite.
- [ ] Y5A.13 Complete virtual-production and XR project workflows for remote
      actor capture, stage/session planning, camera tracking, timecode/genlock,
      LED/ICVFX, spatial review, device/target profiles, recording, dailies, and
      Bellona handoff. Evidence: capability-gated host/device matrix.
  - [ ] Y5A.13.a Define VP/XR project/stage/session/device/performer/camera/
        tracking/timebase/display/render/recording/artifact/profile/handoff
        contracts and ownership. Evidence: schemas.
  - [ ] Y5A.13.b Build remote actor invite/auth/consent/identity/voice-likeness/
        device/network/calibration/rehearsal/capture/monitor/stop/revocation and
        Aja motion handoff. Evidence: remote capture tests.
  - [ ] Y5A.13.c Build stage/session plan for location/stage volume/date/time/
        participants/crew/roles/shots/assets/sets/equipment/devices/safety/
        permits/schedule/call sheet. Evidence: planning tests.
  - [ ] Y5A.13.d Build camera/lens/sensor/distortion/focus/pose/tracking system/
        markers/origin/coordinate/units/calibration/quality/drift/redundancy/
        recording. Evidence: camera tracking tests.
  - [ ] Y5A.13.e Build timecode/genlock/PTP/clock/source/rate/drop-frame/
        synchronization/calibration/offset/drift/failover and per-device status.
        Evidence: sync tests.
  - [ ] Y5A.13.f Build LED/ICVFX wall/processor/panel/canvas/nDisplay or
        profile/ camera frustum/color
        pipeline/latency/genlock/content/lighting/heat/ safety configuration.
        Evidence: LED tests.
  - [ ] Y5A.13.g Build XR scene/space/anchor/scale/origin/tracking/input/
        locomotion/interaction/passthrough/performance/comfort/accessibility/
        safety authoring. Evidence: XR tests.
  - [ ] Y5A.13.h Build spatial review for synchronized multiuser scene/assets/
        cameras/takes/annotations/measurements/versions with desktop/2D semantic
        alternative. Evidence: spatial review tests.
  - [ ] Y5A.13.i Define host/device/OS/runtime/plugin/firmware/model/capability/
        input-output/latency/accuracy/profile/target support matrix and honest
        unavailable/degraded states. Evidence: capability tests.
  - [ ] Y5A.13.j Build multistream
        video/audio/motion/tracking/timecode/metadata/ camera/device recording
        with immutable take IDs/checksums/proxies/ dropped-frame status/consent.
        Evidence: recording tests.
  - [ ] Y5A.13.k Build take/dailies ingest/sync/playback/compare/notes/select/
        reject/retake/promotion and exact stage/shot/performer/device lineage.
        Evidence: dailies tests.
  - [ ] Y5A.13.l Handoff stage/project/assets/cameras/tracks/takes/builds
        through Bellona public contracts and returned
        artifacts/status/loss/recovery; no duplicate bridge authority. Evidence:
        handoff tests.
  - [ ] Y5A.13.m Run capability-gated session from
        plan/calibrate/capture/review/ handoff/recovery across supported
        host/device cells. Evidence: matrix E2E.
- [ ] Y5A.14 Complete unified search across projects, assets, scripts, shots,
      characters, worlds, locations, tasks, people, comments, jobs, plugins,
      models, and marketplace items with permission-safe facets/snippets.
      Evidence: protected, unreleased, and cross-tenant data cannot leak through
      counts, suggestions, or caches.
  - [ ] Y5A.14.a Define versioned searchable-kind registry and per-kind index
        document with canonical ID/revision/tenant/project/owner/status/locale/
        classification/searchable/display-only/forbidden fields. Evidence:
        schemas.
  - [ ] Y5A.14.b Implement project/asset/script/shot/character/world/location/
        task documents and entity-specific title/text/tags/relations/status/
        rights/canon/production metadata. Evidence: content index tests.
  - [ ] Y5A.14.c Implement people/comment/job/plugin/model/marketplace documents
        with least privilege, no secrets/private prompt/spoiler/biometric/
        billing/protected evidence. Evidence: platform index tests.
  - [ ] Y5A.14.d Build transactional/outbox-driven create/update/version/
        permission/membership/publish/quarantine/archive/delete events with
        checkpoints/idempotency/order/rebuild reconciliation. Evidence: index
        lifecycle tests.
  - [ ] Y5A.14.e Implement text/exact/fuzzy/tag/entity/relation/reference search
        with allowlisted query grammar, stable ranking, locale analysis,
        typo/synonym rules, and version/source. Evidence: query tests.
  - [ ] Y5A.14.f Implement permission-safe kind/project/status/date/owner/tag/
        asset type/production/marketplace/plugin/model facets and stable signed
        cursor pagination. Evidence: facet tests.
  - [ ] Y5A.14.g Generate snippets/highlights only from authorized fields after
        property checks and suppress
        spoiler/protected-source/solution/unreleased context. Evidence: snippet
        tests.
  - [ ] Y5A.14.h Generate autocomplete/typeahead/recent/popular/recommendations
        with authorization/privacy thresholds and no hidden-title/existence
        inference. Evidence: suggestion tests.
  - [ ] Y5A.14.i Apply current route/object/property/function authorization
        after every hit and before counts/facets/cache, not solely index ACL
        snapshots. Evidence: authorization tests.
  - [ ] Y5A.14.j Scope query/result/count/facet/suggestion caches by actor/
        tenant/project/roles/permissions/locale/version and purge on access/
        lifecycle changes. Evidence: cache tests.
  - [ ] Y5A.14.k Test colliding tenant/project/title/tag/ID/timestamps, guessed
        protected terms, membership revoke, stale index, timing/count/empty/
        forbidden differences. Evidence: adversarial suite.
  - [ ] Y5A.14.l Load-test production-scale mixed kinds/facets/permissions/index
        lag/rebuild while meeting search SLO and no leakage. Evidence:
        performance report.
- [ ] Y5A.15 Complete asset, plugin, workflow/template, and AI-model marketplace
      flows for submission, packaging, compatibility, security/license review,
      pricing/entitlement, install/update/remove, ratings/reporting, creator
      attribution/payout, deprecation, takedown, and project dependency impact.
      Evidence: marketplace content cannot bypass runtime or release policy.
  - [ ] Y5A.15.a Define marketplace enablement/audience/region/tenant/host and
        separate versioned item contracts for asset, plugin, pipeline/workflow/
        template, and model/modifier with lifecycle. Evidence: schemas.
  - [ ] Y5A.15.b Build creator enrollment/identity/authority/tax/payout/terms/
        rights/contact/security and organization/team publishing roles.
        Evidence: creator tests.
  - [ ] Y5A.15.c Build item/version submission with immutable source/artifact/
        hash/dependencies/metadata/docs/examples/license/model card/provenance/
        pricing/targets/creator attribution. Evidence: submission tests.
  - [ ] Y5A.15.d Build deterministic safe package/manifest/signature/checksum/
        relative paths/permissions/entry points/dependency lock and independent
        unpack/install validation. Evidence: packaging tests.
  - [ ] Y5A.15.e Build per-host/version/profile/OS/runtime/engine/DCC/model/
        workflow/asset/project-type input-output compatibility and declared
        loss/ unsupported cells. Evidence: matrix.
  - [ ] Y5A.15.f Quarantine and run malware/archive/path/serialization/code/
        dependency/vulnerability/signature/sandbox/network/resource/security
        review by item type. Evidence: security tests.
  - [ ] Y5A.15.g Review source/ownership/license/rights/consent/training data/
        attribution/territory/term/derivative/commercial/safety/quality/
        accessibility and human approvals. Evidence: trust tests.
  - [ ] Y5A.15.h Build offers/prices/currencies/taxes/fees/promotions/refunds/
        trials/seats/usage/region/term and server-side entitlement grant/check/
        revoke. Evidence: commerce tests.
  - [ ] Y5A.15.i Build download/install with dependency/permission/impact/
        confirmation, isolated staging, verification, lockfile, project linkage,
        and rollback. Evidence: install tests.
  - [ ] Y5A.15.j Build update/migration/change log/compatibility/project impact/
        staged canary/backup/rollback and remove/disable constraints for active
        dependencies. Evidence: update/remove tests.
  - [ ] Y5A.15.k Build verified-purchase/use ratings/reviews, moderation,
        version relevance, anti-manipulation, replies, reporting/appeal and no
        secret/project leakage. Evidence: rating tests.
  - [ ] Y5A.15.l Preserve creator/item/version/source attribution through
        installation/use/derivative/generation/build/release and calculate
        royalty/fee/hold/refund/chargeback/payout ledger. Evidence: attribution/
        payout tests.
  - [ ] Y5A.15.m Build deprecate/supersede/end-of-support/revoke/quarantine/
        takedown/appeal/restore with consumer notices/download/entitlement/
        project/build/runtime/cache/payout effects. Evidence: lifecycle tests.
  - [ ] Y5A.15.n Query exact project dependency/blast radius for item/version/
        transitive dependencies and preview breakage/migration/rebuild/recall
        before mutation. Evidence: impact tests.
  - [ ] Y5A.15.o Revalidate marketplace item/license/entitlement/security/
        compatibility at runtime/build/release and prohibit listing/review/
        purchase from satisfying production gates. Evidence: boundary tests.
  - [ ] Y5A.15.p Run submission-to-review-to-purchase/install/use/build/release/
        update/takedown/payout journey with cross-tenant/fraud/security
        failures. Evidence: E2E.
- [ ] Y5A.16 Complete organization, team, user, RBAC, invitations, API keys,
      enterprise plan/entitlement, audit/compliance, analytics, webhook, and
      integration administration. Evidence: property-level authorization,
      separation of duties, billing/entitlement consistency, and audit export.
  - [ ] Y5A.16.a Define organization/workspace/team/user/membership/role/
        permission/entitlement/key/webhook/integration/admin contracts and
        canonical ownership. Evidence: schemas.
  - [ ] Y5A.16.b Build organization create/profile/domains/regions/locale/
        security/data/retention/billing owner/transfer/suspend/archive/delete
        with step-up and audit. Evidence: org tests.
  - [ ] Y5A.16.c Build hierarchical/static/dynamic project teams, membership/
        manager/guest/service account/role inheritance and cycle/scope controls.
        Evidence: team tests.
  - [ ] Y5A.16.d Build user status/profile/contact/identity provider/MFA/device/
        sessions/recovery/lock/suspend/delete/export with privacy/redaction.
        Evidence: user tests.
  - [ ] Y5A.16.e Build typed RBAC roles/permissions/resource/property/function/
        conditions/deny/precedence/effective inspector/change preview/version/
        assignment/revocation and code floors. Evidence: RBAC tests.
  - [ ] Y5A.16.f Build invite create/role/scope/expiry/domain/seat/send/resend/
        accept/decline/revoke/conflict/duplicate/transfer and anti-enumeration.
        Evidence: invitation tests.
  - [ ] Y5A.16.g Build API key/service credential purpose/scope/project/
        operations/network/expiry/create-once-secret/rotate/revoke/last-use/
        anomaly and no UI/log secret. Evidence: key tests.
  - [ ] Y5A.16.h Build enterprise plan/seat/storage/compute/provider/feature/
        support/region/marketplace limits and server-side effective entitlement
        from billing/contract/manual grant. Evidence: entitlement tests.
  - [ ] Y5A.16.i Reconcile subscription/invoice/payment/credit/refund/usage/
        purchase/payout/plan changes to entitlements idempotently without access
        ambiguity. Evidence: billing consistency tests.
  - [ ] Y5A.16.j Build immutable audit for auth/admin/content/asset/job/spend/
        marketplace/build/release/integration actions with protected fields,
        search/export/retention/tamper evidence. Evidence: audit tests.
  - [ ] Y5A.16.k Build compliance policy/control/evidence/request/attestation/
        exception/retention/legal hold/export and framework mappings without
        self-certification. Evidence: compliance tests.
  - [ ] Y5A.16.l Build privacy-safe organization/project/usage/cost/performance/
        collaboration/marketplace analytics with metric definitions/cohort
        thresholds/role filters and no worker surveillance. Evidence: analytics
        tests.
  - [ ] Y5A.16.m Build webhook endpoint/event filters/signing secret ref/
        verification/retry/backoff/rate/dedup/order/dead letter/replay/log/
        rotate/disable and payload redaction. Evidence: webhook tests.
  - [ ] Y5A.16.n Build approved integration install/config/OAuth/scopes/
        mapping/sync/direction/status/log/revoke/data deletion through public
        adapters. Evidence: integration tests.
  - [ ] Y5A.16.o Enforce maker-checker/separation for ownership/security/RBAC/
        billing/spend/key/compliance/export/kill actions and property-level auth
        throughout. Evidence: authority suite.
  - [ ] Y5A.16.p Export deterministic redacted signed audit/compliance/admin
        evidence and run organization lifecycle/tenant/billing/entitlement E2E.
        Evidence: audit export journey.
- [ ] Y5A.17 Disposition community/showcase features explicitly: creator
      profile, publish/share, follow, comment, remix/fork, moderation, report,
      attribution, rights, privacy, and takedown if retained; otherwise define
      their separate host or retirement. Evidence: no orphan `community`
      capability or accidental publication path.
  - [ ] Y5A.17.a Inventory all community/showcase routes/components/APIs/stores/
        events/jobs/packages/flags/navigation/consumers/tests and live/fixture
        state at atomic operation level. Evidence: inventory.
  - [ ] Y5A.17.b Decide retain-in-Yemaya, move-to-separate-host/service, retire,
        or explicitly defer per capability with audience/owner/data/authority/
        migration/expiry. Evidence: decision record.
  - [ ] Y5A.17.c If retained, build creator profile identity/display/links/
        portfolio/visibility/verification/block/mute/privacy and no private
        project leakage. Evidence: profile tests.
  - [ ] Y5A.17.d If retained, build explicit publish candidate/review/gates/
        visibility/audience/license/embargo/share-link/revoke/read-back rather
        than making project/asset visibility publish. Evidence: publish tests.
  - [ ] Y5A.17.e If retained, build follow/unfollow/request/block/mute/feed/
        notification/privacy/rate/anti-enumeration with no authority
        inheritance. Evidence: social graph tests.
  - [ ] Y5A.17.f If retained, build comments/replies/mentions/reactions/edit/
        delete/report/moderation/notifications with audience/asset-version/
        accessibility and abuse controls. Evidence: interaction tests.
  - [ ] Y5A.17.g If retained, build remix/fork eligibility/license/attribution/
        source-version/copy scope/new project identity/rights/gates and source
        revocation effects. Evidence: remix tests.
  - [ ] Y5A.17.h If retained, build content/user moderation classifier/rule/
        human queue/uncertainty/decision/appeal/sanction/audit without auto-
        publication or false accusation. Evidence: moderation tests.
  - [ ] Y5A.17.i If retained, build report type/subject/evidence/reporter
        safety/ triage/status/anti-abuse/privacy/escalation and no reporter
        authority. Evidence: report tests.
  - [ ] Y5A.17.j Preserve creator/source/project/asset/model/tool/version/
        derivative attribution and license/consent/rights/territory/term/
        disclosure through showcase/remix/download. Evidence: attribution tests.
  - [ ] Y5A.17.k Build privacy controls for profile/content/follow/interactions/
        discoverability/search/counts/download/contact/minors/blocked users/
        retention/deletion. Evidence: privacy tests.
  - [ ] Y5A.17.l Build takedown/quarantine/unpublish/appeal/restore with blast
        radius/caches/links/downloads/remixes/notifications/receipts/history.
        Evidence: takedown tests.
  - [ ] Y5A.17.m If moved, implement signed least-privilege publication/handoff/
        return contracts and remove duplicate UI/data authority. Evidence:
        separate-host tests.
  - [ ] Y5A.17.n If retired, disable creation/discovery/mutation,
        migrate/export/ retain/delete data per policy, remove
        routes/flags/dependencies, and verify no accidental publication path.
        Evidence: retirement tests.
  - [ ] Y5A.17.o Add bidirectional inventory/route/dependency gate proving no
        orphan `community` capability or unreviewed live surface. Evidence:
        disposition gate.
- [ ] Y5A.18 Complete developer surfaces for REST/WebSocket APIs, webhooks, CLI,
      TypeScript/Python/C++ SDKs, documentation playground, capability
      discovery, authentication, examples, rate limits, versioning, and
      deprecation. Evidence: generated contracts and end-to-end external-client
      conformance for every supported operation.
  - [ ] Y5A.18.a Inventory/version every supported external operation/resource/
        event/stream/command, audience/scope, owner, route/protocol, stability,
        consumer, and evidence. Evidence: developer surface registry.
  - [ ] Y5A.18.b Generate/validate REST OpenAPI for auth/scopes, requests/
        responses/errors, pagination/idempotency/concurrency, uploads/downloads,
        versions/units, examples, and redactions. Evidence: REST contracts.
  - [ ] Y5A.18.c Define WebSocket/SSE connection/auth/subscribe/filter/snapshot/
        cursor/event/order/dedup/reconnect/backpressure/heartbeat/error/version/
        close semantics. Evidence: realtime contracts.
  - [ ] Y5A.18.d Implement webhook event catalog/payload/version/signature/
        rotation/replay/retry/order/dedup/rate/redaction/test/reconciliation
        semantics. Evidence: webhook contracts.
  - [ ] Y5A.18.e Build CLI config/profile/auth/project context, typed commands,
        input/output formats, interactive/noninteractive, dry run/confirm,
        progress/cancel, exit codes, version update, and no secret logs.
        Evidence: CLI tests.
  - [ ] Y5A.18.f Generate TypeScript SDK models/clients/pagination/streams/
        uploads/errors/retries/idempotency/examples and package/version/browser-
        Node compatibility. Evidence: TS conformance.
  - [ ] Y5A.18.g Generate Python SDK sync/async
        models/clients/iterators/streams/
        uploads/errors/retries/idempotency/examples and supported runtime
        versions. Evidence: Python conformance.
  - [ ] Y5A.18.h Generate C++ SDK typed models/client/auth/async/callback/
        streaming/binary transfer/errors/cancellation/build systems/platform ABI
        support. Evidence: C++ conformance.
  - [ ] Y5A.18.i Build documentation from canonical contracts with operation/
        concepts/auth/permissions/lifecycle/failure/limits/version/migration/
        security guides and runnable examples. Evidence: doc drift tests.
  - [ ] Y5A.18.j Build isolated playground using synthetic/sandbox tenant and
        bounded scoped credentials, editable requests, response/event inspector,
        rate/cost warning, reset, and no production secret/data access.
        Evidence: playground tests.
  - [ ] Y5A.18.k Build audience/authenticated capability discovery for
        operations/ asset kinds/hosts/providers/targets/limits/versions/degraded
        states without hidden feature/provider leakage. Evidence: capability
        tests.
  - [ ] Y5A.18.l Implement OAuth/API key/service identity/session/device flows,
        scopes/project/tenant/resource/property authorization, rotation/revoke/
        expiry, and step-up for dangerous actions. Evidence: auth suite.
  - [ ] Y5A.18.m Enforce request/data/compute/cost/concurrency/stream/webhook
        limits with headers/retry/reset, tenant fairness, and no bypass through
        batch/SDK/alternate protocol. Evidence: rate-limit tests.
  - [ ] Y5A.18.n Define URL/schema/event/SDK semantic versioning, compatibility
        windows/negotiation, additive/breaking policy, changelog, migration, and
        consumer testing. Evidence: version matrix.
  - [ ] Y5A.18.o Define deprecation announce/headers/docs/telemetry/owner/
        replacement/sunset/block/exception and remove only after consumer gate.
        Evidence: deprecation tests.
  - [ ] Y5A.18.p Run generated contract tests and real external TS/Python/C++/
        CLI/webhook/realtime journeys for every supported operation; fail
        untested cells. Evidence: conformance matrix.
- [ ] Y5A.19 Complete production safety for prompts/media, model/provider use,
      consent/likeness, rights, content classification, age rating, moderation,
      project policy, incident quarantine, recall, and audit across every AAA
      lane. Evidence: a feature-specific policy matrix; generic upload
      moderation is not accepted as proof.
  - [ ] Y5A.19.a Enumerate every AAA feature/lane/input/output/action/asset
        kind/ host/audience/target/provider and map applicable safety controls,
        owners, stages, and proof. Evidence: policy matrix.
  - [ ] Y5A.19.b Validate prompt/brief/reference/retrieved/imported content for
        injection, disallowed intent, identity/spoiler/protected-source leakage,
        unsafe tools, and policy before planning. Evidence: prompt tests.
  - [ ] Y5A.19.c Moderate generated/uploaded/captured/edited image/video/audio/
        voice/3D/text/code/project/package metadata at ingest, preview,
        promotion, build, release with uncertainty. Evidence: media tests.
  - [ ] Y5A.19.d Admit models/providers/endpoints/workflows/nodes/adapters only
        with identity/version/hash/license/training provenance/security/
        evaluation/data-use/residency/target approval and revalidate. Evidence:
        model/provider tests.
  - [ ] Y5A.19.e Enforce subject/performer/actor/voice/face/likeness/biometric/
        capture consent with scope/purpose/use/territory/term/disclosure/
        revocation at every derivative/use/release. Evidence: consent tests.
  - [ ] Y5A.19.f Enforce asset/source/music/script/reference/model/license/
        ownership/use/adaptation/training/commercial/distribution/attribution/
        territory/term/embargo rights. Evidence: rights tests.
  - [ ] Y5A.19.g Classify content by violence/sexual/hate/self-harm/child/
        substances/language/fear/gambling/user interaction/context/intensity and
        preserve raw evidence. Evidence: classification tests.
  - [ ] Y5A.19.h Map platform/region/audience/project type/content/interactivity
        to age-rating descriptors/evidence/reviewer/label/target requirements.
        Evidence: rating matrix.
  - [ ] Y5A.19.i Build input/output/project/user/marketplace/community
        moderation rules/classifiers/human queue/decisions/appeals/uncertainty/
        anti-bias/privacy and no automatic accusation. Evidence: moderation
        tests.
  - [ ] Y5A.19.j Build platform/region/organization/project/profile/campaign
        typed policy overlays that only tighten code floors, with effective
        preview/version/approval/freshness. Evidence: policy property tests.
  - [ ] Y5A.19.k Apply safety checks at feature-specific typed context points
        for writing, boards, design, gameplay/NPC, VP/XR/capture, DCC/engine,
        rendering/post, collaboration, agents, marketplace/community,
        developer/export/release. Evidence: lane coverage tests.
  - [ ] Y5A.19.l Create incident case/scope/severity/evidence/authority and
        quarantine affected model/provider/workflow/source/asset/project/build/
        marketplace/release from new use. Evidence: quarantine tests.
  - [ ] Y5A.19.m Compute blast radius and recall/unpublish/disable downstream
        projects/assets/derivatives/builds/gallery/marketplace/community/
        external targets/caches/links with receipts. Evidence: recall tests.
  - [ ] Y5A.19.n Persist immutable
        decision/override/exception/scan/model/policy/
        action/notification/verification audit with protected-field controls and
        nonwaivable floors. Evidence: audit tests.
  - [ ] Y5A.19.o Build feature-specific calibrated/adversarial fixtures and fail
        every matrix cell relying only on generic upload moderation. Evidence:
        safety coverage gate.
- [ ] Y5A.20 Complete animation learning and motion authoring for references,
      timing/exposure charts, poses/keyframes, arcs/spacing, cleanup, retiming,
      camera, performance notes, motion assets, Aja capture/retarget handoff,
      preview/onion-skin or semantic equivalent, critique, versioning, and
      export. Evidence: reference→motion→approved shot lineage and accessible
      timing/curve alternatives.
  - [ ] Y5A.20.a Define animation project/sequence/shot/character/rig/control/
        clip/layer/pose/key/curve/annotation/reference/motion asset/version/
        target contracts and units/timebases. Evidence: schemas.
  - [ ] Y5A.20.b Build reference ingest/record/link/trim/sync/loop/annotation/
        rights/consent/source/performer/camera/timecode and side-by-side study.
        Evidence: reference tests.
  - [ ] Y5A.20.c Build exposure/timing sheet with frame/timecode/beat/phoneme/
        action/pose/dialogue/audio/camera tracks, holds/inbetweens/accents/
        breakdowns, versions, and list/table parity. Evidence: timing tests.
  - [ ] Y5A.20.d Build pose/keyframe authoring for rig/control/body/face/hands/
        props/contacts/constraints/space/IK-FK/keys/interpolation/tangents with
        stable identities. Evidence: keyframe tests.
  - [ ] Y5A.20.e Build motion path/arcs/spacing/velocity/acceleration/easing/
        silhouette/line-of-action/weight/balance/contact trajectory
        visualization and measurable diagnostics. Evidence: arc/spacing tests.
  - [ ] Y5A.20.f Build cleanup for curve noise/key reduction/tangent/foot slide/
        penetrations/jitter/pops/constraints/contacts/rig errors as reversible
        layers with before-after. Evidence: cleanup tests.
  - [ ] Y5A.20.g Build retiming/ripple/stretch/compress/hold/ramp/offset/loop/
        frame-rate conversion/audio sync with key/event/annotation/shot impact
        preview. Evidence: retime tests.
  - [ ] Y5A.20.h Build camera shot/lens/framing/pose/path/focus/shake/cut/
        continuity/constraints and motion-camera/Bellona engine-DCC handoffs.
        Evidence: camera tests.
  - [ ] Y5A.20.i Build performance notes with character/shot/frame-range/body/
        facial/voice/emotion/intent/reference anchors, assignments/resolution,
        and actor/consent privacy. Evidence: note tests.
  - [ ] Y5A.20.j Build motion asset clip/take/rig/skeleton/source/performer/
        consent/units/rate/root motion/contacts/tags/versions/gates/library/
        promotion. Evidence: motion asset tests.
  - [ ] Y5A.20.k Handoff capture/calibration/solve/clean/retarget jobs to Aja
        public contracts with exact project/performer/rig/shot context,
        progress, returned lineage, and no duplicate motion pipeline. Evidence:
        Aja tests.
  - [ ] Y5A.20.l Build retarget source-target skeleton/rig/map/rest pose/scale/
        root/IK/contacts/rules/version/preview/loss/validation and acceptance.
        Evidence: retarget tests.
  - [ ] Y5A.20.m Build synchronized playback/scrub/loop/speed/onion skin/ghost/
        trajectory/curve/dope sheet/reference/audio/camera overlays plus
        semantic timing/key/curve/path tables/forms. Evidence:
        preview/accessibility tests.
  - [ ] Y5A.20.n Build rule/model/human critique for
        timing/arcs/spacing/contact/
        continuity/anatomy/weight/performance/camera/technical quality with
        exact regions/uncertainty/decisions. Evidence: critique tests.
  - [ ] Y5A.20.o Implement immutable versions/layers/diff/branch/merge/restore/
        compare/approval and downstream shot/render/build invalidation.
        Evidence: version tests.
  - [ ] Y5A.20.p Export ratified animation/motion formats through Bellona with
        skeleton/rig/keys/curves/events/camera/units/timebase/materials/
        metadata/rights/provenance/loss/read-back. Evidence: export tests.
  - [ ] Y5A.20.q Run reference→timing→keys→Aja capture/retarget→cleanup/
        critique→approved shot→engine/DCC export journey with accessible
        alternatives. Evidence: E2E.

### Y6 — Persistence, APIs, desktop security, and collaboration

- [ ] Y6.1 Decide authoritative stores for projects, study content, cases,
      canon, assets, reviews, playtests, and releases. Evidence: ownership ADR.
  - [x] Y6.1.a Inventory existing databases/schemas/tables/blobs/indexes/caches/
        repositories/services/events/backups/production data per aggregate.
        Evidence: storage inventory.
        <!-- yemaya-storage-inventory.json: storage inventory of Yemaya. 38 relational
                                                                                                                                                                                                                                                                        aggregates over 4 PrismaClients, 1 schema (prisma/schema.prisma). INDEX-HEAVY:
                                                                                                                                                                                                                                                                        120 DB indexes (@@index/@@unique) = 3.2 indexes/aggregate, fitting a creative
                                                                                                                                                                                                                                                                        studio's query load. Storage layers present: tables, schemas, indexes (120),
                                                                                                                                                                                                                                                                        blobs (20 Asset object-storage files via storageKey/storageBucket), caches (13
                                                                                                                                                                                                                                                                        Redis files), events (4 entities: Activity/Notification/Webhook/WebhookDelivery).
                                                                                                                                                                                                                                                                        ARCHITECTURE FINDING: 0 repository + 0 manager classes — Yemaya is SERVICE-DIRECT,
                                                                                                                                                                                                                                                                        consistent with Isis (I6.1.a) and Veritas (V6.1.a): all three domains use
                                                                                                                                                                                                                                                                        service-direct Prisma access with no repository layer (a cross-domain pattern).
                                                                                                                                                                                                                                                                        Backups are ops/infra-level (not modeled in application code, recorded honestly).
                                                                                                                                                                                                                                                                        Precise scoped patterns (storageKey/Bucket, new Redis, class *Repository/Manager);
                                                                                                                                                                                                                                                                        DB indexes counted from the schema. generate-yemaya-storage-inventory.mjs + 6
                                                                                                                                                                                                                                                                        tests, wired into inventory/verify + CI. Y6.1.b (one mutation owner per
                                                                                                                                                                                                                                                                        aggregate) + later Y6.1.* are design and stay unchecked. -->
  - [ ] Y6.1.b Assign exactly one mutation owner/store and identity/version/
        tenant/transaction/retention/security requirements for each named
        aggregate. Evidence: ownership matrix.
  - [ ] Y6.1.c Distinguish canonical records from blobs, derived read/search/
        consumer projections, caches, audit/outbox, analytics, and desktop local
        working copies. Evidence: data-flow map.
  - [ ] Y6.1.d Resolve cross-store links/events/consistency/failure/recovery/
        deletion/legal hold/backup and prohibit distributed shadow writes.
        Evidence: consistency design.
  - [ ] Y6.1.e Ratify owner/store/migration/operational owner/rejected
        alternative ADR and add schema/import ownership checks. Evidence:
        approved ADR.
- [ ] Y6.2 Add missing explicit-column schemas and safe migrations. Evidence:
      production-size rehearsal.
  - [ ] Y6.2.a Diff ratified contracts against owner schemas and classify
        reusable fields versus genuine missing tables/columns/constraints/
        indexes. Evidence: gap report.
  - [ ] Y6.2.b Separate tenant/identity/state/version/owner/time/lock/freshness/
        classification/query-critical columns from immutable typed content
        payloads. Evidence: schema design.
  - [ ] Y6.2.c Add expand migrations and backward-compatible read/write/
        conversion code without destructive rename/drop/default ambiguity.
        Evidence: expand tests.
  - [ ] Y6.2.d Backfill resumably in bounded tenant-safe batches with
        checkpoints, errors/quarantine, throttling, and observable progress.
        Evidence: backfill tests.
  - [ ] Y6.2.e Verify counts/FK/unique/check/null/hash/semantic/read/query-plan/
        divergence invariants before cutover. Evidence: verification report.
  - [ ] Y6.2.f Cut over reversibly, monitor, then contract after rollback
        window/ consumer inventory; rehearse at production
        scale/locks/resources. Evidence: migration rehearsal.
- [ ] Y6.3 Implement durable stores with optimistic concurrency and tombstones.
      Evidence: real-database tests.
  - [ ] Y6.3.a Define store interfaces/transactions for exact
        aggregate/revision/ relation/read/list/transition/restore operations and
        authorization context. Evidence: repository contracts.
  - [ ] Y6.3.b Persist immutable revisions, operational projection, audit, and
        outbox atomically before acknowledge with tenant-scoped constraints.
        Evidence: transaction tests.
  - [ ] Y6.3.c Enforce expected revision/ETag/lease/fencing and return safe
        conflicts; prohibit last-write-wins or stale worker commits. Evidence:
        concurrency tests.
  - [ ] Y6.3.d Represent delete/archive/unlink through typed tombstone with
        actor/reason/time/prior revision/retention/restore scope rather than
        dangling hard delete. Evidence: tombstone tests.
  - [ ] Y6.3.e Make reads/relations/search/events respect tombstone visibility
        and restore as new revision without resurrecting invalid dependencies.
        Evidence: lifecycle tests.
  - [ ] Y6.3.f Test real database restart/deadlock/duplicate/retry/branch/merge/
        retention/two-tenant/production-size paths. Evidence: integration suite.
- [ ] Y6.4 Implement authoring APIs through versioned public contracts.
      Evidence: web/desktop consumer-driven contracts.
  - [ ] Y6.4.a Inventory every project/study/case/canon/source/asset/review/
        playtest/build/release operation and canonical command/query/event/
        stream contracts. Evidence: API inventory.
  - [ ] Y6.4.b Implement versioned list/detail/create-revision/transition/
        relationship/comment/review/job/import-export operations with stable
        errors. Evidence: API tests.
  - [ ] Y6.4.c Enforce route/function/object/property/tenant/project/spoiler/
        protected-source authorization, idempotency, optimistic concurrency,
        pagination, rate/budget. Evidence: security tests.
  - [ ] Y6.4.d Generate OpenAPI/realtime/IPC-facing shared schemas with
        versions/ units/discriminators/absence-redaction and no renderer-local
        shapes. Evidence: contract generation.
  - [ ] Y6.4.e Verify web and desktop consumers for
        success/empty/stale/conflict/
        offline/degraded/not-configured/forbidden/failure/version skew.
        Evidence: consumer-driven suite.
- [ ] Y6.5 Implement compiler/verifier/eval/canon jobs as durable work.
      Evidence: retry, cancellation, lease-loss, and stale-input tests.
  - [ ] Y6.5.a Define job/plan/stage/DAG/attempt/lease/checkpoint/input/output/
        progress/error/cost/event contracts and separate compiler/verifier/eval/
        canon operation schemas. Evidence: job schemas.
  - [ ] Y6.5.b Create idempotently with exact immutable project/case/study/
        source/canon/policy/tool versions and transaction-bound audit/outbox.
        Evidence: submission tests.
  - [ ] Y6.5.c Claim with fenced lease/heartbeat/expiry/CAS and checkpoint only
        validated stage artifacts/hashes/consumed versions. Evidence: worker
        tests.
  - [ ] Y6.5.d Retry only explicit idempotent eligible stage/new attempt with
        limits/backoff; resume only supported checkpoints. Evidence: retry
        tests.
  - [ ] Y6.5.e Cancel queued/running/provider work with late-result fencing,
        resource cleanup, partial evidence and explicit outcome. Evidence:
        cancellation tests.
  - [ ] Y6.5.f Detect lease loss/split brain/worker restart/out-of-order
        callbacks and prevent stale commits/duplicate assets/cost. Evidence:
        race tests.
  - [ ] Y6.5.g Revalidate input/canon/source/policy/tool/dependency freshness
        before stage/commit and reject stale results. Evidence: stale-input
        tests.
- [ ] Y6.6 Implement permission-aware search across study and case content.
      Evidence: spoiler/protected-source leak tests.
  - [ ] Y6.6.a Define per-kind index documents/fields/classification for study
        sources/notes/materials/concepts and case
        entities/scenes/dialogue/clues/ canon/assets with exact revisions.
        Evidence: schemas.
  - [ ] Y6.6.b Build outbox-driven version/tombstone/permission/reveal/
        retraction/publication indexing and reconciliation. Evidence: lifecycle
        tests.
  - [ ] Y6.6.c Implement allowlisted query/filter/facet/sort/snippet/suggestion/
        cursor operations with locale analysis and current object/property
        authorization. Evidence: query tests.
  - [ ] Y6.6.d Exclude/redact protected source/solution/secret/reveal/future
        clue/ unpublished/canon dispute/playtest participant fields from
        indexes, counts, snippets, suggestions, caches, logs. Evidence: leak
        tests.
  - [ ] Y6.6.e Test actor reveal progression/role change/project switch/offline
        cache/stale index/colliding tenants/timing/count inference. Evidence:
        adversarial suite.
- [ ] Y6.7 Harden desktop IPC with an allowlisted, versioned, validated command
      surface. Evidence: arbitrary channel/message tests fail.
  - [x] Y6.7.a Inventory every renderer-preload-main/worker/native IPC channel/
        direction/message/handler/privilege/caller and eliminate unregistered
        channels. Evidence: IPC registry.
        <!-- yemaya-ipc-registry.json: IPC registry of the Yemaya studio-desktop
                                                                                                                                                                                                                                                                        (apps/yemaya/studio-desktop/src). 280 registered channels across 28 main
                                                                                                                                                                                                                                                                        modules (277 ipcMain.handle = invoke/bidirectional, 3 ipcMain.on =
                                                                                                                                                                                                                                                                        send/one-way). DIRECTIONAL orphan detection (the "eliminate unregistered
                                                                                                                                                                                                                                                                        channels" ask): split renderer→main REQUESTS (75 ipcRenderer.invoke/send)
                                                                                                                                                                                                                                                                        from renderer LISTENERS (23 ipcRenderer.on) from main→renderer EMISSIONS
                                                                                                                                                                                                                                                                        (24 webContents/sender.send). ORPHANS: 9 request channels invoked but NOT
                                                                                                                                                                                                                                                                        registered as handlers (bellona:connect/export/sync-assets, dialog:message/
                                                                                                                                                                                                                                                                        open-directory/open-file, ...) = the broken/unregistered channels to
                                                                                                                                                                                                                                                                        eliminate; 19 renderer listeners with no detected main emission (dead-listener
                                                                                                                                                                                                                                                                        candidates); 214 handlers with no LITERAL invoke (mostly internal/module-to-
                                                                                                                                                                                                                                                                        module). CRITICAL FIX: initially conflated events (bellona:engine-* via
                                                                                                                                                                                                                                                                        ipcRenderer.on) with broken invokes → 32 false orphans; separating by
                                                                                                                                                                                                                                                                        direction (invoke/send vs on vs main-send) gives the accurate 9. dynamicInvoke
                                                                                                                                                                                                                                                                        caveat recorded. generate-yemaya-ipc-registry.mjs + 6 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. Y6.7.b (typed preload API) + Y6.7.c (validation) are
                                                                                                                                                                                                                                                                        implementation/design and stay unchecked. -->
  - [ ] Y6.7.b Expose context-isolated preload API with stable versioned typed
        command/query/event capabilities and no raw ipcRenderer/Node/Electron
        objects. Evidence: preload tests.
  - [ ] Y6.7.c Validate channel/version/schema/size/depth/count/unknown fields,
        origin/window/webContents/session, actor/project/capability/permission
        at main process. Evidence: validation tests.
  - [ ] Y6.7.d Define structured errors/timeouts/cancellation/idempotency/
        concurrency/progress/event unsubscribe and protect secrets/paths.
        Evidence: protocol tests.
  - [ ] Y6.7.e Sandbox navigation/window creation/webviews/devtools/clipboard/
        shell/external URL/file/device/native modules according to policy.
        Evidence: Electron security tests.
  - [ ] Y6.7.f Fuzz arbitrary/unknown/nested/alias/mass-assignment/oversize/
        prototype/serialized object/replay/cross-window messages. Evidence:
        negative suite.
- [ ] Y6.8 Harden local file access with user grants, canonical paths, safe
      archives, symlink controls, and scoped bookmarks. Evidence: malicious
      filesystem corpus.
  - [ ] Y6.8.a Define typed open/save/import/export/project/cache/temp
        operation, allowed file/dir/profile, user gesture, purpose,
        read/write/create/ overwrite/delete scope, and lifecycle. Evidence: file
        contract.
  - [ ] Y6.8.b Obtain OS picker/grant/bookmark/token with opaque reference,
        project/actor/device binding, expiry/revocation and never trust renderer
        raw path. Evidence: grant tests.
  - [ ] Y6.8.c Canonicalize existing/nonexisting path parent, normalize Unicode/
        case/separators, enforce allowlisted roots/extensions, and reject
        traversal/absolute/device/network/ADS/special paths. Evidence: path
        corpus.
  - [ ] Y6.8.d Resolve/check every path component and reject symlink/junction/
        hardlink/alias/mount/race escape using safe open semantics. Evidence:
        link/TOCTOU tests.
  - [ ] Y6.8.e Parse archives with entry/count/depth/size/ratio/path/symlink/
        duplicate/case collision/encryption/hidden/executable protections into
        temp quarantine. Evidence: archive tests.
  - [ ] Y6.8.f Implement atomic save/rename/fsync/backup/overwrite confirmation/
        partial cleanup and hash/read-back. Evidence: write tests.
  - [ ] Y6.8.g Store scoped bookmarks/recents/local project grants securely,
        reauthorize on use, revoke/logout/device-loss cleanup, and redact logs.
        Evidence: bookmark tests.
- [ ] Y6.9 Harden protocol/deep-link handlers against command injection and
      cross-tenant replay. Evidence: negative suite.
  - [ ] Y6.9.a Define allowlisted custom/HTTPS link schemes,
        hosts/routes/intents, canonical grammar, minimal signed claims, max
        sizes, and version. Evidence: link registry.
  - [ ] Y6.9.b Parse without shell/interpolation, reject credentials/fragments/
        unknown params/duplicate keys/control chars/Unicode confusables/encoded
        traversal/nested URLs. Evidence: parser tests.
  - [ ] Y6.9.c Validate signature/key/issuer/audience/intent/tenant/project/
        subject/version/expiry/not-before/nonce/replay/host-instance before
        navigation/action. Evidence: validation tests.
  - [ ] Y6.9.d Reauthenticate/reauthorize current actor/object/property/function
        and require confirmation/step-up for allowed mutations; links never
        carry commands/scripts/paths/secrets. Evidence: authority tests.
  - [ ] Y6.9.e Handle single-instance startup/queued links/login continuation/
        window focus/concurrent links/expired/deleted objects safely. Evidence:
        lifecycle tests.
  - [ ] Y6.9.f Test shell metacharacters/URL injection/open redirect/replay/
        tenant substitution/role revoke/old links/offline cache. Evidence:
        negative suite.
- [ ] Y6.10 Harden updater, signing, and version rollback. Evidence: signature,
      channel, expiry, downgrade, and interrupted-update tests.
  - [ ] Y6.10.a Define signed update manifest/artifact/delta/full package with
        product/platform/arch/channel/version/min OS/server/schema/published/
        expiry/hash/size/URL/release notes/rollback rules. Evidence: schema.
  - [ ] Y6.10.b Verify HTTPS pinned/allowlisted source, manifest/artifact hash/
        signature/certificate/status/timestamp/key rotation/revocation before
        staging. Evidence: signing tests.
  - [ ] Y6.10.c Enforce stable/beta/internal channel authorization and prevent
        cross-channel injection, expired/future/stale manifests, mirror/
        redirect/DNS abuse. Evidence: channel tests.
  - [ ] Y6.10.d Enforce monotonic upgrade and explicit authorized compatible
        rollback; block version/schema/security downgrade and replay. Evidence:
        downgrade tests.
  - [ ] Y6.10.e Download resumably with size/disk/resource limits, verify, stage
        atomically, quiesce jobs/files/locks, migrate, restart, and health
        check. Evidence: update tests.
  - [ ] Y6.10.f Recover from network/disk/power/process/signature/install/
        migration/health failure to previous intact version/data with audit.
        Evidence: interrupted-update tests.
  - [ ] Y6.10.g Test old/new app-server-protocol-project compatibility and
        forced security update/offline behavior. Evidence: compatibility suite.
- [ ] Y6.11 Implement presence and collaboration across web and desktop.
      Evidence: reconnect, stale presence, and conflict journey.
  - [ ] Y6.11.a Use the shared collaboration protocol/CRDT/session/auth/version
        contracts through web transport and desktop adapter without forked
        semantics. Evidence: parity tests.
  - [ ] Y6.11.b Implement permission/spoiler-filtered presence/heartbeat/expiry/
        participant/tool/viewport and stable cursor/selection awareness across
        content/graph/timeline/media. Evidence: presence tests.
  - [ ] Y6.11.c Implement durable CRDT operations/snapshots/compaction/offline/
        schema migration with convergence and semantic conflict escalation.
        Evidence: sync tests.
  - [ ] Y6.11.d Reconnect from vector/cursor/snapshot with auth refresh/dedup/
        gap/order/role revoke/project lock and no duplicate edits. Evidence:
        reconnect tests.
  - [ ] Y6.11.e Mark/remove stale presence promptly without deleting edits or
        treating disconnect as lock release/approval/cancel. Evidence: stale
        tests.
  - [ ] Y6.11.f Run mixed web/desktop/version/partition/offline/conflict/
        cross-project/accessibility journey. Evidence: E2E.
- [ ] Y6.12 Implement anchored comments/suggestions on all stable anchor kinds.
      Evidence: anchor survival and orphan workflow.
  - [ ] Y6.12.a Define comment/thread/reply/suggestion/decision identity, exact
        subject revision/typed anchor/author/visibility/status/mentions/
        attachments/time. Evidence: schema.
  - [ ] Y6.12.b Support source/page/text, study/tree/concept, case/entity/scene/
        dialogue/clue/timeline/graph, asset/object/region/frame/timecode/shot/
        curve anchors. Evidence: kind matrix.
  - [ ] Y6.12.c Implement
        create/reply/edit/suggest/accept/reject/resolve/reopen/
        assign/mention/navigate with object/property/spoiler permissions and
        immutable history. Evidence: interaction tests.
  - [ ] Y6.12.d Map anchors on revision as exact/shifted/ambiguous/orphaned/
        invalid and preserve original context/hash plus reattach/dismiss
        workflow. Evidence: survival tests.
  - [ ] Y6.12.e Ensure suggestion acceptance creates a new authored revision and
        comments cannot approve/canonize/publish/change production state.
        Evidence: authority tests.
  - [ ] Y6.12.f Provide synchronized accessible comment/anchor list and test all
        kinds through web/desktop/offline/merge/locale changes. Evidence: E2E.
- [ ] Y6.13 Implement review assignments, quorum, conflicts, escalation, and
      notifications. Evidence: role-policy tests.
  - [ ] Y6.13.a Define review template/dimension/assignment/reviewer
        qualification/ locale/role/quorum/sequence/deadline/decision/freshness
        contracts. Evidence: schemas.
  - [ ] Y6.13.b Assign eligible reviewers by project/track/content/gate/risk/
        locale/skills/workload/availability with conflict/delegation/substitute
        rules. Evidence: assignment tests.
  - [ ] Y6.13.c Calculate quorum from independent eligible fresh decisions per
        dimension; handle reject/request-changes/abstain/timeout/reassignment.
        Evidence: quorum tests.
  - [ ] Y6.13.d Capture conflict-of-interest declaration/detected conflicts/
        recusal/mitigation/reviewer privacy and block incompatible signoff.
        Evidence: conflict tests.
  - [ ] Y6.13.e Compute SLA/business-calendar/quiet-hours/breach and escalate to
        owner/substitute/on-call without changing decision authority. Evidence:
        escalation tests.
  - [ ] Y6.13.f Send localized role-filtered in-app/email/webhook notifications
        with idempotency/preferences/urgent override/ack/redaction. Evidence:
        notification tests.
  - [ ] Y6.13.g Test exact subject/evidence change invalidation, system actor
        refusal, stale role, two-tenant, and time zones. Evidence: role-policy
        suite.
- [ ] Y6.14 Implement two-tenant/project isolation across APIs, search, blobs,
      jobs, desktop cache, playtests, and export. Evidence: integration suite.
  - [ ] Y6.14.a Seed two tenants/projects with colliding names/IDs-like slugs/
        tags/files/times/entities/assets/canon/solutions/actors/roles and
        separate keys/policies. Evidence: fixture.
  - [ ] Y6.14.b Exercise every API/IPC/deep-link/object/property/function/
        membership/role/guest path using foreign IDs/cursors/idempotency keys.
        Evidence: API tests.
  - [ ] Y6.14.c Exercise search/facets/counts/snippets/suggestions/caches/index
        lag and spoiler/protected-source/solution terms. Evidence: search tests.
  - [ ] Y6.14.d Exercise blob/original/proxy/rendition/package/signed URL/hash/
        dedupe/cache/temp/local desktop files/bookmarks/offline data. Evidence:
        storage/cache tests.
  - [ ] Y6.14.e Exercise queues/jobs/events/progress/cancel/retry/callback/
        provider/tool/worker/log/trace/cost and project switching. Evidence: job
        tests.
  - [ ] Y6.14.f Exercise playtest participant/config/build/session/telemetry/
        replay/feedback/consent/retention and reveal state. Evidence: playtest
        tests.
  - [ ] Y6.14.g Exercise project/study/case/asset/audit/standards/marketplace/
        evidence/export/package/download/notifications. Evidence: export tests.
  - [ ] Y6.14.h Inspect UI/error/timing/count/existence/client state/service
        worker/clipboard/recent/app updater channels for leakage. Evidence:
        integration suite.

### Y7 — Review, compilation, localization, publication, and catalog

- [ ] Y7.1 Define review templates for study content and cases. Evidence: source
      grounding, canon, solvability, safety, accessibility, rights,
      localization, and producer roles are explicit.
  - [ ] Y7.1.a Define versioned template
        identity/track/profile/risk/locale/target applicability,
        dimensions/checklists/evidence/roles/quorum/order/
        deadlines/expiry/floors. Evidence: schema.
  - [ ] Y7.1.b Define study source authority/currency/grounding/citation/
        structure/order/accessibility/rights/localization/consumer/producer
        review criteria. Evidence: study matrix.
  - [ ] Y7.1.c Define case canon/continuity/solvability/uniqueness/fairness/
        safety/age/accessibility/rights/localization/assets/engine/producer
        criteria. Evidence: case matrix.
  - [ ] Y7.1.d Resolve qualified
        SME/researcher/canon/solver/safety/accessibility/
        legal/localization/producer/publisher roles with separation/conflicts/
        quorum. Evidence: role tests.
  - [ ] Y7.1.e Bind decisions to exact candidate/evidence/template versions and
        invalidate affected signoffs on changes. Evidence: freshness tests.
  - [ ] Y7.1.f Prove project/season/tenant overlays can add/tighten but never
        remove code-required dimensions/roles. Evidence: property tests.
- [ ] Y7.2 Build the unified review inbox with track-specific saved views.
      Evidence: assignment, SLA, gate, and role filters.
  - [ ] Y7.2.a Define permission/spoiler-filtered queue item projection for
        exact
        candidate/project/track/revision/template/gates/assignment/deadline/
        priority/state. Evidence: contract.
  - [ ] Y7.2.b Build deterministic filters/facets/sort/pagination for track/
        profile/content kind/role/assignee/SLA/status/gate/risk/locale/release.
        Evidence: query tests.
  - [ ] Y7.2.c Build personal/team/track study/case/canon/solver/localization/
        production/release saved views with versioned query/columns/order/share/
        default. Evidence: saved-view tests.
  - [ ] Y7.2.d Build claim/reassign/decline/recuse/request info/open subject/
        review action with expected version, workload/conflict/permission/audit.
        Evidence: action tests.
  - [ ] Y7.2.e Present loading/empty/stale/blocked/overdue/degraded/offline/
        unauthorized without title/spoiler/count leakage. Evidence: state tests.
  - [ ] Y7.2.f Verify keyboard/screen-reader/responsive/live-update/large queue/
        two-tenant journey. Evidence: Playwright.
- [ ] Y7.3 Lock review candidates to exact content, canon, source, asset,
      evaluator, and policy versions. Evidence: dependency change invalidates
      signoff.
  - [ ] Y7.3.a Define candidate manifest containing exact project/content/tree/
        graph/timeline/case/solution/locale/build schema revisions and hashes.
        Evidence: manifest schema.
  - [ ] Y7.3.b Include exact canon assertions/namespaces/decisions, sources/
        extractions/claims/citations/rights, assets/renditions/provenance/
        continuity revisions. Evidence: dependency manifest tests.
  - [ ] Y7.3.c Include exact solver/compiler/engine/evaluator/model/corpus/
        config/gate/policy/threshold/profile versions and results. Evidence:
        evidence binding tests.
  - [ ] Y7.3.d Canonicalize/hash/lock manifest and bind assignment/comments/
        decisions/quorum/approvals/exception to digest. Evidence: lock tests.
  - [ ] Y7.3.e Rehash/revalidate on decision/compile/publish and mark approvals
        stale on any bound dependency/status/rights/policy change. Evidence:
        invalidation tests.
  - [ ] Y7.3.f Route changes to new candidate/revision and retain historical
        locked candidates/signoffs. Evidence: immutability tests.
- [ ] Y7.4 Compile study read models from approved authoring revisions.
      Evidence: deterministic golden bundle.
  - [ ] Y7.4.a Resolve exact approved study candidate/hierarchy/materials/
        sources/concepts/order/activities/assets/locales/accessibility/target
        and reject missing/stale dependencies. Evidence: input tests.
  - [ ] Y7.4.b Normalize/validate content blocks, stable IDs/links/anchors/
        citations/rights, reading order/prerequisites, locale fallback, and
        consumer capabilities. Evidence: compile tests.
  - [ ] Y7.4.c Derive navigation/content/search/concept/glossary/activity/media/
        accessibility/provenance models and exclude author/review/protected
        fields. Evidence: projection goldens.
  - [ ] Y7.4.d Serialize deterministic manifest/files/order/numbers/Unicode/
        timestamps/config with compiler/version/input hashes. Evidence:
        determinism tests.
  - [ ] Y7.4.e Validate bundle schema/links/hashes/rights/gates and rebuild/read
        through consumer workspace. Evidence: golden/read-back tests.
- [ ] Y7.5 Compile case bundles through the real case compiler and pipeline.
      Evidence: deterministic golden case.
  - [ ] Y7.5.a Resolve exact approved locked case/premise/entities/canon/source/
        scenes/dialogue/timeline/clues/solution/branch/state/assets/locales/
        profile/target. Evidence: input tests.
  - [ ] Y7.5.b Invoke canonical real case compiler through public contract with
        pinned version/config, durable job/checkpoints/progress/cancel/errors
        and no workbench substitute. Evidence: integration tests.
  - [ ] Y7.5.c Compile stable runtime world/entity/scene/dialogue/timeline/clue/
        state/branch/rule/hint/save/ending models and spoiler partitions.
        Evidence: compiler goldens.
  - [ ] Y7.5.d Run canonical production pipeline for assets/renditions/
        localization/accessibility/runtime/package/provenance with exact
        dependencies. Evidence: pipeline tests.
  - [ ] Y7.5.e Serialize deterministic bundle/manifest/hashes/order/IDs/config/
        tool versions and validate through real engine loader. Evidence: golden
        case/read-back.
- [ ] Y7.6 Validate case bundles with engine, solver, canon, localization,
      asset, and schema gates. Evidence: broken-bundle corpus.
  - [ ] Y7.6.a Validate bundle/manifest/schema/version/files/hashes/refs/IDs/
        types/cardinality/size/resource/safe package and runtime compatibility.
        Evidence: schema fixtures.
  - [ ] Y7.6.b Load/run with real hermetic engine for init/navigation/state/
        branches/clues/dialogue/save/load/reset/ending/error/performance and no
        protected partition leak. Evidence: engine fixtures.
  - [ ] Y7.6.c Run solver reachability/existence/uniqueness/alternative/fair
        timing/dead-end/soft-lock/timeline traces on compiled bundle, not source
        alone. Evidence: solver fixtures.
  - [ ] Y7.6.d Validate canon entity/assertion/namespace/temporal/knowledge/
        continuity and exact compiled-to-authoring mapping. Evidence: canon
        fixtures.
  - [ ] Y7.6.e Validate every locale’s structure/IDs/branches/clues/solution/
        timing/text/assets/accessibility/spoiler partitions and fallback.
        Evidence: locale fixtures.
  - [ ] Y7.6.f Validate every asset/rendition/dependency/hash/format/target/
        technical/right/consent/provenance/gate and no quarantined inputs.
        Evidence: asset fixtures.
  - [ ] Y7.6.g Preserve raw results separately, require all mandatory gates, and
        maintain broken-bundle corpus for each failure family. Evidence:
        coverage gate.
- [ ] Y7.7 Build localization packages with spoiler segmentation and role
      restrictions. Evidence: translators see only authorized context.
  - [ ] Y7.7.a Define locale package/job/unit/segment/source-target/revision/
        role/spoiler tier/context/asset/status/manifest contracts. Evidence:
        schemas.
  - [ ] Y7.7.b Segment study/case text/metadata/dialogue/clues/hints/reveals/
        solution/UI/assets by stable ID, required context, translation memory/
        glossary, and spoiler boundary. Evidence: segmentation tests.
  - [ ] Y7.7.c Generate minimal role/assignment/locale/segment-scoped package
        with only authorized context/source/canon/character/timing/asset
        references, pseudonyms where needed. Evidence: minimization tests.
  - [ ] Y7.7.d Encrypt/sign/hash/package, issue expiring access, disable
        download/ copy as policy permits, watermark, audit, and revoke.
        Evidence: security tests.
  - [ ] Y7.7.e Import translated units/assets with stable IDs, source revision/
        TM/glossary/provenance, validation/conflicts, reviewer decision, and no
        hidden solution fields. Evidence: import tests.
  - [ ] Y7.7.f Test translator/reviewer/solver/producer roles, escalation for
        additional context, cross-locale/package/tenant and expired/revoked
        access. Evidence: boundary suite.
- [ ] Y7.8 Build locale parity gates for structure, clues, solution,
      accessibility, assets, and timing. Evidence: known mismatch fixtures.
  - [ ] Y7.8.a Resolve source/target locale bundles and exact structure/entity/
        scene/branch/clue/solution/asset/timebase/accessibility revisions and
        profile. Evidence: inputs.
  - [ ] Y7.8.b Compare stable hierarchy/order/IDs/refs/conditions/effects/
        branches/reachability and detect added/removed/orphaned structure.
        Evidence: structure fixtures.
  - [ ] Y7.8.c Compare clue/red-herring/interpretation/dependency/weight/
        discovery/reveal/spoiler and solution/proof/alternative/ending
        semantics. Evidence: case fixtures.
  - [ ] Y7.8.d Validate translated
        text/dialogue/terminology/pronunciation/canon/ source/citation/meaning
        and intentional adaptations with review. Evidence: language tests.
  - [ ] Y7.8.e Validate alt/long descriptions/captions/transcripts/sign/
        noninteractive alternatives/keyboard/reading order and locale metadata.
        Evidence: accessibility fixtures.
  - [ ] Y7.8.f Validate asset
        role/version/locale/renditions/graphics/audio/voice/ rights/consent and
        duration/timecode/dialogue/audio/branch timing. Evidence: asset/timing
        fixtures.
  - [ ] Y7.8.g Persist raw mismatches/uncertainty/N/A/adaptations separately and
        block unresolved mandatory parity. Evidence: parity gate.
- [ ] Y7.9 Publish through plan, execute, read-back verify, and compensate.
      Evidence: partial target failure recovery.
  - [ ] Y7.9.a Define immutable per-target release plan with exact locked
        bundle/ candidate/revisions/hashes, prerequisites, destination
        IDs/config, steps/compensation/estimate/idempotency. Evidence: schema.
  - [ ] Y7.9.b Revalidate approvals/gates/canon/source/rights/assets/locales/
        target/capability/embargo/authorization immediately before confirmation
        and each write. Evidence: TOCTOU tests.
  - [ ] Y7.9.c Execute durable target steps idempotently with checkpoints/
        receipts/partial state/retries/timeouts/cancel and no hidden success.
        Evidence: execution tests.
  - [ ] Y7.9.d Read back authoritative target bytes/manifest/hash/metadata/
        visibility/links/runtime/consumer models and mark success only after
        verification. Evidence: read-back tests.
  - [ ] Y7.9.e Reconcile interruption/unknown destination state before retry;
        compensate eligible successful writes in dependency-safe order and
        verify. Evidence: recovery tests.
  - [ ] Y7.9.f Preserve per-target succeeded/failed/unknown/compensated/residual
        state, all attempts/cost/audit/notifications. Evidence: partial failure
        E2E.
- [ ] Y7.10 Build release calendar for study drops, case episodes, playtests,
      localization, and production capacity. Evidence: dependency and time-zone
      tests.
  - [ ] Y7.10.a Define versioned event/resource/reservation/dependency contracts
        for study drop/case episode/playtest/locale/review/build/release/
        embargo/capacity with time zone. Evidence: schema.
  - [ ] Y7.10.b Build month/week/list/timeline views with filters, role/spoiler
        projection and keyboard/non-drag create/move/resize. Evidence: UI tests.
  - [ ] Y7.10.c Build dependency DAG/backward plan for source/canon/content/
        asset/solver/review/localize/compile/validate/publish/verify tasks and
        buffers. Evidence: dependency tests.
  - [ ] Y7.10.d Reserve authors/solvers/reviewers/localizers/producers/stages/
        providers/render/compile/targets/budget and reject committed
        overbooking. Evidence: capacity tests.
  - [ ] Y7.10.e Monitor freshness/slip/conflict/risk and authorize reschedule/
        scope/fallback/hold/cancel through versioned impact decision. Evidence:
        contingency tests.
  - [ ] Y7.10.f Test IANA zones/DST/recurrence/quiet hours/embargo/dependency/
        reserved slot/actuals and scheduled release read-back. Evidence: suite.
- [ ] Y7.11 Build study collection and case-season curation through bundles.
      Evidence: no direct catalog writes.
  - [ ] Y7.11.a Define collection/season/bundle version,
        purpose/audience/locale/ owner/schedule/ordered exact study/case
        episode/build members/metadata/ targets/lifecycle. Evidence: schema.
  - [ ] Y7.11.b Build draft add/remove/reorder/group/feature/episode numbering/
        arc/dependency/locale/metadata editor and eligibility checks. Evidence:
        curation tests.
  - [ ] Y7.11.c Validate member availability/target/rights/canon/source/gates/
        reviews/spoiler/order/prerequisite/season continuity and steering
        policy. Evidence: membership tests.
  - [ ] Y7.11.d Preview deterministic catalog/consumer/search/navigation/order/
        schedule/metadata/add/remove diffs and impacted links. Evidence:
        preview.
  - [ ] Y7.11.e Review/approve/release exact bundle through governed
        publication; prohibit direct catalog/search/consumer write. Evidence:
        boundary tests.
  - [ ] Y7.11.f Preserve historical releases and propagate member supersession/
        recall/canon/source changes via new bundle/incident. Evidence:
        lifecycle.
- [ ] Y7.12 Build supersession, correction, unpublish, archive, recall, and
      canon hotfix paths. Evidence: customer read models and audit reconcile.
  - [ ] Y7.12.a Define distinct command/state/reason/authority/scope/effective-
        time/targets/notification/verification contracts for every lifecycle
        path. Evidence: schemas.
  - [ ] Y7.12.b Supersede with eligible replacement exact revision/bundle,
        effective behavior/migration/redirect/prior availability/consumer
        notice. Evidence: supersession tests.
  - [ ] Y7.12.c Correct factual/typographic/localization/metadata/content via
        new revisions, source/canon evidence, review/build and correction
        notice, never historical mutation. Evidence: correction tests.
  - [ ] Y7.12.d Unpublish target visibility/links/downloads/search/catalog/
        consumer models/caches with receipts while preserving authoring/release
        evidence. Evidence: unpublish tests.
  - [ ] Y7.12.e Archive inactive authoring/catalog records with retention/
        dependencies/authorized history/restore and no accidental delivery.
        Evidence: archive tests.
  - [ ] Y7.12.f Recall affected source/canon/asset/bundle/release through blast
        radius, quarantine/disable/removal/notification/verification/residual
        copies. Evidence: recall tests.
  - [ ] Y7.12.g Apply canon hotfix as scoped authorized assertion/content/build
        revision with solver/continuity/localization/asset impact and expedited
        but nonwaived gates. Evidence: hotfix tests.
  - [ ] Y7.12.h Rebuild/reconcile customer read/search/catalog/cache/links/state
        from authoritative events/bundles and compare audit. Evidence: lifecycle
        E2E.
- [ ] Y7.13 Build deep links from released items to authorized source,
      provenance, and authoring history. Evidence: role-based disclosure tests.
  - [ ] Y7.13.a Define allowlisted link intents/routes for released study/case/
        asset/bundle to source/citation/provenance/lineage/revision/decision/
        authoring views with minimal claims. Evidence: schema.
  - [ ] Y7.13.b Issue signed opaque tenant/project/subject/version/audience/
        intent/expiry/nonce links only after source authorization. Evidence:
        issuance tests.
  - [ ] Y7.13.c Revalidate signature/time/replay/host/route and current actor/
        object/property/function/reveal/embargo/source rights at open. Evidence:
        validation tests.
  - [ ] Y7.13.d Project learner/customer/participant/author/reviewer/producer/
        operator fields separately, redacting protected source/solution/spoiler/
        private authoring/identity details. Evidence: disclosure tests.
  - [ ] Y7.13.e Handle logged-out/expired/revoked/deleted/superseded/recalled/
        cross-tenant links, back/return context, referrer/log scrubbing.
        Evidence: lifecycle tests.
- [ ] Y7.14 Build post-release feedback intake without silently modifying canon
      or source truth. Evidence: triage-to-revision journey.
  - [ ] Y7.14.a Define feedback/report identity, released item/build/revision/
        locale/target, actor/anonymous/consent, category/severity, text/media/
        anchor, visibility, status, and provenance. Evidence: schema.
  - [ ] Y7.14.b Ingest consumer/support/playtest/reviewer/partner/analytics/
        correction/safety/rights/accessibility feedback with rate/abuse/PII/
        spoiler controls. Evidence: intake tests.
  - [ ] Y7.14.c Triage/deduplicate/cluster/link sources/canon/issues with
        attributed rules/models, uncertainty, counterevidence and human
        priority/owner. Evidence: triage tests.
  - [ ] Y7.14.d Route to source update, canon proposal, content/locale/asset/
        build revision, bug, incident, support, reject/defer decision; no direct
        truth/canon edit. Evidence: authority tests.
  - [ ] Y7.14.e Require normal source/canon/review/gate/build/release lifecycle
        and link outcome/decision/notification back. Evidence: E2E.
- [ ] Y7.15 Build program/season/collection steering overlays for cadence,
      difficulty, canon, source, safety, production, and release constraints.
      Evidence: tighten-only property tests and impact preview.
  - [ ] Y7.15.a Define versioned overlay scope/priority/effective dates/owner
        and typed
        cadence/difficulty/canon/source/safety/production/budget/audience/
        locale/release fields. Evidence: schema.
  - [ ] Y7.15.b Resolve platform/region/organization/program/season/collection/
        project/profile precedence with conflict/cycle detection. Evidence:
        resolution tests.
  - [ ] Y7.15.c Merge by narrower eligibility/allowed canon/source, stricter
        gates/thresholds/reviewer/limits, lower budget/capacity, or additional
        requirements only. Evidence: monotonicity tests.
  - [ ] Y7.15.d Reject omission/default/inheritance/alias attempts to loosen
        safety/rights/privacy/accessibility/canon/source/authority/code floors.
        Evidence: property tests.
  - [ ] Y7.15.e Preview affected
        content/cases/clues/difficulty/schedules/assets/
        pipelines/budgets/locales/builds/releases/consumers/conflicts. Evidence:
        impact tests.
  - [ ] Y7.15.f Review/approve/version/schedule/deactivate/rollback and
        revalidate dependent state without history rewrite. Evidence: lifecycle
        tests.
- [ ] Y7.16 Build UI-governed study, case, genre, clue, audience, difficulty,
      production, and catalog taxonomies over code-owned floors. Evidence:
      versioned review, alias, deprecation, localization, and migration journey.
  - [ ] Y7.16.a Define separate typed registry entry/version/relationship/
        lifecycle contracts and stable codes for every named taxonomy. Evidence:
        schemas.
  - [ ] Y7.16.b Declare code-owned noneditable identities/semantics/required
        fields/forbidden relations/safety-fairness-rights-accessibility/profile
        floors. Evidence: floor tests.
  - [ ] Y7.16.c Build restrained operator list/detail/create-draft/edit/diff/
        validation/dependency/usage UI with typed fields and no arbitrary JSON.
        Evidence: authoring tests.
  - [ ] Y7.16.d Build alias/synonym/parent/related/mapping with
        scope/uniqueness/ collision/cycle/semantic equivalence and no silent
        merge. Evidence: relation tests.
  - [ ] Y7.16.e Build locale label/description/help/search terms/fallback/
        completeness/translator context without localized identity codes.
        Evidence: localization tests.
  - [ ] Y7.16.f Review/approve/publish/deprecate/supersede/archive exact
        versions with effective dates, owner, audit, compatibility checks.
        Evidence: lifecycle tests.
  - [ ] Y7.16.g Preview/execute resumable dependency migrations across projects/
        content/cases/assets/templates/overlays/builds/catalogs with conflicts/
        rollback/history. Evidence: migration tests.
  - [ ] Y7.16.h Verify unknown/deprecated/missing entries fail/degrade safely in
        every consumer/export and run accessibility/performance/drift tests.
        Evidence: registry journey.

### Y8 — Yemaya UX, accessibility, offline, and performance

- [ ] Y8.1 Add study and case entries to the existing Yemaya project navigation.
      Evidence: no second global shell.
  - [ ] Y8.1.a Register study/case routes, labels/icons/keywords/capabilities/
        project profiles/roles in canonical project navigation/command palette.
        Evidence: IA registry tests.
  - [ ] Y8.1.b Show entries only for compatible project type/track/entitlement/
        role/capability and safe unavailable/degraded states. Evidence:
        visibility tests.
  - [ ] Y8.1.c Preserve current project/revision/entity/return context and dirty
        state warnings across entry/exit/deep links. Evidence: navigation tests.
  - [ ] Y8.1.d Reuse existing global/project shell, breadcrumbs, switcher,
        identity/notifications/search/help and remove duplicate nav headers.
        Evidence: shell conformance.
  - [ ] Y8.1.e Test direct authorized/unauthorized routes, project
        switch/profile migration, web/desktop, keyboard/screen reader. Evidence:
        route E2E.
- [ ] Y8.2 Build project dashboards around actionable work, not a card mosaic.
      Evidence: frontend conformance review.
  - [ ] Y8.2.a Define primary user triage questions and live view models for
        assignments/blockers/gate
        failures/jobs/reviews/schedule/budget/incidents/ releases per profile.
        Evidence: dashboard contract.
  - [ ] Y8.2.b Compose a restrained hierarchy with actionable queue/production
        state first, recent project context and secondary metrics second, no
        hero or repeated container cards. Evidence: design review.
  - [ ] Y8.2.c Implement stable filters/sorts/grouping/URLs/freshness/live
        updates and clear owner/deadline/severity/status/units. Evidence: UI
        tests.
  - [ ] Y8.2.d Implement eligible inline claim/assign/open/remediate/retry/hold/
        review actions with permission/state/version/reason/confirmation.
        Evidence: action tests.
  - [ ] Y8.2.e Render
        loading/empty/stale/degraded/offline/fixture/error/partial/ unauthorized
        honestly and verify responsive/accessibility/scale. Evidence: frontend
        conformance E2E.
- [ ] Y8.3 Build workspaces with primary editor, navigation, inspector, and
      visible gate/job status. Evidence: responsive browser snapshots.
  - [ ] Y8.3.a Define shared workspace shell contract for project/context nav,
        primary canvas/editor, contextual inspector, status rail, command
        palette, focus/selection/dirty/capability state. Evidence: shell spec.
  - [ ] Y8.3.b Compose study source/material/order and case
        dossier/entity/graph/ timeline/scene/solution editors using the shared
        shell. Evidence: workspace tests.
  - [ ] Y8.3.c Compose AAA script/board/design/schedule/budget/asset/dailies/
        pipeline/marketplace/admin workspaces without nested global shells.
        Evidence: production tests.
  - [ ] Y8.3.d Keep applicable gates/freshness/blockers/remediation and jobs/
        progress/cost/cancel/failure visible without obscuring the primary work.
        Evidence: status tests.
  - [ ] Y8.3.e Define wide/compact/single-column layouts with inspector/status
        drawer/tab, persistent subject identity, safe focus and no unusable
        squashing. Evidence: snapshots.
- [ ] Y8.4 Preserve route, project, selected entity, tab, graph/timeline
      viewport, and inspector state in deep links. Evidence: refresh/share
      tests.
  - [ ] Y8.4.a Define canonical allowlisted URL state schema/version for route/
        project/track/entity/revision/tab/selection/filter/graph-camera/timeline-
        range/inspector with size/privacy limits. Evidence: link schema.
  - [ ] Y8.4.b Encode stable opaque IDs and bounded viewport/range values while
        excluding secrets/prompts/protected source/solution/local paths/unsaved
        content. Evidence: serialization tests.
  - [ ] Y8.4.c Parse/validate/canonicalize and reauthorize every referenced
        object/property/function; handle stale/deleted/moved/revoked state.
        Evidence: restoration tests.
  - [ ] Y8.4.d Restore after refresh/back/forward/share/new window/web-desktop
        handoff and reconcile local unsaved state/conflicts safely. Evidence:
        navigation tests.
  - [ ] Y8.4.e Test cross-tenant/project substitution, oversized/unknown/bad
        viewport, role/reveal change, offline cache, link expiry. Evidence:
        share tests.
- [ ] Y8.5 Provide semantic alternatives for graphs, timelines, rendered
      previews, and playback. Evidence: keyboard/screen-reader journeys.
  - [ ] Y8.5.a Define operation/content parity requirements and synchronized
        selection/focus/status between each visual surface and semantic
        alternative. Evidence: parity matrix.
  - [ ] Y8.5.b Provide graph tree/table/list/forms for nodes/edges/types/
        properties/evidence/errors/path/relationships and all edit/navigation
        operations. Evidence: graph tests.
  - [ ] Y8.5.c Provide timeline ordered/grouped event table/forms with absolute/
        relative/uncertain time, duration, participants/location/evidence/
        conflicts and editing. Evidence: timeline tests.
  - [ ] Y8.5.d Provide rendered image/video/3D/audio source/scene/object/frame/
        time/track/measurement/annotation list/table and controls. Evidence:
        preview tests.
  - [ ] Y8.5.e Provide playback native controls, transcript/captions/audio
        descriptions/chapter/shot/track/marker lists and synchronized keyboard
        navigation. Evidence: playback tests.
  - [ ] Y8.5.f Verify equivalent inspect/comment/edit/compare/decide operations,
        screen-reader announcements, zoom/reflow, large data performance.
        Evidence: semantic E2E.
- [ ] Y8.6 Provide non-drag alternatives for hierarchy, graph, clue, scene, and
      timeline editing. Evidence: WCAG 2.2 dragging coverage.
  - [ ] Y8.6.a Inventory every drag/drop/pan/reorder/connect/move/resize/trim/
        range operation across named editors and map equivalent outcomes.
        Evidence: drag inventory.
  - [ ] Y8.6.b Add move before/after/into/out/first/last/position commands/forms
        for hierarchy/scene/clue order with destination preview. Evidence:
        reorder tests.
  - [ ] Y8.6.c Add select source/target/relation/ports/condition forms and
        keyboard commands for graph/clue connections and node movement.
        Evidence: graph tests.
  - [ ] Y8.6.d Add date/time/duration/order/lane/range numeric/text forms and
        keyboard step commands for timeline events/clips/markers. Evidence:
        timeline tests.
  - [ ] Y8.6.e Keep validation/impact/permissions/concurrency/undo/audit
        identical between pointer and non-drag paths. Evidence: parity tests.
  - [ ] Y8.6.f Run WCAG keyboard/screen-reader/touch/switch-friendly journeys
        for every operation with focus restoration/announcement. Evidence:
        coverage.
- [ ] Y8.7 Implement autosave/offline drafts with visible sync, conflict, and
      recovery. Evidence: disconnect/reconnect and two-client conflict tests.
  - [ ] Y8.7.a Define draft/local operation/server revision/sync checkpoint/
        device/session/schema/encryption/retention contracts and eligible
        content types. Evidence: schema.
  - [ ] Y8.7.b Implement debounced idempotent autosave with expected revision,
        dirty/saving/saved/error/conflict/offline indicators and unload/crash
        recovery. Evidence: autosave tests.
  - [ ] Y8.7.c Store offline drafts/CRDT operations/assets securely with size/
        quota/expiry/device/logout/project-access controls and no unencrypted
        spoilers/secrets. Evidence: cache tests.
  - [ ] Y8.7.d Replay/rebase/deduplicate on reconnect after current
        auth/project/ lock/schema/source/canon validation; never silently
        last-write-wins. Evidence: reconnect tests.
  - [ ] Y8.7.e Present mine/theirs/base semantic conflict and safe merge/manual
        resolution, preserve copies/export, and create new revisions. Evidence:
        conflict tests.
  - [ ] Y8.7.f Test two clients/web-desktop/offline edits/reorder/delete/role
        revoke/project archive/quota/crash/reconnect. Evidence: E2E.
- [ ] Y8.8 Define web and desktop performance budgets for large projects,
      graphs, timelines, assets, video, and search. Evidence: profiling reports.
  - [ ] Y8.8.a Define shared client/server/end-to-end load/interaction/save/
        search/preview/playback latency, CPU/memory/GPU/disk/network/payload/
        query/error budgets per device/network/profile. Evidence: budget spec.
  - [ ] Y8.8.b Define production-scale project/entity/revision/collaborator/
        source/asset/job/comment/locale/search distributions and representative
        fixtures. Evidence: fixture manifest.
  - [ ] Y8.8.c Define graph/timeline nodes/edges/events/paths/annotations/
        updates/layout/validation budgets and measurement points. Evidence:
        editor budgets.
  - [ ] Y8.8.d Define asset/gallery/proxy/large image/video/audio/3D
        load/decode/ scrub/render/cache/disk/memory/GPU/network budgets by
        web/desktop. Evidence: media budgets.
  - [ ] Y8.8.e Define search indexing lag/query/facet/suggestion/pagination/
        cache/permission overhead and large-project save/sync/reconnect/export
        budgets. Evidence: data budgets.
  - [ ] Y8.8.f Profile supported scenarios, capture client/server traces/query
        plans/resource graphs, trend baselines, and fail regressions. Evidence:
        reports.
- [ ] Y8.9 Virtualize large lists and graphs without breaking accessibility or
      selection. Evidence: high-volume tests.
  - [ ] Y8.9.a Inventory virtualized candidates and define stable item/node/edge
        identity/order/size/focus/selection/scroll/announcement/search
        semantics. Evidence: virtualization spec.
  - [ ] Y8.9.b Implement lists/tables/trees/grids with overscan/dynamic size/
        grouped rows/sticky headers/infinite cursor loading and no duplicate/
        missing entries. Evidence: list tests.
  - [ ] Y8.9.c Implement graph semantic view and visual culling/LOD/tile/layout/
        cluster/incremental updates without dropping reachable selected entities
        or validation. Evidence: graph tests.
  - [ ] Y8.9.d Preserve focus/active
        descendant/selected/expanded/anchor/keyboard index and screen-reader
        position/count when items unmount/remount. Evidence: accessibility
        tests.
  - [ ] Y8.9.e Preserve URL selection/scroll/viewport across filter/sort/update/
        pagination/refresh/back/reconnect and handle deleted targets. Evidence:
        state tests.
  - [ ] Y8.9.f Load-test high-volume pointer/keyboard/screen-reader/search/live
        updates and memory over sustained use. Evidence: performance tests.
- [ ] Y8.10 Add reduced-motion behavior to route, panel, graph, and timeline
      transitions. Evidence: automated assertions.
  - [ ] Y8.10.a Inventory route/page/panel/dialog/menu/toast/loading/progress/
        graph layout/pan-zoom/highlight/timeline scroll/playhead/drag/preview
        animations. Evidence: motion inventory.
  - [ ] Y8.10.b Define semantic state change/feedback purpose and reduced-motion
        replacement as instant, short fade, static highlight, manual control, or
        removal. Evidence: behavior spec.
  - [ ] Y8.10.c Honor OS/browser/app preference before first paint across web/
        desktop and update live without layout/focus loss. Evidence: preference
        tests.
  - [ ] Y8.10.d Disable parallax/spring/large translation/auto pan/continuous
        layout/animated zoom and provide nonmotion graph/timeline state changes.
        Evidence: component tests.
  - [ ] Y8.10.e Preserve essential progress/playback/time/capture indicators
        with accessible text and user-controlled motion. Evidence: parity tests.
  - [ ] Y8.10.f Assert computed styles/animation frames/scroll behavior across
        routes/panels/graphs/timelines/themes/platforms. Evidence: automation.
- [ ] Y8.11 Test themes, density, zoom, RTL, long locale, and high-contrast
      modes. Evidence: visual/a11y matrix.
  - [ ] Y8.11.a Define supported light/dark/system/high-contrast themes,
        compact/ comfortable/touch density, 100–400% zoom/reflow,
        RTL/bidi/long/CJK locales, and OS contrast modes. Evidence: matrix.
  - [ ] Y8.11.b Test color tokens/contrast/focus/selection/status/charts/graphs/
        media annotations/canvas-semantic alternatives in every theme. Evidence:
        theme tests.
  - [ ] Y8.11.c Test density/touch targets/spacing/editor controls/tables/
        timeline/graph labels and user preference persistence. Evidence: density
        tests.
  - [ ] Y8.11.d Test browser/app/text zoom/reflow without clipping/overlap/
        hidden controls/two-dimensional scroll except essential surfaces.
        Evidence: zoom tests.
  - [ ] Y8.11.e Test RTL navigation/order/icons/bidi isolation/mixed script/
        timeline/graph/tree/forms and long/expanding labels/dialogue/captions.
        Evidence: locale tests.
  - [ ] Y8.11.f Run visual regression plus automated/manual accessibility and
        keyboard/screen-reader tests across web/desktop/viewports. Evidence:
        report matrix.
- [ ] Y8.12 Surface fixture, offline, stale, degraded, unavailable, and failed
      state consistently across web and desktop. Evidence: state matrix.
  - [ ] Y8.12.a Define canonical data-origin/service-state discriminators, safe
        messages/timestamps/reasons/actions/recovery/announcements and
        telemetry. Evidence: state contracts.
  - [ ] Y8.12.b Label fixture/demo/synthetic content persistently and prevent
        production mutation/promotion/build/release. Evidence: fixture tests.
  - [ ] Y8.12.c Show offline cached versus unavailable, sync/queued edits/
        prohibited actions/freshness/reconnect and no simulated network success.
        Evidence: offline tests.
  - [ ] Y8.12.d Show stale subject/dependency/evidence/capability with exact
        invalidating version and block unsafe actions. Evidence: stale tests.
  - [ ] Y8.12.e Show per-capability degraded/partial/unavailable/not-configured/
        failed/unknown with safe alternatives and no global healthy implication.
        Evidence: service tests.
  - [ ] Y8.12.f Exercise each state/recovery across every critical web/desktop
        route with identical semantics and accessible announcements. Evidence:
        state matrix.
- [ ] Y8.13 Reconcile navigation and command-palette reachability for every
      retained AAA, production, marketplace, organization, study, and case
      capability. Evidence: route/role/project-type matrix has no reachable
      unauthorized item and no authorized orphan capability.
  - [ ] Y8.13.a Generate retained atomic
        capability/owner/host/route/project-type/
        role/entitlement/capability/command registry from Y0.16/Y5A and source
        routes. Evidence: reachability manifest.
  - [ ] Y8.13.b Register each authorized page/action/deep link/command exactly
        once in existing shell/project nav/palette with contextual
        prerequisites. Evidence: IA tests.
  - [ ] Y8.13.c Compute expected-versus-observed visibility/reachability for
        every role/project type/web-desktop/capability state and classify
        unauthorized exposure/orphan/duplicate/stale/misroute. Evidence: matrix.
  - [ ] Y8.13.d Test navigation hides unauthorized items without hint leaks and
        server rejects direct route/command/field/function attempts. Evidence:
        auth tests.
  - [ ] Y8.13.e Test authorized items resolve to owned live/degraded route with
        correct project context/back path and no second shell/duplicate
        mutation. Evidence: positive tests.
  - [ ] Y8.13.f Fail CI on unreviewed retained capability, authorized orphan,
        reachable unauthorized item, unknown registry row, or expired
        disposition. Evidence: route/role/profile gate.
- [ ] Y8.14 Provide responsive review/incident projections for scripts,
      storyboards, assets, dailies, budgets, schedules, pipeline runs,
      marketplaces, study, and cases while reserving precision editing for
      capable hosts. Evidence: mobile/tablet critical-action journeys.
  - [ ] Y8.14.a Define mobile/tablet review versus precision-edit capability
        matrix, subject identity/context, eligible actions, step-up, and
        desktop/ host handoff per named surface. Evidence: responsive spec.
  - [ ] Y8.14.b Build script/storyboard semantic read/diff/comments/review,
        asset/dailies preview/annotations/gates/decisions, and study/case
        content/ canon/solver review projections. Evidence: review tests.
  - [ ] Y8.14.c Build budget/schedule
        current-versus-baseline/variance/conflicts/ approvals and pipeline
        run/plan/progress/cost/failure/pause/cancel/ incident projections.
        Evidence: operations tests.
  - [ ] Y8.14.d Build marketplace item/security/license/compatibility/report/
        takedown and incident scope/evidence/actions/status views with protected
        data controls. Evidence: marketplace/incident tests.
  - [ ] Y8.14.e Implement comment/approve/reject/request changes/hold/escalate/
        pause/cancel/kill/ack only for current role/state with impact/reason/
        confirmation/concurrency/audit. Evidence: action tests.
  - [ ] Y8.14.f Provide semantic graph/timeline/3D/curve snapshots/tables
        instead of full precision editors and signed open-on-desktop/host return
        links. Evidence: handoff tests.
  - [ ] Y8.14.g Test touch/keyboard/screen reader/zoom/orientation/safe
        area/slow- offline/reconnect/direct authorization/role revoke across
        representative critical journeys. Evidence: E2E.

### Y9 — Yemaya verification, operations, and launch

- [ ] Y9.1 Run contract and migration compatibility tests for case, study,
      project, canon, and release schemas. Evidence: old-version corpus.
  - [ ] Y9.1.a Assemble supported historical case/study/project/canon/release
        records, events, blobs, indexes, manifests, and client payloads with
        provenance and expected outcomes. Evidence: version corpus.
  - [ ] Y9.1.b Test forward migrations one version at a time and from every
        supported direct upgrade point without silent defaulting or data loss.
        Evidence: migration matrix.
  - [ ] Y9.1.c Test current writers with supported old readers and old writers
        with current readers at explicit compatibility boundaries. Evidence:
        mixed-version tests.
  - [ ] Y9.1.d Verify identifiers, ordering, uncertainty, evidence, permissions,
        reveal state, checksums, and release signatures survive round trips.
        Evidence: semantic diffs.
  - [ ] Y9.1.e Test interrupted/retried migrations, rollback or restore,
        malformed/unknown fields, and unsupported versions fail safely.
        Evidence: failure tests.
  - [ ] Y9.1.f Record compatibility window, irreversible steps, operator
        prerequisites, and corpus ownership in the release gate. Evidence:
        compatibility report.
- [ ] Y9.2 Run known-value source, hierarchy, reading-order, canon, timeline,
      solvability, uniqueness, fairness, reachability, and spoiler tests.
      Evidence: gate registry coverage.
  - [ ] Y9.2.a Curate positive, boundary, counterexample, adversarial, and
        intentionally indeterminate fixtures with expert-reviewed expected
        measurements and verdicts. Evidence: known-value corpus.
  - [ ] Y9.2.b Verify source integrity, hierarchy/reference resolution, and
        deterministic reading order across format, locale, revision, and
        partial-ingest cases. Evidence: source gate tests.
  - [ ] Y9.2.c Verify canon contradiction, timeline consistency/uncertainty, and
        stale-dependency invalidation against expected finding sets. Evidence:
        canon/timeline tests.
  - [ ] Y9.2.d Verify solver completeness, uniqueness/ambiguity, reachability,
        hint progression, dead ends, and assumption budgets. Evidence: solver
        tests.
  - [ ] Y9.2.e Verify fairness/accessibility and spoiler/reveal boundaries by
        role, stage, locale, export, search, cache, and notification. Evidence:
        boundary tests.
  - [ ] Y9.2.f Map every retained gate to fixtures, measurement assertions,
        verdict assertions, remediation, owner, and current execution result.
        Evidence: gate registry coverage report.
- [ ] Y9.3 Run calibrated safety/AI evaluations with threshold, subgroup, and
      uncertainty reports. Evidence: review approval.
  - [ ] Y9.3.a Freeze representative allowed/refuse/escalate/abstain fixtures,
        subgroup labels, protected-content classes, and expert ground truth.
        Evidence: evaluation manifest.
  - [ ] Y9.3.b Evaluate extraction/generation/solver/agent quality separately
        from safety, grounding, leakage, and authorization behavior. Evidence:
        metric suite.
  - [ ] Y9.3.c Report threshold curves, confidence intervals, calibration,
        abstention, false-positive/negative severity, and subgroup deltas.
        Evidence: evaluation report.
  - [ ] Y9.3.d Test prompt/source injection, spoiler elicitation, sensitive
        inference, unsafe-media requests, tool misuse, and multi-turn attacks.
        Evidence: adversarial suite.
  - [ ] Y9.3.e Reproduce regressions against prior approved baselines and route
        misses to mitigation, model/prompt rollback, or human-only disposition.
        Evidence: regression decisions.
  - [ ] Y9.3.f Obtain named domain/safety/editorial approval for thresholds,
        residual risk, exceptions, and expiry. Evidence: signed review.
- [ ] Y9.4 Run real-database, blob, search, queue, compiler, and read-model
      integration suites. Evidence: durable round-trip.
  - [ ] Y9.4.a Provision supported production-equivalent database/blob/search/
        queue/compiler/read-model versions with isolated tenant fixtures.
        Evidence: environment manifest.
  - [ ] Y9.4.b Exercise transactional writes, outbox/events, blob completion,
        indexing, queued compilation, and consumer projection end to end.
        Evidence: trace-linked tests.
  - [ ] Y9.4.c Verify retries/idempotency/order/deduplication, concurrent edits,
        partial outages, poison messages, dead letters, and replay. Evidence:
        fault tests.
  - [ ] Y9.4.d Verify tenant/role/field/reveal isolation in primary records,
        blobs, indexes, queue payloads, compiler artifacts, and read models.
        Evidence: isolation tests.
  - [ ] Y9.4.e Compare authoritative and projected states after create/update/
        delete/archive/restore/release and repair induced divergence. Evidence:
        reconciliation report.
  - [ ] Y9.4.f Preserve durable IDs/checksums/provenance/audit across backup,
        restart, retry, and cold consumer rebuild. Evidence: round-trip proof.
- [ ] Y9.5 Run web full journey: source to published study material. Evidence:
      live backing and verified consumer read model.
  - [ ] Y9.5.a Create an authorized study project and ingest representative
        files/URLs/media with real blob storage, parsing, provenance, and
        quarantine outcomes. Evidence: journey trace.
  - [ ] Y9.5.b Review source structure/reading order/metadata/rights, correct
        issues, and approve a pinned source revision. Evidence: review audit.
  - [ ] Y9.5.c Author grounded material, ordering,
        questions/hints/accessibility/ locale variants with citations and
        protected answer boundaries. Evidence: authoring trace.
  - [ ] Y9.5.d Run applicable source/canon/fairness/accessibility/spoiler gates,
        remediate a failure, and prove verdict freshness. Evidence: gate trace.
  - [ ] Y9.5.e Preview as eligible learner/reviewer roles and prove protected
        fields do not leak through route, API, search, cache, or export.
        Evidence: role tests.
  - [ ] Y9.5.f Publish through real job/release services, then consume the
        signed immutable read model and reconcile it to the approved revision.
        Evidence: published-consumer proof.
- [ ] Y9.6 Run web full journey: premise to solved, reviewed, localized,
      published case. Evidence: real compiler/verifier.
  - [ ] Y9.6.a Create a case project from premise and build dossier/entities/
        clues/relationships/timeline/scenes with source-backed uncertainty.
        Evidence: authoring trace.
  - [ ] Y9.6.b Define reveal order, red herrings, hints, solution/assumptions,
        alternate hypotheses, and role-specific spoiler boundaries. Evidence:
        case revision.
  - [ ] Y9.6.c Run real graph/timeline/canon/solvability/uniqueness/fairness/
        reachability gates, repair failures, and rerun against pinned inputs.
        Evidence: verifier trace.
  - [ ] Y9.6.d Complete independent editorial/domain/safety review with
        comments, changes, approvals, audit, and stale-approval invalidation.
        Evidence: review trace.
  - [ ] Y9.6.e Localize content, clues, layouts, captions, reading order, and
        solution reveal while preserving IDs, evidence, and solver parity.
        Evidence: locale comparison.
  - [ ] Y9.6.f Compile/publish through real services and solve the immutable
        consumer artifact as intended and plausible alternate paths. Evidence:
        release/consumer proof.
- [ ] Y9.7 Run desktop full journey with local import, offline edit, sync
      conflict, render job, and publish handoff. Evidence: signed build.
  - [ ] Y9.7.a Install/launch the signed supported desktop build in a clean
        standard-user profile and authenticate/open an entitled real project.
        Evidence: build/install trace.
  - [ ] Y9.7.b Import representative local files through approved pickers,
        validate/quarantine/deduplicate/upload them, and preserve local-source
        provenance without exposing paths. Evidence: import trace.
  - [ ] Y9.7.c Disconnect, edit eligible content/assets, close/relaunch, and
        recover encrypted offline drafts with honest availability state.
        Evidence: offline trace.
  - [ ] Y9.7.d Create a concurrent web edit, reconnect, inspect
        mine/theirs/base, resolve the semantic conflict, and verify
        revision/audit continuity. Evidence: conflict trace.
  - [ ] Y9.7.e Submit/monitor/cancel/retry and complete a real render/compile
        job with progress/cost/logs/artifacts and resource limits. Evidence: job
        trace.
  - [ ] Y9.7.f Hand off the approved revision to web publish, consume the
        released artifact, and verify signed-build/deep-link/updater boundaries.
        Evidence: desktop journey report.
- [ ] Y9.8 Run agent fault, budget, cancellation, and human-override journeys.
      Evidence: no unauthorized direct mutation.
  - [ ] Y9.8.a Exercise approved agent plans across representative read,
        recommend, draft, job-submit, and proposed-mutation capabilities.
        Evidence: journey manifest.
  - [ ] Y9.8.b Inject model/tool/provider timeout, malformed output, rate limit,
        stale subject, dependency outage, and partial-result failures. Evidence:
        fault traces.
  - [ ] Y9.8.c Exhaust token/cost/time/tool/step/concurrency budgets and verify
        deterministic stop, retained partial artifacts, and no hidden overrun.
        Evidence: budget tests.
  - [ ] Y9.8.d Cancel before execution, during tool work, during durable job,
        and after commit boundary; verify compensation and truthful terminal
        state. Evidence: cancellation tests.
  - [ ] Y9.8.e Pause/override/edit/reject/escalate from the human control
        surface and verify revalidation, approval, audit, and ownership
        transitions. Evidence: override tests.
  - [ ] Y9.8.f Attempt prompt/tool/direct API mutation outside the plan/role/
        capability/project and prove server rejection with no side effect.
        Evidence: authorization report.
- [ ] Y9.9 Run WCAG 2.2 AA, keyboard, announcement, and semantic-equivalence
      suites on web and desktop renderer. Evidence: matrix report.
  - [ ] Y9.9.a Define route/workspace/dialog/editor/action coverage across
        wide/compact/single-column, web/desktop, themes, zoom, RTL, and locales.
        Evidence: accessibility matrix.
  - [ ] Y9.9.b Run automated WCAG rules, contrast, name/role/value, landmark/
        heading, form/error, target-size, reflow, and motion assertions.
        Evidence: automation report.
  - [ ] Y9.9.c Complete keyboard-only journeys with logical order, visible
        focus, no traps, skip/recovery paths, and non-drag alternatives.
        Evidence: keyboard traces.
  - [ ] Y9.9.d Verify live announcements for save/sync/job/gate/filter/error/
        conflict/review state without duplication, interruption, or silent
        change. Evidence: announcement tests.
  - [ ] Y9.9.e Compare graph/timeline/render/playback visual and semantic paths
        for content, selection, editing, decisions, validation, and permissions.
        Evidence: equivalence tests.
  - [ ] Y9.9.f Run named assistive-technology/manual audits on supported web and
        desktop combinations and close or explicitly block every finding.
        Evidence: signed matrix report.
- [ ] Y9.10 Run large-project performance, memory, startup, search, graph,
      timeline, render-monitor, and sync tests. Evidence: budgets pass.
  - [ ] Y9.10.a Generate versioned production-scale fixtures matching documented
        project/entity/asset/revision/collaboration/job distributions. Evidence:
        fixture report.
  - [ ] Y9.10.b Measure cold/warm web and desktop startup, project open, route/
        workspace transition, idle, and sustained-session memory. Evidence:
        startup/memory profiles.
  - [ ] Y9.10.c Measure indexed search/facets/pagination/update visibility and
        large graph/timeline load/layout/query/edit/validation. Evidence:
        interaction profiles.
  - [ ] Y9.10.d Measure proxy/media load, scrub/playback, render-monitor live
        updates/logs/artifacts, and bounded client/server resource use.
        Evidence: media/job profiles.
  - [ ] Y9.10.e Measure large save/autosave/offline cache/reconnect/rebase/
        conflict/export with concurrency and constrained networks. Evidence:
        sync profiles.
  - [ ] Y9.10.f Compare every metric to platform/device/network budgets, retain
        traces, fail regressions, and assign any exception with expiry.
        Evidence: signed budget report.
- [ ] Y9.11 Run desktop IPC, filesystem, deep-link, updater, signing, sandbox,
      and dependency security tests. Evidence: zero blockers.
  - [ ] Y9.11.a Enumerate every renderer-to-main IPC channel with input/output
        schema, caller capability, authorization, timeout, size, and audit
        requirements. Evidence: IPC security manifest.
  - [ ] Y9.11.b Fuzz unknown/malformed/oversized/replayed/concurrent IPC and
        prove privileged operations cannot be invoked from untrusted content.
        Evidence: IPC adversarial tests.
  - [ ] Y9.11.c Test picker-scoped filesystem access, traversal/symlink/race/
        archive/device-file attacks, permissions, quotas, cleanup, and path
        redaction. Evidence: filesystem report.
  - [ ] Y9.11.d Test deep-link allowlists, canonical parsing, reauthorization,
        expired/replayed links, malicious schemes/arguments, and safe focus.
        Evidence: link security tests.
  - [ ] Y9.11.e Test signed update metadata/download/install/rollback/recovery
        against downgrade, tamper, interruption, channel/version skew, and
        revoked keys. Evidence: updater report.
  - [ ] Y9.11.f Verify code signing/notarization or platform equivalent,
        sandbox/CSP/permission boundaries, SBOM/licenses/vulnerabilities, and
        release artifact provenance with zero blockers. Evidence: security gate.
- [ ] Y9.12 Run source injection, spoiler leak, canon tamper, unsafe media,
      archive, SSRF, auth, and tenant adversarial tests. Evidence: zero
      blockers.
  - [ ] Y9.12.a Inject active instructions/markup/scripts/formulas/links and
        Unicode/bidi/confusable payloads through every supported source format
        and derived field. Evidence: source-injection tests.
  - [ ] Y9.12.b Probe spoiler/answer/solution leakage through API fields,
        errors, search, suggestions, notifications, logs, cache, exports, model
        context, and timing. Evidence: leak report.
  - [ ] Y9.12.c Tamper with
        canon/evidence/revision/gate/result/read-model/release IDs, hashes,
        ordering, signatures, and stale approvals. Evidence: integrity tests.
  - [ ] Y9.12.d Upload polyglot/malformed/oversized/decompression-bomb/active/
        prohibited media and verify quarantine, transform isolation, and safe
        preview. Evidence: media tests.
  - [ ] Y9.12.e Exercise archive traversal/symlink/nesting/ratio and URL import
        SSRF/DNS rebinding/redirect/private-network/credential attacks.
        Evidence: import security tests.
  - [ ] Y9.12.f Test cross-role/project/tenant object, field, function, blob,
        search, queue, export, deep-link, and cache access with zero side
        effects. Evidence: authorization gate.
- [ ] Y9.13 Run backup/restore and web/desktop version-skew drills. Evidence:
      RPO/RTO and compatibility results.
  - [ ] Y9.13.a Define protected data/services, backup cadence/retention/
        encryption/immutability/region, recovery order, RPO/RTO, and owners.
        Evidence: recovery contract.
  - [ ] Y9.13.b Restore database/blob/search/queue/config/secrets references and
        release artifacts into an isolated environment from selected full/
        incremental/point-in-time backups. Evidence: restore trace.
  - [ ] Y9.13.c Reconcile records, blobs, indexes, projections, jobs, audits,
        signatures, tenant boundaries, and unresolved edits after restore.
        Evidence: integrity report.
  - [ ] Y9.13.d Exercise supported older/newer web API, desktop client, schema,
        job worker, compiler, and read-model combinations. Evidence:
        version-skew matrix.
  - [ ] Y9.13.e Verify incompatible combinations block safely with upgrade/
        retry/export/recovery guidance and no corrupt or silently dropped edits.
        Evidence: boundary tests.
  - [ ] Y9.13.f Measure achieved data loss and recovery duration, record gaps/
        exceptions, and obtain operations/security approval. Evidence: signed
        drill report.
- [ ] Y9.14 Publish author, canon, solver, localization, producer, desktop,
      incident, and recovery runbooks. Evidence: game-day.
  - [ ] Y9.14.a Publish author/source-ingest and canon/timeline remediation
        runbooks with prerequisites, diagnostics, safe edits, revalidation, and
        escalation. Evidence: runbook review.
  - [ ] Y9.14.b Publish solver/uniqueness/fairness/spoiler investigation
        runbooks with known-value comparisons and human disposition. Evidence:
        runbook review.
  - [ ] Y9.14.c Publish localization/release and producer schedule/budget/
        pipeline/job/engine handoff runbooks with rollback boundaries. Evidence:
        runbook review.
  - [ ] Y9.14.d Publish desktop import/offline/conflict/filesystem/deep-link/
        updater/signing support runbooks without requesting secrets or unsafe
        filesystem access. Evidence: support review.
  - [ ] Y9.14.e Publish security/safety/privacy/availability incident triage,
        containment, evidence, communication, recovery, and postmortem runbooks.
        Evidence: incident review.
  - [ ] Y9.14.f Run timed cross-role game-days from detection through recovery,
        capture unclear/missing/unsafe steps, revise, and reapprove. Evidence:
        game-day report.
- [ ] Y9.15 Produce engineering-readiness evidence per track and host. Evidence:
      all required reports current.
  - [ ] Y9.15.a Define required architecture/contracts/migrations/security/
        privacy/safety/accessibility/quality/operations evidence by study, case,
        production, marketplace, organization, and developer track. Evidence:
        readiness matrix.
  - [ ] Y9.15.b Define additional web, desktop, mobile/tablet review,
        engine/DCC, virtual-production/XR, and external-host evidence with
        owners/freshness. Evidence: host matrix.
  - [ ] Y9.15.c Link each retained capability and release cell to current test/
        evaluation/report/runbook/approval artifacts and immutable versions.
        Evidence: evidence index.
  - [ ] Y9.15.d Verify evidence was produced against the candidate commit,
        configuration, models/providers, dependencies, schemas, and build.
        Evidence: provenance check.
  - [ ] Y9.15.e Classify missing/stale/failed/waived evidence, block required
        cells, and require named owner/reason/scope/expiry for exceptions.
        Evidence: readiness gate.
  - [ ] Y9.15.f Publish a per-track/host go/no-go summary with residual risks
        and explicit unsupported combinations. Evidence: readiness packet.
- [ ] Y9.16 Obtain named editorial, domain, safety, accessibility,
      privacy/security, engineering, and product approvals. Evidence: human
      signoff.
  - [ ] Y9.16.a Define each approver’s scope, required evidence, decision
        criteria, independence/conflict rules, delegates, and expiry. Evidence:
        approval policy.
  - [ ] Y9.16.b Route immutable candidate/evidence packets to named editorial
        and relevant study/case/production/domain reviewers. Evidence: review
        records.
  - [ ] Y9.16.c Route calibrated safety, accessibility, privacy, and security
        reports with open findings/exceptions to accountable reviewers.
        Evidence: specialist decisions.
  - [ ] Y9.16.d Route engineering/operations and product readiness, support,
        compatibility, rollout, and residual-risk evidence. Evidence: launch
        decisions.
  - [ ] Y9.16.e Record approve/reject/request-changes/conditional decisions with
        identity, role, scope, candidate, reason, conditions, and timestamp.
        Evidence: signed audit.
  - [ ] Y9.16.f Invalidate affected approvals after candidate/evidence/config/
        model/dependency/schema changes and block launch until renewed.
        Evidence: stale-approval tests.
- [ ] Y9.17 Run full-journey coverage for project/asset lifecycle,
      script→storyboard→animatic, character/world→scene, schedule/budget/call
      sheet, animation/motion, dailies/editorial, collaboration, autonomous
      film/game pipeline, engine round-trip, gameplay/NPC, virtual
      production/XR, marketplace, organization administration, community
      disposition, and developer integration. Evidence: one generic project test
      cannot stand in for these distinct workflows.
  - [ ] Y9.17.a Run project/asset create-import-version-relate-search-review/
        archive/restore/release across authorized roles and web/desktop.
        Evidence: lifecycle journey.
  - [ ] Y9.17.b Run script→storyboard→animatic and character/world→scene
        continuity, provenance, review, change-impact, and handoff journeys.
        Evidence: creative journeys.
  - [ ] Y9.17.c Run schedule/budget/call-sheet and animation/motion with
        baseline/ variance/conflict/approval and Aja/host round-trip. Evidence:
        production journeys.
  - [ ] Y9.17.d Run dailies/editorial and multi-user collaboration through
        ingest/proxy/sync/notes/decisions/CRDT conflict/offline recovery.
        Evidence: review/collaboration journeys.
  - [ ] Y9.17.e Run autonomous film and game pipelines through approved plan,
        budget, jobs, human override, artifacts, gates, and publish handoff.
        Evidence: agent pipeline journeys.
  - [ ] Y9.17.f Run engine round-trip, gameplay/NPC behavior, and virtual
        production/XR capture/calibration/session/incident journeys. Evidence:
        runtime/VP journeys.
  - [ ] Y9.17.g Run marketplace submit-scan-review-install-license-update/
        report/takedown and organization membership/policy/audit/retention/
        billing administration. Evidence: marketplace/admin journeys.
  - [ ] Y9.17.h Run community submission/moderation/appeal/disposition and
        developer API/webhook/service-account/key rotation/rate-limit/version
        journeys. Evidence: community/developer journeys.
  - [ ] Y9.17.i Prove each journey uses real owned services, isolates tenants/
        roles, emits trace/audit evidence, and cleans or retains fixtures by
        policy. Evidence: journey coverage ledger.
- [ ] Y9.18 Run pairwise compatibility and loss-profile gates for every
      supported project type, asset kind, web/desktop version, engine/DCC host,
      external integration, marketplace package, and release target. Evidence:
      unsupported combinations are explicit and supported cells have current
      proof.
  - [ ] Y9.18.a Build canonical dimensions/values/constraints from supported
        project profiles, assets/formats, clients, hosts/versions, integrations,
        packages, locales, and release targets. Evidence: compatibility model.
  - [ ] Y9.18.b Generate deterministic pairwise cells plus required high-risk
        higher-order combinations and label invalid/unsupported cells with
        rationale. Evidence: coverage set.
  - [ ] Y9.18.c Define loss profile per transform/round-trip for identity,
        hierarchy, metadata, timing, geometry/materials/animation/audio,
        interactivity, accessibility, provenance, and rights. Evidence: loss
        contracts.
  - [ ] Y9.18.d Execute import/export/round-trip/install/build/release cells on
        exact versioned hosts and compare semantic measurements to tolerances.
        Evidence: compatibility results.
  - [ ] Y9.18.e Exercise version skew, optional capability absence, degradation,
        warnings, refusal, repair, and rollback without silent loss. Evidence:
        boundary tests.
  - [ ] Y9.18.f Publish current supported/conditional/unsupported matrix with
        evidence age, known loss, prerequisites, owner, and expiry; fail stale
        or unproven supported cells. Evidence: release gate.
- [ ] Y9.19 EVE-SEAM-EXIT: require "required Eve reads and writes admitted with
      source and behavior evidence, or an exact task-0.8 explicit non-goal
      proves zero operations; a named defer preserves ownership but never passes
      completion" before this phase exits.
      Evidence: source-derived totality record and passing verifier.
  - [ ] Y9.19.a Re-run the route, bridge, state-signal, and complete domain-code
        envelope census; review every changed candidate instead of carrying a
        stale phase disposition forward. Evidence: current ratchets.
  - [ ] Y9.19.b Bind the exact ratified task-0.8 workflow and requirement set;
        classify it as a required seam or an exact source-backed non-goal.
        Evidence: charter-bound disposition set.
  - [ ] Y9.19.c For a required seam, admit both read and write operations with
        byte-pinned source and behavior evidence. A named defer retains its owner
        and unblock condition but fails completion. Evidence: operation proofs.
  - [ ] Y9.19.d Permit a zero-operation boundary only for an exact ratified
        task-0.8 explicit non-goal; absence or unavailability is not a non-goal.
        Evidence: source-bound non-goal proof.
  - [ ] Y9.19.e Run the totality verifier, every retained gate-case CLI, and the
        injected durable-candidate control; retain red and green receipts.
        Evidence: release gate logs.

---

## Phase V — Veritas newsroom workbench

Veritas is the V1 grounded-story domain. The workbench must preserve source
protection, make claims and counterclaims inspectable, enforce the implemented
editorial and attestation policies, publish through the real CMS, and carry
corrections or retractions to every dependent surface.


<!-- SPEC 2026-09-11: PHASE V ESTATE FACTS (measured 2026-09-11 at 48f4bc8ddb;
§2.5 binds). (1) NO NEWSROOM AUTHORING UI EXISTS.
`apps/oshun/web/src/app/veritas/*` (story, claim, source, evidence,
counterclaims, retraction, topic, provenance, mobile) is the Lilith CONSUMER
room (`VeritasRoom`, reader-facing); `/domains/veritas/*` (articles, research,
headlines, knowledge-graph) are Lilith consumer tools; both are V1.2-scoped by
`libs/oshun/navigation/src/release-scope.ts` and the BFF
`middleware/release-scope.ts` refuses Veritas scope in V1.0 (the existing
`veritas-full-journey.spec.ts` asserts that refusal). `apps/veritas/web` is a
three-page public reader (`[locale]`, `reading-list`, `offline`, reads
`NEXT_PUBLIC_API_URL`, no e2e). The committed route inventory labels the
consumer room "newsroom authoring" — that label is wrong and V0.1.a/V0.1.c are
re-opened below. (2) THREE DISJOINT PERSISTENCE STORIES, NONE CANONICAL:
`libs/veritas/database` (Prisma, 67 models, 2 migrations, `VERITAS_DATABASE_URL`
— not in env-master, no compose service, so a database must be created on the
shared Postgres); `apps/veritas/api` (Hono :3002, raw SQL through a pg pool +
Redis + Elasticsearch, HMAC JWT carrying userId/email and NO role, 19 tests that
mock the pool; its SQL reads `user_subscriptions`, a table the Prisma migration
does not create); `apps/veritas/cms` (Fastify :3004, 28 routes,
`src/storage/memory-storage.ts` is its ONLY storage, no auth on routes, 2 tests,
nothing consumes it). "Publish through the real CMS" in this phase's header
therefore has no durable CMS behind it today; V6.1 decides, V6.9 builds on the
decision, V9.8 proves it. (3) V1-SIDE CODE THAT IS REAL:
`libs/oshun/domain-veritas` (36 specs, built on `@oshun/contracts/veritas`:
SourceKind 15-vocabulary, bands, ClaimClass, RetractionState, EvidenceStance,
attestor), `libs/oshun/veritas-cascade-worker` (live-infra integration tests)
wired by `apps/oshun/bff/src/veritas/retraction-cascade-runtime.ts`, and
`apps/oshun/bff/src/routes/veritas-briefing.ts`. Legacy Zod models live in
`libs/veritas/models/src/schemas` (nested) against Prisma (flattened) — V0.3.a
already measured the mismatch. (4) STANDARDS: NewsML-G2 and IPTC Media Topics
have ZERO code in the estate; C2PA is `libs/veritas/content-auth` with an
optional `c2paValidator` seam (1 test) and `c2pa-node@0.5.26` installed under
pnpm; Ghana NMC and election rules exist only in
`apps/veritas/api/src/domain/compliance/{nmc,election}.ts`; USSD is
`libs/veritas/ussd`. (5) MODELS: `libs/veritas/llm` defaults to a Claude model
table over `OPENROUTER_API_KEY`;
`libs/veritas/{claims,fact-checking,bias-detection}` import it. Bind
`VERITAS_LLM_PRIMARY_MODEL` and `VERITAS_LLM_FALLBACK_MODEL` to
`deepseek/deepseek-v4-flash-0731` with `OPENROUTER_PROVIDER_SORT=price` for
every test (§2.5.4); `bias-detection` has no tests at all. (6) 13 of 64
`libs/veritas/*` packages have zero tests: agents-product, archive, b2b-sdk,
b2b-sdk-python, bias-detection, billing, content-classification, core,
expansion, ingestion-core, knowledge-graph, models, signup. (7) Playwright:
`apps/oshun/web/e2e/*veritas*` are consumer-room specs (only
`home-veritas-briefing` intercepts routes); none exercises authoring. Every
V-phase journey cell starts from nothing and must obey §2.5.2. -->
### V0 — Newsroom scope, route, roles, and ethics

- [ ] V0.1 Reconcile Oshun Studio, `apps/veritas/web`, `apps/veritas/cms`, and
      existing BFF routes. Evidence: canonical authoring/reader/operator route
      map. <!-- partial: route inventory (a) done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/veritas-route-inventory.json. b-e (journey diagrams,
                                                                                                                                                                                                                                                                      collision ledger, canonical owner map, boundary tests) are runtime/design/human
                                                                                                                                                                                                                                                                      and stay unchecked. -->
      <!-- SPEC 2026-09-11: the canonical host does not exist (Phase V header
      note). .d is a product decision with three real options — a new newsroom
      under Oshun Studio (the Section 1 table's stated host) backed by the
      Prisma store, a newsroom UI inside `apps/veritas/*`, or promoting the
      headless CMS — and an agent drafts
      `docs/adr/ADR-V0.1-veritas-newsroom-host.md` (Proposed) rather than
      choosing. Until a person accepts it, V2–V8 UI cells may build components
      and contracts but no route tree; V8.2 registers routes only after .d. -->
  - [ ] V0.1.a Inventory Studio, Veritas web/CMS, BFF, public reader, deep-link,
        redirect, navigation, and command registrations with owners/audiences.
        Evidence: route inventory.
        <!-- evidence: .pageRoutes + .cmsServiceRoutes + .bffRoutes — all 4 named
                                                                                                                                                                                                                                                                        surfaces: 11 Oshun-Studio /veritas/* authoring pages, 3 veritas-web public
                                                                                                                                                                                                                                                                        reader pages ([locale]-prefixed), 28 veritas-cms Fastify service routes
                                                                                                                                                                                                                                                                        (headless service, no pages), 2 BFF modules (16 registrations). owners/
                                                                                                                                                                                                                                                                        audiences classification is V0.1.b/d (semantic). generate-veritas-route-
                                                                                                                                                                                                                                                                        inventory.mjs + 7 tests, wired into inventory/verify. First Phase V task. -->
        <!-- SPEC 2026-09-11: RE-OPENED. The inventory's host label "oshun-web
        (/veritas/* newsroom authoring)" is wrong — those pages render
        `VeritasRoom` for readers — and it omits
        `/domains/veritas/{articles,research,headlines,knowledge-graph}` and
        `apps/oshun/web/src/app/api/veritas/*`. The cell's own clause asks for
        owners/audiences, which the comment defers. Regenerate with audience =
        consumer/reader/service/bff per row and the missing rows added, then
        flip. -->
  - [ ] V0.1.b Trace each newsroom authoring/review/publish/operations journey
        through current UI, BFF, service, CMS, and public read-back. Evidence:
        current-state diagrams.
  - [ ] V0.1.c Identify duplicate shells/routes/mutations, split ownership,
        legacy aliases, dead paths, and protected/public boundary violations.
        Evidence: collision ledger.
        <!-- evidence: veritas-collision-ledger.json — cross-surface scan of the
                                                                                                                                                                                                                                                                        V0.1.a inventory: 0 duplicate page routes (studio /veritas/* authoring vs
                                                                                                                                                                                                                                                                        veritas-web [locale] reader don't overlap), 0 protected/public boundary
                                                                                                                                                                                                                                                                        violations (reader is locale-scoped, never reaches a protected /veritas/*
                                                                                                                                                                                                                                                                        path — boundary INTACT), 0 CMS↔BFF operation-path overlaps, 0 dead paths.
                                                                                                                                                                                                                                                                        split-ownership + legacy-alias disposition is V0.1.d (semantic).
                                                                                                                                                                                                                                                                        generate-veritas-collision-ledger.mjs + 5 tests. -->
        <!-- SPEC 2026-09-11: RE-OPENED. The "0 boundary violations" conclusion
        was computed over the mislabeled V0.1.a inventory (consumer pages
        counted as protected authoring). Re-run after V0.1.a is corrected; the
        CMS routes with no auth (`apps/veritas/cms/src/server.ts`) are a
        boundary finding this ledger must list, not a zero. -->
  - [ ] V0.1.d Assign one canonical host/route/API owner and migration/redirect/
        removal disposition to every retained capability. Evidence: route map.
  - [ ] V0.1.e Verify authorized entry/back/deep-link behavior and prevent
        reader/operator navigation from exposing protected newsroom surfaces.
        Evidence: route tests.
- [ ] V0.2 Map all `libs/veritas/*` packages to newsroom capabilities or
      explicit out-of-scope products. Evidence: complete package ledger.
      <!-- SPEC 2026-09-11: .c starts from the Phase V header note's list of 13
      zero-test packages and the unconsumed CMS; a package with no tests and no
      consumer needs an explicit keep/remove disposition, not silence.
      `libs/oshun/domain-veritas` and `libs/oshun/veritas-cascade-worker` are
      V1-side consumers to record as retained. -->
  - [x] V0.2.a Enumerate every package, public export, consumer, runtime target,
        persistence/integration dependency, test, and documented purpose.
        Evidence: package inventory.
        <!-- evidence: .domains.veritas.packages — 64 libs/veritas/* packages with exports/consumer/dependency/test/disposition. owning host/service + accountable team are V0.2.b (semantic). evidence/v1-workbenches/domain-package-inventories.json (254 pkgs; per-package exports/consumers/deps/disposition/tests + capability catalog with exact source locations). -->
  - [ ] V0.2.b Map retained exports to atomic newsroom capabilities, canonical
        contracts, owning host/service, and accountable team. Evidence:
        capability ledger.
  - [ ] V0.2.c Classify duplicate/legacy/dead/demo/reader/commercial/platform
        code with keep/merge/migrate/deprecate/remove/out-of-scope disposition.
        Evidence: disposition ledger.
  - [ ] V0.2.d Identify protected-source, authorization, standards, provenance,
        and editorial-policy bypasses at package boundaries. Evidence: boundary
        review.
  - [ ] V0.2.e Verify every package and export has one reviewed disposition and
        no retained consumer depends on an orphan or forbidden path. Evidence:
        completeness gate.
- [ ] V0.3 Reconcile canonical V1 contracts with legacy Veritas API/client
      models. Evidence: translation boundaries and no new third vocabulary.
      <!-- SPEC 2026-09-11: "canonical" here is `libs/contracts/src/veritas`
      (the V1 contracts `libs/oshun/domain-veritas` already consumes); "legacy"
      is both `libs/veritas/models/src/schemas` (Zod, nested) and the Prisma
      schema (flattened) plus the raw SQL shapes in `apps/veritas/api`. .b
      decides per mismatch; .d must not add a fourth vocabulary in the CMS. -->
  - [x] V0.3.a Inventory canonical and legacy
        story/source/claim/evidence/review/ release field names, types, enums,
        defaults, nullability, and versions. Evidence: contract diff.
        <!-- veritas-contract-diff.json: Veritas carries TWO typed representations
                                                                                                                                                                                                                                                                        per entity — the generated Prisma DB schema (libs/veritas/database) and the
                                                                                                                                                                                                                                                                        Zod domain schema (libs/veritas/models/src/schemas). This diffs their
                                                                                                                                                                                                                                                                        field sets for story(Article)/source(Source)/claim(Claim)/evidence
                                                                                                                                                                                                                                                                        (ClaimEvidence)/review(ClaimVerification): 60 shared fields, 16 Prisma-only
                                                                                                                                                                                                                                                                        (DB-internal ids/timestamps/soft-delete/counters), 34 Zod-only. Surfaces a
                                                                                                                                                                                                                                                                        REAL structural mismatch — Zod NESTS (ownership, bias) while Prisma FLATTENS
                                                                                                                                                                                                                                                                        (ownershipName/ownershipType, biasConfidence/biasMethodology/…) — which is
                                                                                                                                                                                                                                                                        exactly the semantic mismatch V0.3.b must reconcile. Nullability captured
                                                                                                                                                                                                                                                                        per Zod field (.nullable()/.optional()). The Zod parser walks brace depth to
                                                                                                                                                                                                                                                                        take only top-level keys (nested object keys excluded). `release` has no
                                                                                                                                                                                                                                                                        dedicated schema pair (publication is derived, not a first-class entity —
                                                                                                                                                                                                                                                                        recorded honestly). generate-veritas-contract-diff.mjs + 6 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. V0.3.b (DECIDE canonical source per mismatch),
                                                                                                                                                                                                                                                                        V0.3.c (transform contracts), V0.3.d+ (migration) are design and stay open. -->
  - [ ] V0.3.b Decide canonical source of truth and explicit adapter/migration
        boundary for each semantic mismatch. Evidence: mapping table.
  - [ ] V0.3.c Define lossless conversions or documented refusal/known-loss
        behavior for legacy-only and canonical-only values. Evidence: transform
        contracts.
  - [ ] V0.3.d Update serializers/clients/routes/storage mappings without
        publishing a hybrid third vocabulary. Evidence: schema/API tests.
  - [ ] V0.3.e Round-trip historical/current fixtures across server, clients,
        jobs, CMS, and public projections. Evidence: compatibility goldens.
- [ ] V0.4 Reconcile story record states, canonical editorial workflow states,
      and legacy snake-case states. Evidence: lossless mapping or approved
      migration.
      <!-- SPEC 2026-09-11: the nine vocabularies V0.4.a found include the CMS
      `ARTICLE_STATUSES` in `apps/veritas/cms/src/config.ts` and the contracts'
      `RetractionState`; .b's canonical machine lives in
      `libs/contracts/src/veritas`, and .d's migration rehearsal runs against
      the Prisma database created per the Phase V header note. -->
  - [x] V0.4.a Inventory every persisted/API/client/job/CMS/public state value,
        transition, terminal condition, and side effect. Evidence: state
        inventory.
        <!-- evidence: veritas-state-inventory.json — 9 editorial/workflow state
                                                                                                                                                                                                                                                                        Zod enums across 9 DISTINCT value vocabularies (kebab-case + lowercase),
                                                                                                                                                                                                                                                                        each with binding name/values/source/case-convention. sharedValues surfaces
                                                                                                                                                                                                                                                                        the reconciliation collisions (draft/pending/completed each recur in 4 enums;
                                                                                                                                                                                                                                                                        published in 3). The state VALUES are fully inventoried; the transition graph +
                                                                                                                                                                                                                                                                        terminal/side-effect mapping is V0.4.b (scattered state-machine logic, not one
                                                                                                                                                                                                                                                                        registry — noted honestly). generate-veritas-state-inventory.mjs + 5 tests. -->
  - [ ] V0.4.b Define canonical state machine, orthogonal
        embargo/review/release/ correction dimensions, actor/guard rules, and
        invalid transitions. Evidence: transition specification.
  - [ ] V0.4.c Map legacy snake-case and story-record states with explicit
        ambiguity, split/merge, terminal, and unknown handling. Evidence:
        mapping matrix.
  - [ ] V0.4.d Migrate durable records/events/jobs/read models idempotently and
        preserve history, audit, schedules, and in-flight work. Evidence:
        migration rehearsal.
  - [ ] V0.4.e Run mixed-version and transition/property tests; reject unmapped
        states rather than defaulting to a publishable state. Evidence: state
        compatibility suite.
- [ ] V0.5 Define reporter, researcher, producer, assignment editor, copy
      editor, fact-checker, standards editor, legal reviewer, source-protection
      officer, publisher, corrections editor, and administrator roles. Evidence:
      permission matrix.
      <!-- SPEC 2026-09-11: no Veritas runtime carries a role today — the API
      JWT has userId/email only, the CMS has no auth. .a–.d are the matrix
      (Proposed); .e is buildable only after V6.4/V6.5 give the roles a server
      to enforce them, so it stays open until then and says so. -->
  - [ ] V0.5.a Define duties and least-privilege object/field/function rights
        for each named role across pitch through archive/correction. Evidence:
        role definitions.
  - [ ] V0.5.b Define desk/project/story/source-compartment/locale/territory
        scope, temporary delegation, break-glass, and separation-of-duties.
        Evidence: scope policy.
  - [ ] V0.5.c Define create/read/edit/review/attest/waive/publish/correct/
        retract/export/admin rights per state and data class. Evidence:
        permission matrix.
  - [ ] V0.5.d Define incompatible role combinations, recusal/conflict effects,
        quorum, and independent specialist approvals. Evidence: SoD tests.
  - [ ] V0.5.e Verify UI projection and server enforcement for positive,
        negative, stale-session, revoked, cross-desk, and cross-compartment
        cases. Evidence: authorization suite.
- [ ] V0.6 Define protected-source compartment, access-grant, emergency-access,
      disclosure, retention, and audit policy. Evidence: security/legal review.
  - [ ] V0.6.a Define compartment identity/key boundary and which source
        identifiers, contacts, communications, documents, locations, and linkage
        metadata are protected. Evidence: data-boundary model.
  - [ ] V0.6.b Define nominative/time-bound/purpose-bound access requests,
        approvals, step-up, session restrictions, revocation, and periodic
        review. Evidence: grant policy.
  - [ ] V0.6.c Define emergency-access eligibility, dual control where required,
        minimal disclosure, alerts, expiry, evidence preservation, and mandatory
        post-event review. Evidence: break-glass policy.
  - [ ] V0.6.d Define redacted projections for ordinary stories/reviews/search/
        notifications/logs/analytics/exports/support and safe reidentification
        boundary. Evidence: disclosure rules.
  - [ ] V0.6.e Define retention/deletion/legal hold/key destruction/backup/
        recovery and subject-safety exception behavior. Evidence: lifecycle
        policy.
  - [ ] V0.6.f Define tamper-evident access/attempt/export/disclosure audit with
        protected details visible only to authorized reviewers. Evidence:
        security/legal approval.
- [ ] V0.7 Define editorial AI disclosure and acceptable-use policy. Evidence:
      authoring, publication, correction, and reader-visible requirements.
  - [ ] V0.7.a Classify allowed, approval-required, prohibited, and human-only
        AI uses across research, sourcing, drafting, verification, media,
        localization, distribution, correction, and moderation. Evidence: use
        matrix.
  - [ ] V0.7.b Define input restrictions for protected sources, personal/
        licensed/embargoed/legal data, external providers, retention, and model
        training. Evidence: data-use policy.
  - [ ] V0.7.c Define per-change provenance, model/provider/version,
        prompt/tool/ source use, human acceptance, and audit requirements.
        Evidence: provenance policy.
  - [ ] V0.7.d Define internal reviewer and reader-visible disclosure thresholds
        by materiality, content type, target, locale, and correction status.
        Evidence: disclosure rules.
  - [ ] V0.7.e Define prohibited fabricated
        sources/quotes/citations/attribution, automated
        attestation/approval/publication/correction, and enforcement. Evidence:
        acceptable-use controls.
  - [ ] V0.7.f Define incident, correction, notification, model rollback, and
        retrospective duties for AI-caused errors. Evidence: governance review.
- [ ] V0.8 Define conflicts, recusal, attestation independence, false-balance,
      corrections, takedown, and retraction policies. Evidence: ethics decision
      records.
  - [ ] V0.8.a Define personal/financial/political/source/advertiser/partner/
        prior-work conflicts, declarations, scope, updates, and visibility.
        Evidence: conflict policy.
  - [ ] V0.8.b Define recusal effects on assignment, editing, review,
        attestation, publishing, correction, reassignment, and audit. Evidence:
        recusal policy.
  - [ ] V0.8.c Define attestor credential/domain/independence/conflict/
        disagreement/expiry floors and prohibited manufactured independence.
        Evidence: attestation policy.
  - [ ] V0.8.d Define evidence-based counterclaim surfacing and false-balance
        suppression with uncertainty and human accountability. Evidence: balance
        decision record.
  - [ ] V0.8.e Distinguish clarification/update/correction/takedown/retraction,
        thresholds, authority, reader notice, distribution, appeal, and
        restoration. Evidence: remedy policy.
  - [ ] V0.8.f Define emergency/legal requests without silent deletion or
        retraction suppression and require immutable decisions. Evidence: ethics
        approvals.
- [ ] V0.9 Threat-model source coercion, identity leakage, poisoned evidence,
      prompt injection, fabricated citations, defamation, legal exposure,
      embargo leak, account compromise, mass publishing, and retraction
      suppression. Evidence: mitigation/test map.
  - [ ] V0.9.a Map actors, assets, trust boundaries, data flows, privileged
        actions, public/distribution surfaces, and protected compartments.
        Evidence: threat-model diagrams.
  - [ ] V0.9.b Model coercion, impersonation, correlation, metadata/log/search/
        notification leakage, compromised reviewer, and emergency-access abuse.
        Evidence: source-protection threats.
  - [ ] V0.9.c Model poisoned/malicious evidence, prompt/tool injection,
        fabricated/washed citations, provenance tamper, and correlated agent
        failure. Evidence: integrity threats.
  - [ ] V0.9.d Model defamation/privacy/contempt/rights exposure, unsafe media,
        false balance, election/emergency misuse, and jurisdictional conflict.
        Evidence: harm/legal threats.
  - [ ] V0.9.e Model embargo/schedule leak, credential/session compromise, mass
        publish/notification, correction/retraction suppression, and audit
        tamper. Evidence: operations threats.
  - [ ] V0.9.f Assign preventive/detective/recovery controls, owners, residual
        risk, abuse tests, monitoring, and review cadence to every scenario.
        Evidence: mitigation/test map.
- [ ] V0.10 Define data classification for pitches, drafts, embargoes, source
      identities, communications, evidence, legal notes, unpublished media,
      analytics, and public content. Evidence: retention and residency rules.
  - [x] V0.10.a Inventory each named data class plus derived/indexed/cached/log/
        backup/export/model-context forms and accountable owner. Evidence: data
        catalog.
        <!-- veritas-data-catalog.json: derived from the generated Prisma client
                                                                                                                                                                                                                                                                        (libs/veritas/database/src/generated/client), the authoritative data model.
                                                                                                                                                                                                                                                                        67 named data classes: 57 primary entities (Article, Source, Claim,
                                                                                                                                                                                                                                                                        Election, DpaConsent, ...) + 10 satellite forms tagged from model naming
                                                                                                                                                                                                                                                                        (model-context/embedding 1 = ArticleEmbedding; derived render 2 / variant 2
                                                                                                                                                                                                                                                                        / revision 1 / translation 1 / normalized 1; log outbox 1 / delivery 1),
                                                                                                                                                                                                                                                                        classified against a 45-enum canonical vocabulary (SourceKind, ContentPriority,
                                                                                                                                                                                                                                                                        DpaLawfulBasis, ElectionType, ...). Of V0.10.a's 7 named form types,
                                                                                                                                                                                                                                                                        derived/model-context/log are entity-modeled; indexed/cached/backup/export
                                                                                                                                                                                                                                                                        are recorded HONESTLY as infrastructure forms (DB indexes, cache layer,
                                                                                                                                                                                                                                                                        backup/DR, export jobs — NOT Prisma entities), not fabricated. Owner = the
                                                                                                                                                                                                                                                                        veritas domain package (named human owners are V0.10.b). The sensitivity/
                                                                                                                                                                                                                                                                        compartment/lawful-basis CLASSIFICATION (V0.10.b) and encryption/residency/
                                                                                                                                                                                                                                                                        retention DEFINITIONS (V0.10.c+) are design/judgment and stay unchecked.
                                                                                                                                                                                                                                                                        generate-veritas-data-catalog.mjs + 7 tests, wired into inventory/verify + CI. -->
  - [ ] V0.10.b Assign sensitivity, compartment, tenant/desk/story scope, lawful
        basis/purpose, and permitted actors/actions. Evidence: classification
        matrix.
  - [ ] V0.10.c Define encryption/key, region/residency/transfer, provider,
        masking/minimization, and device/offline restrictions. Evidence:
        handling rules.
  - [ ] V0.10.d Define active/archive/legal-hold/backup retention, deletion/
        anonymization, correction preservation, and conflict resolution.
        Evidence: lifecycle schedule.
  - [ ] V0.10.e Define safe public/redacted/aggregate projections and prohibit
        protected data in analytics/search/logs/notifications/support/model
        training. Evidence: projection policy.
  - [ ] V0.10.f Verify rules against representative end-to-end flows and obtain
        privacy/security/legal/source-protection approval. Evidence: signed
        review.
- [ ] V0.11 Define newsroom SLOs and success metrics without optimizing for
      volume at the expense of accuracy. Evidence: quality guardrails.
  - [ ] V0.11.a Define availability/latency/durability/freshness/recovery SLOs
        separately for protected authoring, publish, correction/retraction, and
        public read-back. Evidence: service objectives.
  - [ ] V0.11.b Define accuracy, citation/support, gate escape, correction
        propagation, source freshness, accessibility, and safety quality
        measures. Evidence: quality metric catalog.
  - [ ] V0.11.c Define measurement windows, denominators, exclusions, data
        lineage, privacy floors, uncertainty, and alert thresholds. Evidence:
        metric contracts.
  - [ ] V0.11.d Define balanced scorecard rules so speed/output/engagement/
        revenue never override accuracy, protected-source, ethics, or correction
        obligations. Evidence: guardrail policy.
  - [ ] V0.11.e Define error budgets, stop-the-line conditions, escalation,
        remediation, and release consequences. Evidence: SLO policy.
  - [ ] V0.11.f Validate dashboards with known-value fixtures and prevent
        individual/source identification or automatic editorial scoring.
        Evidence: observability review.
- [ ] V0.12 Ratify the NewsML-G2, IPTC Media Topics, C2PA, and CMS publication
      profiles. Evidence: standards decision record.
      <!-- SPEC 2026-09-11: nothing to ratify exists for NewsML-G2 or IPTC (zero
      code). .a pins the standard versions and the validators (IPTC NewsML-G2
      2.x XSD bundle + the IPTC Media Topics vocabulary release, both public
      downloads to be vendored with sha256; C2PA via `c2pa-node@0.5.26` already
      installed); .f's approval is human (§2.5.9). -->
  - [ ] V0.12.a Pin standard/profile/schema/validator versions, namespaces,
        required/optional extensions, compatibility window, and update owner.
        Evidence: standards manifest.
  - [ ] V0.12.b Map internal stories/packages/planning/concepts/topics/rights/
        embargo/update/correction fields to NewsML-G2/IPTC semantics. Evidence:
        mapping matrix.
  - [ ] V0.12.c Define governed IPTC topic selection, aliases, localization,
        deprecation, and internal taxonomy relationships. Evidence: vocabulary
        profile.
  - [ ] V0.12.d Define C2PA claims/ingredients/actions/signers/trust validation
        for media and article packages, including stripped/unknown provenance.
        Evidence: provenance profile.
  - [ ] V0.12.e Define CMS plan/publish/read-back/update/correct/retract
        behavior, target extensions, idempotency, and partial-failure
        compensation. Evidence: CMS profile.
  - [ ] V0.12.f Validate representative and edge-case fixtures, record
        deviations/interoperability findings, and obtain editorial/engineering
        approval. Evidence: decision record.
- [ ] V0.13 Author and ratify
      `V1/VERITAS_NEWSROOM_WORKBENCH_DESIGN_2026-07-23.md` with current-state,
      host, roles, protected-source boundary, data, APIs, editorial policy, UI,
      standards, migration, rollout, and decision records. Evidence: approved
      doc and resolved review comments.
  - [ ] V0.13.a Document current applications/packages/routes/services/stores/
        integrations, duplicate ownership, constraints, and known gaps.
        Evidence: current-state chapter.
  - [ ] V0.13.b Document target hosts/ownership/roles/protected-source and
        public boundaries, data contracts/stores/APIs/events/jobs, and threat
        controls. Evidence: architecture chapters.
  - [ ] V0.13.c Document editorial/ethics/AI/regulatory/standards/gate policies,
        raw measurement versus verdict, approvals, and remedies. Evidence:
        governance chapters.
  - [ ] V0.13.d Document task-led UI/IA, responsive/accessibility/offline/
        performance states, semantic alternatives, and authorized reachability.
        Evidence: experience chapter.
  - [ ] V0.13.e Document migrations/compatibility, rollout/rollback, operations/
        recovery/observability, verification, evidence, and launch criteria.
        Evidence: delivery chapters.
  - [ ] V0.13.f Record alternatives/decisions/owners/dates, resolve every
        substantive review comment, and obtain named ratification. Evidence:
        approved design.
- [ ] V0.14 Reconcile every Veritas application, `libs/veritas/*` package,
      database entity, BFF/CMS/API route, reader walkthrough, agent, worker,
      scheduled job, and documented feature at atomic-feature level. Evidence:
      explicit dispositions cover ingestion, research/tips, editorial/claims,
      knowledge graph, multimedia/live, newsletter/social, multilingual,
      election/regulatory, community, notifications/emergency, reader/search/
      recommendation, B2B/USSD, subscription/revenue, analytics, archive, and
      operations.
  - [x] V0.14.a Build a source inventory across code, schema, routes,
        navigation, walkthroughs, workers/jobs/agents, documentation, flags, and
        deployed integrations. Evidence: reconciliation inputs.
        <!-- evidence: veritas-code-census.json — comprehensive launch census: 64
                                                                                                                                                                                                                                                                        libs packages + 13 app subsystems (9 worker/agent/media: agents/ai-workers/
                                                                                                                                                                                                                                                                        analytics/audio/ingestion/nlp/notifications/social/video), 1270 code files +
                                                                                                                                                                                                                                                                        156 tests, 58 routes across studio/reader/CMS/BFF, 9 state enums (boundary
                                                                                                                                                                                                                                                                        clean), 79 docs, and 28 deploy targets (web + mobile android/ios + workers +
                                                                                                                                                                                                                                                                        prisma). Consolidated from git counts + committed V0.1/V0.4 evidence.
                                                                                                                                                                                                                                                                        Atomic-feature split + dispositions are V0.14.b+ (semantic).
                                                                                                                                                                                                                                                                        generate-veritas-code-census.mjs + 4 tests. -->
  - [ ] V0.14.b Split each source item into atomic user/operator/system
        capabilities with host, audience, owner, dependencies, data class, and
        current implementation status. Evidence: atomic ledger.
  - [ ] V0.14.c Cover ingestion/research/tips, editorial/claims/graph,
        multimedia/live, newsletter/social/messaging, multilingual/election/
        regulatory, and community explicitly. Evidence: editorial coverage.
  - [ ] V0.14.d Cover reader/search/recommendation/archive, notifications/
        emergency, B2B/USSD/voice, subscriptions/revenue/analytics, and
        operations explicitly. Evidence: audience/operations coverage.
  - [ ] V0.14.e Assign keep/consolidate/migrate/replace/deprecate/remove/
        externalize decisions with rationale, target owner/host, dependencies,
        and delivery/expiry. Evidence: disposition ledger.
  - [ ] V0.14.f Reconcile ledger to routes/packages/entities/contracts/tests and
        fail any orphan, duplicate owner, unknown feature, or unreviewed row.
        Evidence: completeness gate.
- [ ] V0.15 Define the boundary among protected newsroom authoring, public
      reader/mobile, source/tip intake, live/community moderation, commercial
      operations, B2B developer, USSD/voice, and platform-operator surfaces.
      Evidence: every retained capability has one host and audience plus a
      redacted handoff where appropriate.
      <!-- SPEC 2026-09-11: the surfaces that exist today are: consumer room
      (`apps/oshun/web/src/app/veritas/*`, `/domains/veritas/*`), public reader
      (`apps/veritas/web`), commercial/B2B/USSD routes inside `apps/veritas/api`
      (payments, subscriptions, ads, b2b-api, tips), and the unauthenticated
      CMS. Protected newsroom authoring is absent. .b assigns every one of
      these; .f's suite runs against `apps/veritas/api` and the CMS as served.
      -->
  - [ ] V0.15.a Define audience/trust/data/action boundaries for each named
        surface and prohibit shared UI/service assumptions that cross them.
        Evidence: surface boundary matrix.
  - [ ] V0.15.b Assign every retained capability and privileged mutation to one
        canonical host/owner, with authorized role/project/channel context.
        Evidence: host ledger.
  - [ ] V0.15.c Define typed redacted handoffs for tips, public feedback,
        moderation, live operations, commercial conflicts, B2B consumption, and
        operator incidents. Evidence: handoff contracts.
  - [ ] V0.15.d Define identity/session/token/deep-link/storage/logging/
        notification separation and step-up transitions between surfaces.
        Evidence: security model.
  - [ ] V0.15.e Define public correction/retraction and emergency propagation
        without granting public/commercial/channel systems editorial authority.
        Evidence: control-flow diagrams.
  - [ ] V0.15.f Test authorized journeys and attempted cross-surface field/
        function/object access; eliminate duplicate mutations. Evidence:
        boundary suite.
- [ ] V0.16 Ratify Ghana NMC/editorial, election-period, data-protection,
      accessibility, child/trauma, advertising/sponsorship, payment, emergency
      broadcast, and platform-channel obligations separately. Evidence:
      jurisdiction/version/owner matrix and feature-specific enforcement plan.
      <!-- SPEC 2026-09-11: the only code-owned floors today are
      `apps/veritas/api/src/domain/compliance/{nmc,election}.ts` and the
      `nmc_complaints` table. .a needs counsel; an agent may fill the register
      with the obligation text and leave reviewer/effective date blank (§2.5.9).
      -->
  - [ ] V0.16.a Identify current authoritative obligations, jurisdiction/scope/
        effective date/version/review cadence, counsel or specialist owner, and
        conflicts for each named regime. Evidence: obligation register.
  - [ ] V0.16.b Translate NMC/editorial/election obligations into content/
        source/claim/review/schedule/publish/complaint/remedy controls.
        Evidence: editorial enforcement map.
  - [ ] V0.16.c Translate data-protection/accessibility/child/trauma obligations
        into collection, minimization, consent, display, distribution,
        retention, request, and review controls. Evidence: rights/safety map.
  - [ ] V0.16.d Translate advertising/sponsorship/payment obligations into
        separation, disclosure, consent, entitlement, refund, audit, and
        newsroom-independence controls. Evidence: commercial map.
  - [ ] V0.16.e Translate emergency-broadcast and each platform/channel rule
        into authority, template, targeting, delivery, correction/recall,
        moderation, rate, and archival controls. Evidence: channel map.
  - [ ] V0.16.f Link each obligation to feature/code-owned floor, policy
        version, tests/evidence/approver, operator runbook, and update trigger;
        record unresolved legal questions. Evidence: ratified matrix.

### V1 — Story, source, claim, evidence, and editorial contracts

- [ ] V1.1 Define story, pitch, assignment, draft, block, headline, dek, byline,
      topic, desk, edition, package, media, and publish-target entities.
      Evidence: schema fixtures.
  - [ ] V1.1.a Define stable identity, tenant/newsroom/desk/project scope,
        lifecycle, version, timestamps, actor, and audit fields for each entity.
        Evidence: base schemas.
  - [ ] V1.1.b Define pitch→assignment→story→draft/block and headline/dek/byline
        ownership/cardinality/order/reference invariants. Evidence: editorial
        relationship fixtures.
  - [ ] V1.1.c Define topic/desk/edition/package/media/publish-target
        membership, locale/territory/rights, scheduling, and release
        relationships. Evidence: distribution fixtures.
  - [ ] V1.1.d Define state-transition, deletion/archive, validation, unknown
        enum, and cross-tenant reference constraints. Evidence: schema invariant
        tests.
  - [ ] V1.1.e Publish storage/API/event/read-model schemas with versioning and
        representative valid/invalid fixtures. Evidence: contract package.
- [ ] V1.2 Define immutable draft revisions and semantic block/range anchors.
      Evidence: paragraph, quote, claim, and comment anchors survive edits or
      become explicitly orphaned.
  - [ ] V1.2.a Define immutable revision identity, parent(s), author, cause,
        schema/editor version, created time, and content hash. Evidence:
        revision contract.
  - [ ] V1.2.b Define stable block identity/type/order and semantic text-range
        anchors with context/fingerprint/affinity. Evidence: anchor schema.
  - [ ] V1.2.c Define deterministic anchor mapping across insert/delete/move/
        split/merge/replace and concurrent-operation histories. Evidence:
        transformation tests.
  - [ ] V1.2.d Preserve paragraph/quote/claim/citation/comment/review anchors or
        mark ambiguous/orphaned with candidates and reason; never silently
        retarget. Evidence: edit corpus.
  - [ ] V1.2.e Define restore/branch/merge/compaction/migration and archival
        behavior without mutating historical revisions. Evidence: history tests.
- [ ] V1.3 Use the canonical 15-kind source vocabulary. Evidence: no lossy
      fallback to the smaller legacy type enum.
  - [ ] V1.3.a Pin the canonical 15 identifiers, labels, definitions, permitted
        locators/metadata, and unknown/extension policy. Evidence: vocabulary
        contract.
  - [ ] V1.3.b Inventory every legacy/API/client/storage/index/import/export
        source enum and its consumers. Evidence: usage ledger.
  - [ ] V1.3.c Map each legacy value losslessly or require explicit review/
        refusal for ambiguous values; never collapse to an approximate kind.
        Evidence: migration matrix.
  - [ ] V1.3.d Update validation/forms/search/facets/scoring/gates/NewsML/CMS
        adapters to preserve the canonical kind. Evidence: integration tests.
  - [ ] V1.3.e Round-trip all 15 kinds and unknown historical inputs across
        storage/API/UI/jobs/export. Evidence: kind fixture suite.
- [ ] V1.4 Define source identity separately from protected identity details.
      Evidence: ordinary source records cannot expose compartment secrets.
  - [ ] V1.4.a Define ordinary source pseudonymous/public identity, descriptive
        metadata, type, publisher/organization, quality, and safe display key.
        Evidence: ordinary source schema.
  - [ ] V1.4.b Define separately encrypted/compartmented legal/contact/
        communications/location/safety/linkage identity details and keys.
        Evidence: protected schema.
  - [ ] V1.4.c Define opaque join/reference/tokenization and authorized reveal/
        reidentification workflows without inferable sequential identifiers.
        Evidence: boundary contract.
  - [ ] V1.4.d Define redacted projections for stories, claims, reviews, search,
        notifications, logs, analytics, exports, backups, and public products.
        Evidence: projection fixtures.
  - [ ] V1.4.e Test object/field/function/correlation access across roles,
        tenants, compartments, revocation, and emergency grants. Evidence:
        negative suite.
- [ ] V1.5 Define source quality vector, domain weight profile, band,
      adjustments, reasons, evaluator version, and evidence. Evidence: canonical
      algorithm fixture.
  - [ ] V1.5.a Define each raw factor’s meaning, domain/range/unit, missing/
        unknown handling, evidence locator, and measurement provenance.
        Evidence: factor schema.
  - [ ] V1.5.b Define versioned domain weight profiles, applicability,
        normalization, combination/rounding, band thresholds, and confidence.
        Evidence: algorithm specification.
  - [ ] V1.5.c Define manual adjustment direction/magnitude/floor/cap/reason/
        evidence/authority/expiry and keep it separate from raw measurement.
        Evidence: adjustment contract.
  - [ ] V1.5.d Define result identity, source/revision/domain, evaluator/
        weights/policy versions, inputs, timestamp, staleness, and supersession.
        Evidence: result schema.
  - [ ] V1.5.e Publish hand-calculated positive/boundary/missing/adversarial
        fixtures and property invariants. Evidence: canonical goldens.
- [ ] V1.6 Define claim class, text, anchor, severity/risk, confidence band,
      retraction state, supporting sources, counter-sources, and status.
      Evidence: canonical contract invariants.
  - [ ] V1.6.a Define claim identity/class/text/normalized proposition, story/
        draft/revision/anchor scope, author/origin, and lifecycle. Evidence:
        claim schema.
  - [ ] V1.6.b Define independent severity/harm/legal risk, evidential
        confidence, verification/editorial status, and contested/retraction
        state vocabularies. Evidence: state model.
  - [ ] V1.6.c Define typed supporting/contradicting/contextual/irrelevant
        source links with citation locator, assessment, version, and reviewer.
        Evidence: evidence-link schema.
  - [ ] V1.6.d Define status-transition, minimum evidence, stale-source,
        counter-source, merge/split, anchor orphan, correction, and retraction
        invariants. Evidence: contract tests.
  - [ ] V1.6.e Define protected/public/read-model projections and prohibit
        confidence/status conflation or unsupported “verified” defaults.
        Evidence: projection fixtures.
- [ ] V1.7 Define citation locator types for page, section, paragraph, quote,
      dataset slice, court record, transcript, audio/video timecode, image
      region, and URI fragment. Evidence: validation fixtures.
  - [ ] V1.7.a Define a discriminated locator contract with common source/
        revision/content-hash identity and kind-specific fields. Evidence:
        locator schema.
  - [ ] V1.7.b Define page/section/paragraph/quote locators with numbering,
        labels, text/context fingerprints, editions, and range rules. Evidence:
        document fixtures.
  - [ ] V1.7.c Define dataset/court/transcript locators with table/query/row/
        column/cell, jurisdiction/docket/filing, speaker/turn/line semantics.
        Evidence: structured fixtures.
  - [ ] V1.7.d Define media/URI locators with time/range/track/frame/region/
        coordinate/version/fragment and bounded normalization. Evidence: media/
        web fixtures.
  - [ ] V1.7.e Define resolution/unreachable/changed/ambiguous/stale handling
        and prohibit silent relocation to new source content. Evidence:
        validation corpus.
- [ ] V1.8 Define evidence packs with story/claim/source membership, reviewer,
      state, version, and integrity hash. Evidence: referential-integrity tests.
  - [ ] V1.8.a Define immutable pack identity/version/parent, story/draft/
        revision scope, purpose, creator, created time, and integrity algorithm.
        Evidence: pack schema.
  - [ ] V1.8.b Define ordered typed membership for claims/sources/citations/
        excerpts/media/measurements/attestations with pinned versions/hashes.
        Evidence: membership schema.
  - [ ] V1.8.c Define draft/in-review/approved/rejected/superseded/invalid
        state, reviewer/quorum/decision, and mutation-invalidates-approval
        rules. Evidence: pack state machine.
  - [ ] V1.8.d Define canonical serialization/hash/signature verification and
        behavior for missing, changed, unauthorized, or retracted members.
        Evidence: integrity tests.
  - [ ] V1.8.e Test cross-story/tenant/compartment references, duplicate/
        circular membership, stale versions, and tampering. Evidence:
        referential-integrity suite.
- [ ] V1.9 Define counterclaim and unsupported-claim entities without
      manufacturing equivalence. Evidence: canonical four-way surfacing result.
  - [ ] V1.9.a Define counterclaim identity/proposition/class/origin/anchor/
        evidence/confidence/status and relation to one or more subject claims.
        Evidence: counterclaim schema.
  - [ ] V1.9.b Define unsupported-claim reason, missing evidence, attempted
        verification, severity, owner, remediation, and allowed disposition.
        Evidence: unsupported schema.
  - [ ] V1.9.c Define versioned measurements for relevance, evidential quality,
        independence, consensus, harm, uncertainty, and false-balance risk.
        Evidence: assessment contract.
  - [ ] V1.9.d Define four distinct surfacing results—surface, suppress false
        balance, require more evidence, editorial review—with reason/evidence/
        policy version. Evidence: decision schema.
  - [ ] V1.9.e Test that popularity/number of sources alone cannot create
        equivalence and that contested/unknown/evaluator failure never becomes
        settled. Evidence: invariant fixtures.
- [ ] V1.10 Define attestor identity, scope, credential, domain, conflict,
      validity, disagreement, and decision. Evidence: attestation invariants.
  - [ ] V1.10.a Define attestor person/organization/service identity and
        verified contact/credential references separate from public display
        identity. Evidence: identity schema.
  - [ ] V1.10.b Define attestation scope over exact claim/source/evidence-pack/
        draft revision, domain/jurisdiction, question, and permitted conclusion.
        Evidence: scope contract.
  - [ ] V1.10.c Define credential/issuer/verification/domain fit, conflict/
        independence declaration, validity window, revocation, and expiry.
        Evidence: eligibility schema.
  - [ ] V1.10.d Define agree/disagree/qualified/abstain/unable decisions with
        rationale/evidence/uncertainty, signature, and immutable timestamp.
        Evidence: decision contract.
  - [ ] V1.10.e Define disagreement aggregation, quorum, specialist floors,
        supersession, stale-input invalidation, and public/redacted projection.
        Evidence: attestation invariants.
- [ ] V1.11 Define correction notes, prior public version, reader-visible
      change, distribution action, and affected claims. Evidence: chronological
      and text-match invariants.
  - [ ] V1.11.a Define correction
        identity/type/severity/status/reason/discovery/ owner, exact
        story/release/locale/target, and immutable chronology. Evidence:
        correction schema.
  - [ ] V1.11.b Pin prior and replacement public revisions/blocks/ranges/text/
        media with semantic diff and hashes. Evidence: version-link contract.
  - [ ] V1.11.c Define affected claims/citations/sources/packages and whether
        evidential confidence, status, or downstream content changes. Evidence:
        impact schema.
  - [ ] V1.11.d Define reader-visible note wording/placement/timestamp/history
        and target-specific update/replace/notify actions. Evidence: notice/
        action schema.
  - [ ] V1.11.e Enforce prior-text match, chronological append-only history,
        immutable published evidence, idempotency, and correction-of-correction.
        Evidence: invariant tests.
- [ ] V1.12 Define retraction cascade, cause, source/claim/story graph, actions,
      target states, owner, progress, and verification. Evidence: schema.
  - [ ] V1.12.a Define cascade identity/cause/scope/severity/authority/trigger/
        source revision and required emergency/standard process. Evidence:
        cascade schema.
  - [ ] V1.12.b Represent affected source→citation→claim→story→package→target
        dependency graph with versioned discovery and completeness state.
        Evidence: impact graph.
  - [ ] V1.12.c Define per-node/edge retain-correct-withdraw-retract-unpublish/
        notify/reindex/invalidate actions and desired target state. Evidence:
        action plan schema.
  - [ ] V1.12.d Define owner/approval/dependency/order/attempt/idempotency/
        progress/failure/retry/compensation state per action. Evidence:
        execution schema.
  - [ ] V1.12.e Define downstream read-back verification, unresolved/unsupported
        target handling, closure criteria, audit, and reopen behavior. Evidence:
        verification contract.
- [ ] V1.13 Define embargo, schedule, exclusivity, rights, territory, and
      syndication metadata. Evidence: policy tests.
  - [ ] V1.13.a Define embargo policy independently from editorial/release state
        with start/end/event trigger/time zone/authority/beneficiaries.
        Evidence: embargo schema.
  - [ ] V1.13.b Define schedule windows, target/locale/edition dependencies,
        readiness conditions, retries, missed-window, and reschedule semantics.
        Evidence: schedule schema.
  - [ ] V1.13.c Define exclusivity type/party/window/channel/exception and
        relationship to embargo, rights, and syndication. Evidence: exclusivity
        contract.
  - [ ] V1.13.d Define rights holder/license/use/format/channel/territory/
        language/duration/attribution/fee/expiry and proof. Evidence: rights
        schema.
  - [ ] V1.13.e Define syndication partner/package/transform/attribution/
        correction-retraction obligations and validate overlapping/conflicting/
        expired policies. Evidence: policy tests.
- [ ] V1.14 Define NewsML-G2 News Item, Package Item, Concept/Knowledge Item,
      Planning Item, and update/correction mappings. Evidence: profile matrix.
      <!-- SPEC 2026-09-11: greenfield — no NewsML code exists. Put the mapping
      in `libs/contracts/src/veritas/newsml-g2.ts` with goldens under
      `libs/contracts/src/veritas/goldens/newsml/`, and validate .f against the
      vendored IPTC XSDs (V0.12.a), never against our own parser only. -->
  - [ ] V1.14.a Map story releases and media renditions to News Item identity,
        version/status, content metadata, rights, subjects, and content sets.
        Evidence: News Item mapping.
  - [ ] V1.14.b Map editions/collections/related coverage and ordered/
        role-qualified membership to Package Items. Evidence: Package mapping.
  - [ ] V1.14.c Map people/organizations/places/events/topics/claims and their
        aliases/relationships/validity/provenance to Concept/Knowledge Items.
        Evidence: concept mapping.
  - [ ] V1.14.d Map assignments/coverage plans/schedules/embargoes/deliverables/
        contacts to Planning Items without leaking protected sources. Evidence:
        planning mapping.
  - [ ] V1.14.e Define initial/update/correction/replacement/retraction version
        chains, signals, prior references, and target read-back. Evidence:
        lifecycle matrix.
  - [ ] V1.14.f Validate required/optional/extension/cardinality/code-list rules
        against the pinned profile and goldens. Evidence: profile tests.
- [ ] V1.15 Define C2PA media and article-package provenance relationships.
      Evidence: ingredient and editing lineage fixture.
      <!-- SPEC 2026-09-11: build on `libs/veritas/content-auth` (its
      `c2paValidator` seam is the boundary) and `c2pa-node@0.5.26`; a manifest
      state must be one of valid/tampered/stripped/unknown/untrusted/expired and
      never "verified" when the validator was not configured. -->
  - [ ] V1.15.a Define claim generators/signers/trust stores/identities and
        supported C2PA version/algorithms/assertions. Evidence: trust profile.
  - [ ] V1.15.b Map original media captures/imports as ingredients with hashes,
        capture/source metadata, rights, and protected-data minimization.
        Evidence: ingredient schema.
  - [ ] V1.15.c Record edit/transform/AI/action lineage, tools/versions/
        parameters, ingredient relationships, and derived renditions. Evidence:
        action lineage.
  - [ ] V1.15.d Relate article/package manifests to exact media/article
        revisions, NewsML/CMS release, corrections, and replacements. Evidence:
        package relationship.
  - [ ] V1.15.e Define valid/tampered/stripped/unknown/untrusted/expired states
        and display/gate behavior without treating absence as falsity. Evidence:
        validation fixtures.
- [ ] V1.16 Define version compatibility with existing Veritas records.
      Evidence: migration goldens.
  - [ ] V1.16.a Inventory deployed record/event/blob/index/API/client/CMS/
        distribution versions and historical edge cases. Evidence: version
        corpus.
  - [ ] V1.16.b Define support window and per-version read/write/migrate/export/
        rollback behavior for every contract in V1. Evidence: compatibility
        matrix.
  - [ ] V1.16.c Write deterministic adapters/migrations for renamed/split/
        merged/encrypted/compartmented/versioned fields with explicit loss.
        Evidence: migration code/tests.
  - [ ] V1.16.d Rebuild indexes/read models and reconcile references/hashes/
        anchors/state/history after migration. Evidence: rehearsal report.
  - [ ] V1.16.e Round-trip goldens through old/current readers and writers,
        interrupted retries, unknown fields, and downgrade refusal. Evidence:
        migration suite.
  - [ ] V1.16.f Document irreversible steps, backup/restore, rollout/rollback,
        telemetry, and operator approval. Evidence: migration runbook.

### V2 — Pitch, assignment, research, and source registry

- [ ] V2.1 Build zero-form pitch capture with desk, topic, thesis, public
      interest, urgency, location, sources, risks, format, and target window.
      Evidence: keyboard-first journey.
  - [ ] V2.1.a Define a fast capture command/inbox entry with minimal required
        text, reporter identity, timestamp, origin, and draft privacy. Evidence:
        capture contract.
  - [ ] V2.1.b Add progressive structured extraction/editing for desk/topic/
        thesis/public interest/urgency/location/format/window without blocking
        initial capture. Evidence: field tests.
  - [ ] V2.1.c Attach existing/new/protected source references and safety/legal/
        conflict/embargo risks through authorized redacted projections.
        Evidence: source/risk tests.
  - [ ] V2.1.d Support save/duplicate/merge/submit/park/discard with autosave,
        stable identity, validation, audit, and explicit state. Evidence:
        lifecycle tests.
  - [ ] V2.1.e Complete capture/triage/edit/submit using keyboard and screen
        reader on responsive web/desktop without a long mandatory form.
        Evidence: keyboard-first E2E.
- [ ] V2.2 Build story-angle and headline divergence behind the fenced provider
      seam. Evidence: exact outputs, attribution, and no auto-selection.
  - [ ] V2.2.a Define request contract with pinned pitch/story revision, allowed
        source excerpts, constraints, locale/desk/style, count, and budget.
        Evidence: provider request schema.
  - [ ] V2.2.b Generate materially distinct angle and headline candidates
        separately, preserving factual constraints and protected-data policy.
        Evidence: known-input tests.
  - [ ] V2.2.c Return exact candidate text, rationale, source-use/citations,
        model/provider/prompt/version, cost, uncertainty, and safety results.
        Evidence: attributed response.
  - [ ] V2.2.d Present unranked or transparently scored candidates for human
        compare/edit/accept/reject with no hidden winner or automatic mutation.
        Evidence: selection tests.
  - [ ] V2.2.e Evaluate duplication, factual drift, sensationalism, source
        invention, protected leakage, provider failure, and deterministic
        replay. Evidence: divergence eval.
- [ ] V2.3 Build pitch comparison with novelty, evidence readiness, risk,
      audience, production scope, and conflict views. Evidence: score
      provenance.
  - [ ] V2.3.a Define comparable raw measures, units, missing/unknown handling,
        weights/policies, and provenance for each named dimension. Evidence:
        comparison contract.
  - [ ] V2.3.b Select two or more pitch revisions and show normalized side-by-
        side thesis/source/evidence/risk/audience/scope/conflict differences.
        Evidence: comparison UI tests.
  - [ ] V2.3.c Show measurement evidence, evaluator/weight/policy versions,
        uncertainty, freshness, and manual adjustments rather than opaque
        scores. Evidence: provenance tests.
  - [ ] V2.3.d Preserve protected-source redaction and prevent count/timing/
        similarity leakage to unauthorized comparators. Evidence: auth tests.
  - [ ] V2.3.e Record commission/merge/park/kill decision with chosen revision,
        rationale, conflicts/recusal, dissent, and no automatic selection.
        Evidence: decision audit.
- [ ] V2.4 Build assignment planning with owner, contributors, deadlines,
      deliverables, budget, embargo, dependencies, and safety plan. Evidence:
      state and notification tests.
  - [ ] V2.4.a Define assignment identity/story/pitch revision, accountable
        owner, role-qualified contributors, desk, state, priority, and scope.
        Evidence: assignment schema.
  - [ ] V2.4.b Define milestone/deadline/time-zone/deliverable/format/locale/
        target acceptance criteria and dependency graph. Evidence: plan schema.
  - [ ] V2.4.c Define budget/currency/approval/spend constraints, embargo/
        exclusivity/rights, and fieldwork/source/data/media safety plan.
        Evidence: constraint schema.
  - [ ] V2.4.d Implement propose/accept/decline/reassign/update/pause/complete/
        cancel transitions with reason, optimistic concurrency, and audit.
        Evidence: state tests.
  - [ ] V2.4.e Send permission-filtered deadline/dependency/risk/change/
        escalation notifications without embargo or protected-source leakage.
        Evidence: notification suite.
- [ ] V2.5 Build protected-source onboarding in a compartmented workflow.
      Evidence: step-up authorization and redacted audit.
  - [ ] V2.5.a Begin onboarding from an authorized story/tip with purpose,
        pseudonymous ordinary source record, compartment request, and safety
        assessment. Evidence: initiation tests.
  - [ ] V2.5.b Capture identity/contact/communications/relationship/consent/
        risk details only inside the encrypted compartment with minimization.
        Evidence: storage inspection.
  - [ ] V2.5.c Verify source-protection role, step-up, grant/quorum/expiry,
        device/session restrictions, and emergency contact rules. Evidence:
        authorization tests.
  - [ ] V2.5.d Create opaque linkage/redacted projections for assignment,
        claims, evidence, reviews, notifications, logs, search, and exports.
        Evidence: projection tests.
  - [ ] V2.5.e Exercise onboard/update/revoke/reidentify/emergency-access/
        retention/legal-hold workflows with tamper-evident redacted audit.
        Evidence: protected-source E2E.
- [ ] V2.6 Build public-source intake from URL, document, feed, wire, dataset,
      court record, transcript, image, audio, and video. Evidence: kind-specific
      validation.
  - [ ] V2.6.a Define intake request/status/result contracts and kind-specific
        metadata/locator/rights/language/size/type requirements. Evidence:
        source intake schemas.
  - [ ] V2.6.b Implement URL/feed/wire fetch through allowlisted network policy
        with redirects, robots/license/rate, authenticity, and capture metadata.
        Evidence: remote intake tests.
  - [ ] V2.6.c Implement document/dataset/court/transcript parsing with format/
        structure/encoding/schema/table/docket/speaker validation. Evidence:
        structured intake tests.
  - [ ] V2.6.d Implement image/audio/video ingest with media probing, duration/
        dimensions/tracks, metadata privacy, transcripts/proxies, and safe
        preview. Evidence: media intake tests.
  - [ ] V2.6.e Classify canonical source kind, deduplicate without destroying
        provenance, preserve failures/quarantine, and require human metadata/
        rights review. Evidence: intake journey.
- [ ] V2.7 Quarantine and scan remote/downloaded content. Evidence: malicious
      file, SSRF, redirect, archive, and decompression corpus.
      <!-- SPEC 2026-09-11: the fetch sites to fence are
      `apps/veritas/ingestion`, `apps/veritas/api` URL imports (sources, tips)
      and any `fetch`/`undici` call in `libs/veritas/ingestion-core`; the
      quarantine store is MinIO from `docker/docker-compose.dev.yml`. SSRF cases
      here are the same list V9.7.f reuses. -->
  - [ ] V2.7.a Place all untrusted bytes in isolated non-executable quarantine
        before parsing/preview/indexing/model use. Evidence: storage boundary
        tests.
  - [ ] V2.7.b Enforce URL scheme/host/IP/port/redirect/DNS/private-network/
        credential/size/time policy at every fetch hop. Evidence: SSRF corpus.
  - [ ] V2.7.c Detect type/signature mismatch, malware/active content/polyglots,
        malformed media, and unsafe parser conditions. Evidence: scan corpus.
  - [ ] V2.7.d Enforce archive path/symlink/nesting/member/count/ratio/
        decompressed-size limits and scan every extracted member. Evidence:
        archive bomb tests.
  - [ ] V2.7.e Record scan engine/signature/policy versions and release/reject/
        manual-review decisions; rescan on updates. Evidence: quarantine audit.
  - [ ] V2.7.f Test timeout/crash/partial scan/unknown type/false-positive
        override with no unscanned content escaping. Evidence: failure suite.
- [ ] V2.8 Preserve original bytes, fetch metadata, checksum, extraction,
      capture time, and rights. Evidence: immutable source evidence.
  - [ ] V2.8.a Store original bytes in immutable content-addressed blobs after
        quarantine with tenant/compartment encryption and retention policy.
        Evidence: blob tests.
  - [ ] V2.8.b Record origin URL/provider/request/redirect/headers/status/
        filename/MIME/size/fetcher/version and trusted clock capture time.
        Evidence: fetch manifest.
  - [ ] V2.8.c Compute/version strong checksums for original and every derived
        extraction/proxy/transcript/snapshot with parent lineage. Evidence:
        integrity tests.
  - [ ] V2.8.d Record extractor/parser/model/version/config/output/errors and
        exact source ranges so derived text remains traceable. Evidence:
        extraction provenance.
  - [ ] V2.8.e Capture rights/license/terms/territory/expiry/attribution/proof
        separately from factual provenance and block unsupported use. Evidence:
        rights tests.
  - [ ] V2.8.f Prove later refetch/update cannot overwrite originals and that
        export/read-back verifies bytes, metadata, and chain. Evidence:
        immutable evidence suite.
- [ ] V2.9 Build source-quality scoring from the canonical composite and domain
      weights. Evidence: independently verified known values.
  - [ ] V2.9.a Implement each raw factor as a versioned pure measurement with
        evidence, unit, missing/unknown state, and no policy verdict. Evidence:
        factor tests.
  - [ ] V2.9.b Resolve the applicable pinned domain-weight profile and validate
        completeness/ranges/normalization before computation. Evidence: profile
        tests.
  - [ ] V2.9.c Implement the canonical combine/adjust/round/band/confidence
        algorithm exactly and preserve all intermediate values. Evidence:
        algorithm tests.
  - [ ] V2.9.d Persist result/source revision/evaluator/weight/policy/corpus
        versions and mark stale when inputs or policy change. Evidence:
        provenance tests.
  - [ ] V2.9.e Compare independently hand-calculated positive/boundary/missing/
        adversarial fixtures and cross-language implementations. Evidence:
        known-value report.
- [ ] V2.10 Build source review with factor evidence, manual adjustments,
      conflict, attestation, and expiry. Evidence: override authorization.
  - [ ] V2.10.a Show source identity/redacted projection, revision, raw factors,
        locators, measurements, algorithm, band, confidence, freshness, and
        prior reviews. Evidence: review UI.
  - [ ] V2.10.b Require factor-by-factor inspect/confirm/challenge/recompute and
        preserve reviewer disagreement instead of overwriting measurements.
        Evidence: review tests.
  - [ ] V2.10.c Implement bounded manual adjustment with authorized role,
        direction/value/reason/evidence, conflict/recusal, and second approval
        where required. Evidence: override tests.
  - [ ] V2.10.d Attach applicable attestation with exact scope, credential,
        independence, decision, expiry, and redacted protected-source handling.
        Evidence: attestation tests.
  - [ ] V2.10.e Set review/adjustment/attestation expiry and stale triggers for
        source updates, retractions, policy/weight changes, or new contrary
        evidence. Evidence: lifecycle tests.
  - [ ] V2.10.f Record immutable approve/reject/request-evidence/contest/
        override decisions and prohibit unauthorized direct score edits.
        Evidence: authorization/audit suite.
- [ ] V2.11 Build source search, dedupe, relationship, publisher history, and
      retraction history. Evidence: protected fields never enter the index.
  - [ ] V2.11.a Define permission-aware indexed safe fields, tokenization/
        language, facets, freshness, deletion, and compartment exclusion.
        Evidence: search schema.
  - [ ] V2.11.b Implement exact/fuzzy/semantic retrieval with stable pagination,
        result reasons, current source revision, and no protected count/snippet/
        suggestion/timing leaks. Evidence: search tests.
  - [ ] V2.11.c Implement deterministic/explainable duplicate candidates using
        public identifiers, publisher, content hashes, locators, and reviewed
        merge/link/not-duplicate decisions. Evidence: dedupe tests.
  - [ ] V2.11.d Model
        source/publisher/ownership/derivation/citation/translation/ syndication
        relationships with temporal validity and provenance. Evidence:
        relationship tests.
  - [ ] V2.11.e Show publisher correction/retraction/reliability history with
        evidence, scope, version, and no automatic guilt-by-association verdict.
        Evidence: history tests.
  - [ ] V2.11.f Inspect index/documents/logs/cache/analytics under two tenants
        and compartments to prove protected fields never enter search. Evidence:
        leakage suite.
- [ ] V2.12 Build source communications references without storing plaintext in
      ordinary newsroom tables. Evidence: vault/secure-system links and access
      audit.
  - [ ] V2.12.a Define opaque communication-reference contract for external
        secure-system/provider/conversation/item identity, type, time, and safe
        label. Evidence: reference schema.
  - [ ] V2.12.b Store addresses, message bodies, attachments, keys, and
        sensitive metadata only in approved compartment/vault systems. Evidence:
        storage inspection.
  - [ ] V2.12.c Implement authorized create/link/open/verify/unlink with
        step-up, short-lived scoped tokens, current access grants, and safe
        return. Evidence: access journey.
  - [ ] V2.12.d Provide redacted ordinary projections that reveal only existence
        and workflow state when authorized, never participant/content metadata.
        Evidence: projection tests.
  - [ ] V2.12.e Audit attempts and successful access/export without copying
        plaintext into ordinary audit/log/notification/search/analytics data.
        Evidence: audit tests.
- [ ] V2.13 Build research notebook and evidence-board views. Evidence: every
      excerpt remains anchored to a source.
  - [ ] V2.13.a Define notebook note/question/hypothesis/task/excerpt/entity/
        relationship/timeline item and board grouping/order/link contracts.
        Evidence: workspace schema.
  - [ ] V2.13.b Create excerpts only from pinned source revisions with exact
        locator/text/hash/context and protected/redacted identity semantics.
        Evidence: anchoring tests.
  - [ ] V2.13.c Support authoring/linking/grouping/filtering/reordering/status/
        assignment without converting notes or hypotheses into verified claims.
        Evidence: editor tests.
  - [ ] V2.13.d Detect source change/retraction/unreachable locator and show
        stale/orphan/candidate remap with explicit reviewer action. Evidence:
        update tests.
  - [ ] V2.13.e Support collaborative revision/comments/offline conflicts/
        export with story/compartment permissions and legal holds. Evidence:
        collaboration tests.
  - [ ] V2.13.f Verify every displayed/exported excerpt resolves to exact source
        evidence or is visibly unresolved. Evidence: integrity gate.
- [ ] V2.14 Build story cluster and related coverage suggestions. Evidence:
      human acceptance and model/version attribution.
  - [ ] V2.14.a Define candidate inputs/features/exclusions and cluster/
        relationship types without exposing protected or embargoed material.
        Evidence: suggestion contract.
  - [ ] V2.14.b Generate cluster membership/related-story suggestions with
        similarity/evidence/rationale, confidence/uncertainty, and freshness.
        Evidence: model tests.
  - [ ] V2.14.c Attribute model/provider/version/config/index/source revisions,
        cost, and generation time; retain raw result separately from decisions.
        Evidence: provenance record.
  - [ ] V2.14.d Let editors accept/reject/merge/split/relabel suggestions
        individually with rationale and no automatic public curation/ranking.
        Evidence: decision tests.
  - [ ] V2.14.e Evaluate duplication, topic drift, feedback loops, subgroup/
        language performance, correction/retraction propagation, and stale
        suggestions. Evidence: calibrated report.
- [ ] V2.15 Build source update/retraction preview. Evidence: read-only blast
      radius across claims, stories, packages, newsletters, social, audio,
      video, and archives.
  - [ ] V2.15.a Ingest or draft a source update/retraction event with exact
        source/revision, publisher evidence, cause, scope, time, confidence, and
        verification state. Evidence: event contract.
  - [ ] V2.15.b Traverse versioned source→citation→claim→story→package→target
        dependencies without performing mutations. Evidence: impact query.
  - [ ] V2.15.c Include draft/published stories, packages, newsletters, social,
        audio/video timecodes, archives/search, B2B/channel consumers, and
        unknown/unregistered edges. Evidence: coverage tests.
  - [ ] V2.15.d Show exact affected locator/claim/release/target, current state,
        proposed remedy, owner, urgency, confidence, and permission-filtered
        redaction. Evidence: preview UI.
  - [ ] V2.15.e Pin the preview graph/version and require a separately
        authorized correction/retraction plan before any downstream action.
        Evidence: read-only enforcement.
  - [ ] V2.15.f Verify completeness against registered consumers and display
        unresolved/stale/unauthorized targets rather than reporting a safe blast
        radius. Evidence: reconciliation tests.
- [ ] V2.16 Build decision records for pitch commission, kill, park, merge, and
      reassignment. Evidence: rationale and source state preserved.
  - [ ] V2.16.a Define immutable decision identity/type/actor/role/time and
        exact pitch/story/assignment/source/evidence revisions considered.
        Evidence: decision schema.
  - [ ] V2.16.b Require decision-specific rationale, public-interest/novelty/
        readiness/risk/scope/conflict considerations, uncertainty, and dissent.
        Evidence: policy validation.
  - [ ] V2.16.c For commission, create a linked assignment without mutating the
        original pitch/evidence snapshot. Evidence: commission tests.
  - [ ] V2.16.d For kill/park, record retention/reopen/review conditions and
        preserve sources, safety restrictions, and legal holds. Evidence:
        disposition tests.
  - [ ] V2.16.e For merge/reassign, preserve lineage, duplicate identity,
        source/compartment grants, deadlines, ownership acceptance, and audit.
        Evidence: transfer tests.
  - [ ] V2.16.f Enforce roles/recusal/concurrency and prohibit model/agent
        impersonation or silent state change. Evidence: authorization tests.
- [ ] V2.17 Build ingestion operations for feed/source schedules, crawl and
      social monitors, normalization, language detection, dedupe, clustering,
      retries, quarantine, source health, rate limits, robots/licensing policy,
      and operator replay. Evidence: live item-to-story-cluster lineage and
      failure journey.
  - [ ] V2.17.a Define feed/source/crawl/social monitor configuration, owner,
        credential reference, schedule, cursor, locale/territory, policy, and
        enabled state. Evidence: monitor contracts.
  - [ ] V2.17.b Implement due scheduling, leases, concurrency, backoff/jitter,
        rate/provider quotas, cancellation, missed runs, and no duplicate active
        run. Evidence: scheduler tests.
  - [ ] V2.17.c Enforce robots/licensing/terms/allowlist/network/redirect/
        credential policy before fetch and record policy version/decision.
        Evidence: fetch policy tests.
  - [ ] V2.17.d Preserve raw response/item bytes and fetch provenance, then
        quarantine/scan/unpack/parse with isolated failures. Evidence: intake
        trace.
  - [ ] V2.17.e Normalize identifiers/times/authors/publishers/topics/content/
        media/rights while retaining original values and transform lineage.
        Evidence: normalization tests.
  - [ ] V2.17.f Detect language/script with uncertainty and human correction;
        deduplicate exact/near/updated/syndicated items without losing sources.
        Evidence: language/dedupe tests.
  - [ ] V2.17.g Generate reviewed story-cluster candidates with versioned model/
        features/evidence and preserve correction/retraction relationships.
        Evidence: clustering tests.
  - [ ] V2.17.h Expose source health/run/item/quarantine/error/dead-letter/
        retry/replay state with permissioned logs and trace correlation.
        Evidence: operations UI.
  - [ ] V2.17.i Make replay idempotent against pinned raw inputs/config/code and
        prevent re-notification or auto-publication. Evidence: replay tests.
  - [ ] V2.17.j Run live item→raw evidence→normalized item→cluster lineage plus
        outage/rate/policy/malware/parser/poison-item recovery. Evidence:
        end-to-end journey.
- [ ] V2.18 Build anonymous tip intake and investigative triage with secure
      submission, attachment scanning, identity/minimization choices, receipt
      code, threat/safety assessment, protected assignment, retention, and
      source conversion. Evidence: no ordinary newsroom log, notification,
      analytics event, or search index reveals the tipster.
  - [ ] V2.18.a Define an isolated public intake surface with current threat
        guidance, no required identity, locale/accessibility, rate/abuse
        controls, and safe emergency alternatives. Evidence: intake design.
  - [ ] V2.18.b Collect tip text and optional contact/identity/minimization/
        consent/retention choices into the protected compartment only. Evidence:
        storage tests.
  - [ ] V2.18.c Upload attachments directly to quarantine with metadata
        minimization, malware/archive/media scanning, and no ordinary preview.
        Evidence: attachment tests.
  - [ ] V2.18.d Issue a high-entropy nonenumerable receipt code and protected
        status/reply channel with no existence, timing, or identity oracle.
        Evidence: receipt tests.
  - [ ] V2.18.e Triage credibility/urgency/coercion/retaliation/legal/physical/
        digital safety with trained roles, step-up, and protected notes.
        Evidence: safety triage.
  - [ ] V2.18.f Create a redacted protected assignment and least-privilege
        grants without disclosing tip content/source beyond need to know.
        Evidence: assignment tests.
  - [ ] V2.18.g Convert to a source only through explicit authorized review,
        preserving tip lineage/choices/consent and not inventing identity.
        Evidence: conversion tests.
  - [ ] V2.18.h Apply retention/deletion/legal hold/emergency access and
        redacted tamper-evident audit. Evidence: lifecycle tests.
  - [ ] V2.18.i Inspect ordinary DB/logs/errors/traces/notifications/search/
        analytics/cache/backups/support exports for tipster leakage. Evidence:
        non-leakage gate.
- [ ] V2.19 Build investigative research plans for document sets, datasets,
      entities, relationships, timelines, public records, interviews, requests,
      hypotheses, verification status, and collaboration compartments. Evidence:
      every assertion remains anchored and export respects source
      protection/legal holds.
  - [ ] V2.19.a Define plan identity/scope/question/owner/team/state/milestones/
        risk/budget/legal-hold and exact pitch/story revision. Evidence: plan
        schema.
  - [ ] V2.19.b Define document/dataset collections with immutable inputs,
        schemas/queries/transforms/snapshots, review status, and rights.
        Evidence: material contracts.
  - [ ] V2.19.c Define entity/alias/relationship/timeline items with temporal
        validity, uncertainty, provenance, merge/split, and protected identity.
        Evidence: research graph schema.
  - [ ] V2.19.d Define public-record/interview/information-request tasks with
        jurisdiction/contact/deadline/response/appeal/safety and secure
        communication references. Evidence: task contracts.
  - [ ] V2.19.e Define hypothesis/assertion/counter-hypothesis, required
        evidence, supporting/contradicting locators, confidence, verifier, and
        status without promoting notes to facts. Evidence: hypothesis schema.
  - [ ] V2.19.f Build compartmented workstreams/membership/grants/comments/
        notifications/search with cross-compartment redacted handoffs. Evidence:
        collaboration tests.
  - [ ] V2.19.g Build plan board/timeline/notebook/evidence views with stable
        filters, dependencies, stale-source warnings, and complete anchor
        resolution. Evidence: workspace tests.
  - [ ] V2.19.h Export an authorized purpose-bound snapshot with redaction/
        watermark/expiry/audit while enforcing source protection and legal
        holds. Evidence: export tests.
  - [ ] V2.19.i Verify every assertion resolves to pinned evidence or is
        explicitly unsupported/contested/unknown. Evidence: integrity gate.
- [ ] V2.20 Build knowledge-graph entity resolution and relationship review for
      people, organizations, places, events, topics, claims, aliases, temporal
      validity, merge/split, confidence, provenance, and retractions. Evidence:
      graph suggestions require review and protected entities remain
      compartmented.
  - [ ] V2.20.a Define canonical entity/relation/alias/identifier/attribute
        schemas with type, temporal validity, uncertainty, provenance, tenant/
        compartment scope, and version. Evidence: graph contracts.
  - [ ] V2.20.b Extract resolution/link candidates from pinned source/story/
        claim revisions behind the provider seam with model/features/evidence/
        confidence attribution. Evidence: candidate tests.
  - [ ] V2.20.c Present side-by-side candidate/current records and conflicting
        identifiers/attributes/relationships/times for human accept/reject/
        defer. Evidence: resolution review.
  - [ ] V2.20.d Implement merge with surviving identity, field-level provenance,
        alias/edge/reference redirect, protected-grant union constraints, and
        reversible audit. Evidence: merge tests.
  - [ ] V2.20.e Implement split with explicit record/alias/edge/evidence/
        history allocation, downstream impact preview, and no orphaned claims.
        Evidence: split tests.
  - [ ] V2.20.f Review/create/update/delete relationships with source locators,
        direction/type, validity interval, confidence, contradiction, and
        authorization. Evidence: relation tests.
  - [ ] V2.20.g Propagate source/claim/entity retraction or correction as
        versioned invalidation/review signals without deleting historical graph
        evidence. Evidence: retraction tests.
  - [ ] V2.20.h Exclude protected entities/identifiers/degree/count/snippets
        from unauthorized graph/search/API/log/cache projections. Evidence:
        compartment tests.
  - [ ] V2.20.i Provide a semantic table/tree/path alternative and verify
        keyboard/screen-reader operation for all review actions. Evidence:
        accessible graph E2E.

### V3 — Drafting, media, claims, and authoring assist

- [ ] V3.1 Build the story board with guarded editorial-state transitions.
      Evidence: server state-machine tests.
  - [ ] V3.1.a Define board columns/views from canonical editorial state plus
        independent desk/owner/priority/deadline/embargo/gate/review dimensions.
        Evidence: board view model.
  - [ ] V3.1.b Query permission-filtered live story summaries with stable sort/
        filter/group/pagination and no protected source or embargo leakage.
        Evidence: board query tests.
  - [ ] V3.1.c Expose only server-authorized transitions for current state/role/
        revision/gates/approvals, with reason/impact/confirmation. Evidence:
        transition UI tests.
  - [ ] V3.1.d Enforce transitions atomically server-side with optimistic
        concurrency, idempotency, audit, notifications, and no drag-only path.
        Evidence: state-machine suite.
  - [ ] V3.1.e Handle stale cards, conflicts, partial/degraded data, revoked
        access, and real-time updates without implying a transition succeeded.
        Evidence: resilience tests.
- [ ] V3.2 Build the story dossier with assignment, draft, sources, claims,
      evidence, media, gates, reviews, schedule, distribution, and history.
      Evidence: live-data browser journey.
  - [ ] V3.2.a Define dossier route/context/read model pinned to story and
        selected revision with permission-filtered section summaries/freshness.
        Evidence: dossier contract.
  - [ ] V3.2.b Compose primary draft editor with assignment/source/claim/
        evidence/media inspectors rather than duplicating owned records.
        Evidence: workspace tests.
  - [ ] V3.2.c Show gates/reviews/schedule/distribution current state, blockers,
        owners, versions, timestamps, remediation, and eligible actions.
        Evidence: status tests.
  - [ ] V3.2.d Provide chronological state/revision/decision/approval/publish/
        correction history with redacted protected events. Evidence: history
        tests.
  - [ ] V3.2.e Exercise authorized/unauthorized live database journey from
        assignment through current public read-back, including stale/degraded/
        empty states. Evidence: browser E2E.
- [ ] V3.3 Build a structured article editor for paragraph, heading, list,
      quote, fact box, data, table, image, gallery, audio, video, embed,
      correction, and disclosure blocks. Evidence: block registry.
  - [ ] V3.3.a Define versioned block registry with stable ID, kind-specific
        content, children/order, validation, serialization, migration, and
        semantic output contract. Evidence: registry schema.
  - [ ] V3.3.b Implement text structure blocks—paragraph/heading/list/quote/fact
        box—with canonical commands, paste normalization, and anchor stability.
        Evidence: text block tests.
  - [ ] V3.3.c Implement data/table blocks with source/dataset/query/transform/
        units/uncertainty and accessible table semantics. Evidence: data block
        tests.
  - [ ] V3.3.d Implement image/gallery/audio/video/embed blocks via approved
        assets/allowlists with captions/transcripts/rights/provenance and safe
        fallbacks. Evidence: media block tests.
  - [ ] V3.3.e Implement correction/disclosure blocks with policy-required
        immutable links and target renderer behavior. Evidence: policy block
        tests.
  - [ ] V3.3.f Test add/edit/move/split/merge/copy-paste/delete/undo/redo/
        collaboration across all kinds and reject unknown/unsafe content.
        Evidence: block registry suite.
- [ ] V3.4 Build attribution-aware quote editing with speaker, source, locator,
      verification, context, edit notation, and consent. Evidence: quote
      validation.
  - [ ] V3.4.a Define quote text/language, exact source revision/locator,
        speaker/public label versus protected identity, and captured context.
        Evidence: quote contract.
  - [ ] V3.4.b Support exact/paraphrase/translated/edited quote types with
        omission/interpolation/clarification notation and immutable original.
        Evidence: edit tests.
  - [ ] V3.4.c Record verifier/method/time/result, recording/transcript/source
        relationship, uncertainty, and stale/unreachable state. Evidence:
        verification tests.
  - [ ] V3.4.d Record consent/on-record/background/off-record terms, scope,
        expiry/withdrawal, rights, safety restrictions, and authorized display.
        Evidence: consent tests.
  - [ ] V3.4.e Gate publication on kind-specific attribution/context/locator/
        verification/consent rules and prevent identity leakage. Evidence: quote
        validation suite.
- [ ] V3.5 Build media attachment with rights, caption, credit, alt text,
      transcript, content warning, location/privacy, and C2PA status. Evidence:
      publish gate.
  - [ ] V3.5.a Attach only approved immutable media asset/revision/rendition
        with source/story provenance, hash, scan state, and intended
        block/target. Evidence: attachment contract.
  - [ ] V3.5.b Capture rights/license/holder/territory/channel/locale/expiry/
        attribution and block unsupported use or expired rights. Evidence:
        rights tests.
  - [ ] V3.5.c Capture factual caption/credit, language, alt text/long
        description, transcript/captions/audio description, and review status.
        Evidence: accessibility tests.
  - [ ] V3.5.d Capture graphic/trauma/child/privacy/location/doxxing warnings,
        redactions/crops, consent, and distribution-specific restrictions.
        Evidence: harm/privacy tests.
  - [ ] V3.5.e Validate C2PA valid/tampered/stripped/unknown lineage separately
        from editorial truth and show exact provenance status. Evidence: C2PA
        tests.
  - [ ] V3.5.f Compute target-specific publish eligibility from all current
        fields/reviews without silently dropping media or metadata. Evidence:
        publish gate suite.
- [ ] V3.6 Build selection-anchored claim creation. Evidence: claim remains
      attached through unrelated edits.
  - [ ] V3.6.a Capture current draft revision, selected
        block/range/text/context, direction/affinity, author, and intended claim
        class. Evidence: creation contract.
  - [ ] V3.6.b Create normalized claim text separately from quoted selection
        while preserving exact origin anchor and requiring human confirmation.
        Evidence: creation tests.
  - [ ] V3.6.c Map the anchor through unrelated before/after edits, formatting,
        block moves, and collaboration operations. Evidence: survival tests.
  - [ ] V3.6.d Mark ambiguous/deleted/split/merged replacement as orphaned or
        candidate-remap requiring review, never silently retarget. Evidence:
        adversarial edit tests.
  - [ ] V3.6.e Preserve stable claim identity and history across revisions,
        undo/redo, restore, copy, and branch/merge. Evidence: revision tests.
- [ ] V3.7 Build claim extraction behind the fenced provider seam. Evidence:
      proposals require human confirmation.
      <!-- SPEC 2026-09-11: "fenced provider seam" is the M2.6 shape: bounded
      authorized passages in, typed proposals out, human confirmation before
      anything canonical, provider/model/prompt/config version on every
      proposal. Bind the model per the Phase V header note (5);
      `libs/veritas/claims` is the starting point and imports `@veritas/llm`.
      -->
  - [ ] V3.7.a Define request with pinned draft blocks/revision, eligible text,
        locale/domain/classes, excluded protected fields, budget, and policy.
        Evidence: request schema.
  - [ ] V3.7.b Return candidate claim text/class/anchor/severity/confidence/
        rationale and source-use without writing canonical claim records.
        Evidence: response tests.
  - [ ] V3.7.c Record provider/model/prompt/config/version, input/output hashes,
        cost/latency/safety, and run provenance. Evidence: attribution.
  - [ ] V3.7.d Present candidates for individual accept/edit/reject/duplicate
        handling and revalidate anchors against the current revision. Evidence:
        confirmation tests.
  - [ ] V3.7.e Evaluate missed high-risk claims, invented/merged claims, bad
        anchors, protected leakage, injection, and provider failure. Evidence:
        extraction eval.
- [ ] V3.8 Build citation and counter-source attachment with exact locators.
      Evidence: duplicate and invalid link tests.
  - [ ] V3.8.a Search/select an authorized source revision and create/validate a
        kind-specific exact locator with excerpt/context/hash. Evidence: locator
        journey.
  - [ ] V3.8.b Classify relationship as supporting/contradicting/contextual/
        irrelevant/counter-source with assessment, confidence, and reviewer.
        Evidence: link contract.
  - [ ] V3.8.c Show source quality/retraction/rights/freshness/protection state
        without leaking compartmented identity. Evidence: projection tests.
  - [ ] V3.8.d Detect exact/overlapping/semantically duplicate links and require
        deliberate merge/retain decisions. Evidence: duplicate tests.
  - [ ] V3.8.e Reject cross-tenant/story-policy violations, invalid/unreachable/
        stale locators, wrong revisions, unsupported kinds, and unauthorized
        fields. Evidence: invalid link suite.
  - [ ] V3.8.f Preserve link revision/history and invalidate affected gates/
        approvals when citation/source/claim changes. Evidence: lifecycle tests.
- [ ] V3.9 Build source/claim/evidence sidebar with authority, confidence,
      status, contradiction, attestation, and retraction context. Evidence:
      screen-reader accessible.
  - [ ] V3.9.a Define sidebar view model pinned to draft/selection/claim/source
        revisions with permissions, freshness, loading, and partial state.
        Evidence: inspector contract.
  - [ ] V3.9.b Show sources/citations/evidence locators, raw quality factors,
        band/confidence/authority, and protected redaction. Evidence: source
        inspector tests.
  - [ ] V3.9.c Show claim severity/status/support/counter-sources/
        contradictions/unsupported state and exact anchors. Evidence: claim
        inspector tests.
  - [ ] V3.9.d Show evidence-pack integrity, attestation scope/credential/
        conflict/disagreement/expiry, and retraction/correction impact.
        Evidence: evidence inspector tests.
  - [ ] V3.9.e Support inspect/link/unlink/request review/remediate operations
        with server authorization and focus returning to the editor. Evidence:
        action tests.
  - [ ] V3.9.f Provide headings/lists/tables/status text/announcements and
        keyboard synchronization with the editor at zoom/reflow. Evidence:
        screen-reader E2E.
- [ ] V3.10 Build headline/dek variants and body-accuracy comparison. Evidence:
      exact gate outputs.
  - [ ] V3.10.a Create immutable human or attributed AI headline/dek candidate
        revisions linked to exact body/claim/evidence revision. Evidence:
        variant schema.
  - [ ] V3.10.b Compare candidates side by side for factual propositions,
        entities/numbers/time/location, certainty, omissions, tone, length,
        target, and accessibility. Evidence: comparison UI.
  - [ ] V3.10.c Run versioned accuracy/sensationalism/clickbait/SEO/format gates
        and display raw findings/evidence separately from verdicts. Evidence:
        gate output tests.
  - [ ] V3.10.d Allow edit/select/reject only by authorized humans with
        rationale and no model/score auto-selection. Evidence: decision tests.
  - [ ] V3.10.e Recompute/invalidate comparison and approval when body/claims/
        sources/candidate/policy changes. Evidence: freshness tests.
- [ ] V3.11 Build authoring assist for structure, clarity, headline, summary,
      SEO, localization, and format adaptation. Evidence: per-change accept and
      AI attribution.
  - [ ] V3.11.a Define separate assist operations with pinned input revision/
        selection, constraints, eligible sources, target/locale/style, provider
        policy, and budgets. Evidence: operation contracts.
  - [ ] V3.11.b Generate structure/clarity suggestions as minimal anchored diffs
        that preserve claims/quotes/citations/meaning. Evidence: editing tests.
  - [ ] V3.11.c Generate headline/summary/SEO suggestions with factual
        proposition comparison and no keyword-driven certainty or sensational
        drift. Evidence: accuracy tests.
  - [ ] V3.11.d Generate localization/format adaptations linked to exact source
        text, terminology, claims/evidence, disclosure, rights, and target
        constraints. Evidence: adaptation tests.
  - [ ] V3.11.e Present each change for accept/edit/reject with preview,
        rationale/source use, conflicts, and stale-revision rebase. Evidence:
        interaction tests.
  - [ ] V3.11.f Record model/provider/prompt/config/version/input/output/cost
        and accepted human actor per change; never bulk-apply silently.
        Evidence: attribution audit.
- [ ] V3.12 Prohibit AI from silently inventing sources, quotes, facts,
      citations, or named attribution. Evidence: adversarial generation eval.
  - [ ] V3.12.a Enforce typed outputs that distinguish verbatim source content,
        grounded inference, proposal, placeholder, uncertainty, and refusal.
        Evidence: response schema tests.
  - [ ] V3.12.b Allow source/quote/citation/attribution references only to
        authorized provided stable IDs and validate locators/text exactness.
        Evidence: grounding validator.
  - [ ] V3.12.c Reject or visibly flag invented/altered/unresolvable references,
        unsupported facts, merged speakers, and implied named attribution.
        Evidence: enforcement tests.
  - [ ] V3.12.d Require human confirmation before canonical
        claim/citation/quote/ source/attribution creation and retain generator
        provenance. Evidence: mutation boundary tests.
  - [ ] V3.12.e Evaluate prompt/source injection, plausible fake citations,
        source laundering, paraphrase drift, multilingual names, and multi-turn
        persistence. Evidence: adversarial eval.
  - [ ] V3.12.f Define stop-the-line thresholds, regression ownership, provider/
        model rollback, and approved residual exceptions. Evidence: safety gate.
- [ ] V3.13 Build word/character, reading time, media, claim density, citation,
      and freshness meters from server logic. Evidence: known-value tests.
  - [ ] V3.13.a Define canonical eligible content, Unicode/grapheme/locale
        rules, exclusions, units, rounding, and versions for
        word/character/reading time. Evidence: metric contracts.
  - [ ] V3.13.b Define media counts/durations/types and claim/citation density
        numerators, denominators, duplicate/overlap, and zero-content behavior.
        Evidence: density contracts.
  - [ ] V3.13.c Define source/citation/claim/evidence/policy freshness inputs,
        clocks, thresholds, unknown/unavailable state, and invalidation.
        Evidence: freshness contract.
  - [ ] V3.13.d Compute authoritatively on exact server revision and return raw
        values, units, timestamp, calculator/policy version, and evidence.
        Evidence: API tests.
  - [ ] V3.13.e Show advisory meters without converting them into automatic
        quality/editorial scores or overriding gates. Evidence: UI tests.
  - [ ] V3.13.f Verify hand-calculated Unicode/locale/block/media/anchor/stale/
        missing fixtures. Evidence: known-value suite.
- [ ] V3.14 Build preview for reader web, mobile, newsletter, wire/package,
      audio, video, and social targets. Evidence: renderer parity.
  - [ ] V3.14.a Define target/version/locale/device/audience preview request
        pinned to exact draft/media/policy/template/config revisions. Evidence:
        preview contract.
  - [ ] V3.14.b Render reader web/mobile and newsletter using production-shared
        renderers with navigation, disclosures, corrections, accessibility, and
        responsive behavior. Evidence: reader/email previews.
  - [ ] V3.14.c Render NewsML/wire/package structured payload and validation
        diagnostics without hiding unsupported/dropped fields. Evidence: package
        preview.
  - [ ] V3.14.d Render audio/video timeline/transcript/captions/chapters/media/
        provenance and platform aspect/safe-area variants. Evidence: AV preview.
  - [ ] V3.14.e Render social/channel copy/media/link/cards/disclosures/
        corrections/limits per current adapter capability. Evidence: target
        previews.
  - [ ] V3.14.f Compare preview semantic output to published/read-back output,
        label fixtures/staleness, and block unsupported target claims. Evidence:
        renderer parity suite.
- [ ] V3.15 Build autosave, undo/redo, revision history, semantic diff, restore,
      presence, and conflict resolution. Evidence: concurrent edit Playwright.
  - [ ] V3.15.a Define local operation/draft/server
        revision/checkpoint/presence/ conflict schemas and eligible
        protected/offline storage policy. Evidence: collaboration contract.
  - [ ] V3.15.b Implement debounced idempotent autosave with expected revision,
        dirty/saving/saved/error/offline/conflict state and crash recovery.
        Evidence: autosave tests.
  - [ ] V3.15.c Implement operation-aware undo/redo scoped to the actor/session
        without reverting collaborators or immutable published history.
        Evidence: history tests.
  - [ ] V3.15.d Provide immutable revision timeline and semantic block/text/
        claim/citation/media/disclosure diff with author/cause/provenance.
        Evidence: diff tests.
  - [ ] V3.15.e Restore as a new revision with preview, permissions, invalidated
        gates/approvals/schedules, and no history rewrite. Evidence: restore
        tests.
  - [ ] V3.15.f Show privacy-filtered presence/selection and resolve concurrent
        block/text/anchor/source/claim edits using mine/theirs/base with copies.
        Evidence: conflict tests.
  - [ ] V3.15.g Run two-client web/desktop/offline/delete/reorder/role-revoke/
        reconnect journeys. Evidence: concurrent Playwright.
- [ ] V3.16 Build disclosure editor for AI assistance, methodology, corrections,
      conflicts, sponsorship, and limitations. Evidence: target-specific
      required-field gates.
  - [ ] V3.16.a Define typed disclosure records with scope, exact story/block/
        media/release/locale/target revision, author, status, and history.
        Evidence: disclosure schema.
  - [ ] V3.16.b Build AI-use and methodology disclosures from attributed
        provenance/methods while allowing accurate human-authored explanation.
        Evidence: disclosure tests.
  - [ ] V3.16.c Build correction/history and conflicts/recusal disclosures
        linked to canonical decisions without exposing protected/legal details.
        Evidence: editorial disclosure tests.
  - [ ] V3.16.d Build sponsorship/advertising/partnership and limitations/
        uncertainty disclosures separated from editorial content and controls.
        Evidence: independence tests.
  - [ ] V3.16.e Define required types/fields/placement/wording/reviewer by desk/
        risk/audience/locale/target and recompute on changes. Evidence: policy
        matrix.
  - [ ] V3.16.f Preview/read-back exact reader-visible disclosures and block
        release if required content is missing, stale, or silently dropped.
        Evidence: target gate tests.
- [ ] V3.17 Build journalism, research, fact-checking, standards, copy-edit,
      format, and distribution agent plans with least-privilege tool grants.
      Evidence: story revision, allowed sources, policy, budget, stop
      conditions, and approval points are locked before dispatch.
  - [ ] V3.17.a Define distinct plan templates and allowed outcomes for
        journalism/research/fact-check/standards/copy-edit/format/distribution;
        prohibit unsupported role blending. Evidence: template registry.
  - [ ] V3.17.b Pin tenant/story/draft/source/evidence/policy/locale/target
        revisions, objective, constraints, expected artifacts, and acceptance
        criteria. Evidence: immutable plan inputs.
  - [ ] V3.17.c Resolve allowed sources/fields and least-privilege read/search/
        analyze/draft/propose/job tools with object/function scopes and expiry.
        Evidence: grant manifest.
  - [ ] V3.17.d Lock token/cost/time/step/tool/concurrency/provider budgets,
        retry/fallback, data-use rules, and protected-source exclusions.
        Evidence: budget/policy manifest.
  - [ ] V3.17.e Define checkpoint/human approval/attestation-required/stop-the-
        line/cancellation/escalation conditions before dispatch. Evidence:
        control contract.
  - [ ] V3.17.f Preview exact plan/grants/risks to an authorized human, record
        approval, sign/hash it, and reject stale or mutated dispatch. Evidence:
        dispatch tests.
- [ ] V3.18 Build Veritas-scoped agent run, queue, plan, tool-call, budget,
      source-use, output, and provenance views. Evidence: protected-source
      redaction and live trace.
  - [ ] V3.18.a Define run/step/tool-call/queue/event/budget/source-use/output/
        artifact/provenance state contracts and retention. Evidence: run schema.
  - [ ] V3.18.b Build queue/run list with owner/story/plan/status/progress/
        budget/priority/wait reason/failure/created/updated and stable filters.
        Evidence: operations view.
  - [ ] V3.18.c Build live run timeline showing immutable plan, step
        transitions, tool name/scoped purpose/input-output summaries, retries,
        decisions, and human interventions. Evidence: trace view.
  - [ ] V3.18.d Show token/cost/time/tool/step limits versus actual/forecast and
        stop/cancel controls with truthful delayed accounting. Evidence: budget
        view tests.
  - [ ] V3.18.e Show exact allowed/used source/evidence IDs, locators, revision,
        result attribution, and derived artifact lineage. Evidence: provenance
        view.
  - [ ] V3.18.f Apply compartment/field redaction to UI/API/events/logs/export
        and prevent counts/timing/tool summaries from revealing protected
        sources. Evidence: non-leakage tests.
  - [ ] V3.18.g Test live update reconnect/order/deduplication/stale/partial/
        unavailable and terminal-state reconciliation. Evidence: live trace E2E.
- [ ] V3.19 Intake agent output only as attributed suggestions, proposed claims,
      or draft revisions. Evidence: agents cannot create source identity, attest
      facts, approve legal/standards gates, publish, correct, or retract.
  - [ ] V3.19.a Define an allowlisted output envelope for suggestion, proposed
        claim/citation, anchored edit set, or complete draft revision with run/
        plan/model/tool/source provenance. Evidence: output schema.
  - [ ] V3.19.b Validate output type/schema/size/anchors/revisions/source IDs/
        locators/grounding/rights/safety and quarantine malformed content.
        Evidence: intake validation.
  - [ ] V3.19.c Store output in a proposal namespace with explicit unreviewed
        state, no canonical source/attestation/approval/release semantics, and
        safe rendering. Evidence: persistence tests.
  - [ ] V3.19.d Present semantic diff/evidence/source use/uncertainty for
        per-item authorized human accept/edit/reject; rebase or invalidate stale
        outputs. Evidence: review tests.
  - [ ] V3.19.e Apply accepted content as a human-attributed new draft/claim
        revision while retaining AI contribution attribution and re-running
        gates. Evidence: acceptance tests.
  - [ ] V3.19.f Attempt source identity creation, attestation, legal/standards
        approval, publish/correct/retract, privilege escalation, and direct API
        mutation; prove rejection/no side effect. Evidence: capability tests.
- [ ] V3.20 Implement agent pause, redirect, cancel, budget kill, and replay.
      Evidence: partial results retain source/run provenance and cannot appear
      approved.
  - [ ] V3.20.a Define legal control transitions by run/step/job state, actor,
        authority, idempotency, and terminal/commit boundary. Evidence: control
        state machine.
  - [ ] V3.20.b Pause at safe checkpoints, freeze new tools, retain leases/
        partial state, and resume only after current plan/grant validation.
        Evidence: pause tests.
  - [ ] V3.20.c Redirect via a reviewed plan amendment/new plan with changed
        objective/constraints/grants/budgets, preserving the original. Evidence:
        redirect tests.
  - [ ] V3.20.d Cancel queued/running/tool/job phases with provider
        cancellation, compensation, timeout, and honest
        requested-versus-completed state. Evidence: cancellation tests.
  - [ ] V3.20.e Enforce automatic/manual budget kill at every metering boundary
        with bounded overshoot and no new unmetered actions. Evidence: kill
        tests.
  - [ ] V3.20.f Replay from pinned plan/inputs/tool versions or explicitly mark
        substitutions/nonreproducibility; deduplicate committed effects.
        Evidence: replay tests.
  - [ ] V3.20.g Preserve all partial suggestions/artifacts/source use/run
        lineage as incomplete/unapproved and require fresh human review.
        Evidence: status tests.
- [ ] V3.21 Evaluate multi-agent handoffs for fabricated citation, source
      laundering, instruction loss, privilege expansion, and correlated error.
      Evidence: adversarial corpus and stop-the-line thresholds.
  - [ ] V3.21.a Define typed handoff envelope with parent/child run, pinned
        constraints/policy/grants/budget, source/evidence IDs, claims,
        artifacts, uncertainty, and unresolved issues. Evidence: handoff schema.
  - [ ] V3.21.b Verify receivers cannot reinterpret unavailable/protected/
        unverified sources as ordinary evidence or drop source-use provenance.
        Evidence: laundering tests.
  - [ ] V3.21.c Test altered/invented citations/quotes/claims across extraction,
        research, fact-check, editing, localization, and distribution hops.
        Evidence: fabrication corpus.
  - [ ] V3.21.d Test loss/contradiction of negative instructions, disclosure/
        embargo/rights/locale constraints, approval requirements, and stop
        conditions. Evidence: instruction-retention tests.
  - [ ] V3.21.e Test delegated tool/role/field/object/tenant/compartment
        privilege expansion and confused-deputy attacks. Evidence: capability
        tests.
  - [ ] V3.21.f Measure correlated error/amplification, independence,
        calibration, detection/abstention, and subgroup/language performance
        over chained runs. Evidence: evaluation report.
  - [ ] V3.21.g Set per-severity stop thresholds, block dispatch/intake/release
        on breach, assign mitigation/model rollback, and require human approval.
        Evidence: stop-the-line gate.
- [ ] V3.22 Build audio/podcast production for script/rundown, host/voice,
      recording, clips, music/SFX, edit, mix, loudness, transcript, chapters,
      captions, translations, rights, provenance, review, and feed/package
      outputs. Evidence: story claims and corrections stay linked to timecoded
      audio.
  - [ ] V3.22.a Define audio project/episode/script/rundown/segment/track/clip/
        marker/version schemas linked to exact story/claim/evidence revisions.
        Evidence: audio contracts.
  - [ ] V3.22.b Build script/rundown editing with host/guest/voice assignments,
        pronunciation, fact-check holds, timing, disclosures, and source
        timecode/citation links. Evidence: preproduction tests.
  - [ ] V3.22.c Ingest or capture recording with device/session/files/checksums/
        consent, original immutable audio, proxies, and privacy-safe metadata.
        Evidence: recording lineage.
  - [ ] V3.22.d Build clip/select/trim/sequence/fade/music/SFX editing using
        nondestructive operations, licensed assets, and semantic/non-drag
        controls. Evidence: edit tests.
  - [ ] V3.22.e Build mix/render with track/voice/music/SFX balances, channel/
        codec/rate, target loudness/true peak, deterministic job provenance, and
        quality measurements. Evidence: mix tests.
  - [ ] V3.22.f Generate/edit/align transcripts, speakers, chapters, captions,
        descriptions, and translations with human review and locale parity.
        Evidence: accessibility/localization tests.
  - [ ] V3.22.g Enforce voice/music/recording/source/territory/channel/expiry
        rights, AI/synthetic voice disclosure, consent, and C2PA/provenance.
        Evidence: rights/provenance gate.
  - [ ] V3.22.h Build editorial/fact-check/legal/accessibility/audio review on
        exact version with timecoded comments and stale invalidation. Evidence:
        review journey.
  - [ ] V3.22.i Generate/validate feed/enclosure/NewsML/CMS/article/package
        outputs with renditions, metadata, chapters, transcripts, and read-back.
        Evidence: distribution tests.
  - [ ] V3.22.j Preserve claim/citation/correction/retraction links to timecoded
        ranges through edits/renders/replacements and cascade every target.
        Evidence: correction lineage E2E.
- [ ] V3.23 Build video and short-form production for script, shot list, source
      media, edit/timeline, graphics, captions, voice, aspect renditions,
      thumbnails, platform-safe areas, rights, provenance, review, and
      correction replacement. Evidence: YouTube/Shorts/TikTok and article embeds
      derive from locked story evidence.
  - [ ] V3.23.a Define video project/version/sequence/track/clip/shot/graphic/
        caption/marker/rendition schemas linked to locked story evidence.
        Evidence: video contracts.
  - [ ] V3.23.b Build script/shot-list/storyboard planning with claim/source/
        locator, capture/acquisition, contributor, safety, rights, and target
        requirements. Evidence: production plan.
  - [ ] V3.23.c Ingest source media as immutable scanned assets with checksums/
        capture metadata/C2PA/proxies/transcripts and privacy review. Evidence:
        media lineage.
  - [ ] V3.23.d Build nondestructive edit/timeline for clips/audio/voice/music/
        graphics/lower thirds with semantic/non-drag controls and source links.
        Evidence: timeline tests.
  - [ ] V3.23.e Build factual graphics, names/numbers/maps/charts, captions/
        translations/audio descriptions, and human verification against
        evidence. Evidence: overlay/accessibility tests.
  - [ ] V3.23.f Generate landscape/vertical/square renditions and thumbnails
        with crop/reframe/safe-area/text-length/codec/bitrate constraints and no
        meaning loss. Evidence: rendition tests.
  - [ ] V3.23.g Enforce media/music/voice/likeness/location/platform/territory/
        expiry rights, safety warnings, disclosure, and provenance. Evidence:
        publish gates.
  - [ ] V3.23.h Run editorial/fact-check/legal/standards/accessibility review on
        exact timeline/renditions with timecoded comments. Evidence: review
        journey.
  - [ ] V3.23.i Publish/read back YouTube/Shorts/TikTok and article embeds
        through target adapters with partial failure, acknowledgement, and no
        silent auto-publish. Evidence: channel tests.
  - [ ] V3.23.j Replace/correct/retract exact ranges/renditions/thumbnails/
        captions across targets while retaining prior lineage and notices.
        Evidence: correction E2E.
- [ ] V3.24 Build live-program rundown and stream production for segments,
      guests, sources, lower thirds, fact-check hold, delay, captions,
      translations, moderator cues, Q&A, emergency stop, recording, clipping,
      archive, and post-live correction. Evidence: live urgency never becomes a
      generic editorial-gate bypass.
  - [ ] V3.24.a Define live program/rundown/segment/cue/guest/source/claim/
        destination/session/version/state contracts and accountable operator
        roles. Evidence: live schemas.
  - [ ] V3.24.b Build ordered/timed rundown with script, guests/consent,
        sources/evidence, assets, lower thirds, transitions, moderator cues,
        contingencies, and approvals. Evidence: rundown tests.
  - [ ] V3.24.c Validate names/titles/numbers/claims/media/rights/disclosures
        against locked story evidence and route unresolved items to fact-check
        hold. Evidence: preflight gate.
  - [ ] V3.24.d Build ingest/encoder/stream/destination health, preview/program,
        delay buffer, captions/translations, recording, and failover controls.
        Evidence: stream operations tests.
  - [ ] V3.24.e Build moderated Q&A with question intake/screening/source
        status/ identity safety, answer evidence, block/remove/escalate, and
        audit. Evidence: Q&A tests.
  - [ ] V3.24.f Enforce role/step-up/two-step confirmation for go-live, fact-
        check release, delay dump, destination changes, emergency stop, and
        restart. Evidence: control authorization.
  - [ ] V3.24.g Record synchronized program/clean feeds, captions/translations,
        operator/decision/incident timeline, and immutable provenance. Evidence:
        session record.
  - [ ] V3.24.h Create reviewed clips/archive/article embeds with timecode/
        claim/source linkage, rights, accessibility, and target approvals.
        Evidence: post-live workflow.
  - [ ] V3.24.i Author and propagate post-live correction/retraction to
        recordings/clips/captions/transcripts/archive/embeds/channels with
        notices and read-back. Evidence: correction E2E.
  - [ ] V3.24.j Prove urgency cannot bypass protected-source, legal/high-risk,
        evidence, disclosure, accessibility, or human publish floors; test
        emergency authority separately. Evidence: gate suite.
- [ ] V3.25 Build data/visual journalism authoring for datasets, queries,
      transformations, charts/maps/tables, methodology, uncertainty, source
      snapshots, accessible alternatives, responsive embeds, and update
      detection. Evidence: published visualization is reproducible from locked
      data/code and has semantic parity.
  - [ ] V3.25.a Define data project/dataset snapshot/schema/query/transform/
        visualization/view/release contracts linked to story/claims/sources.
        Evidence: data-viz schemas.
  - [ ] V3.25.b Ingest immutable licensed datasets with checksum, acquisition/
        refresh metadata, schema/profile, privacy/classification, and
        quarantine. Evidence: dataset lineage.
  - [ ] V3.25.c Build parameterized query/notebook/transform pipeline with
        pinned engine/dependencies/code/config/random seeds, review, and
        reproducible outputs. Evidence: compute tests.
  - [ ] V3.25.d Build chart/map/table specifications with units/denominators/
        baselines/scales/geography/time, annotation, provenance, and no
        misleading defaults. Evidence: visual contract tests.
  - [ ] V3.25.e Author methodology/limitations/uncertainty/missing-data/
        statistical notes tied to exact data/code/output revisions. Evidence:
        methodology review.
  - [ ] V3.25.f Provide equivalent accessible table/text/key findings, data
        download where permitted, keyboard navigation, contrast/pattern, and
        noncolor encodings. Evidence: semantic parity tests.
  - [ ] V3.25.g Generate responsive embeds/static/social/print renditions with
        stable canonical URL, safe sizing, fallback, performance, and target
        metadata. Evidence: renderer tests.
  - [ ] V3.25.h Detect upstream data/schema/code/dependency changes, compare
        outputs, mark stale, and require human review before update. Evidence:
        update detection.
  - [ ] V3.25.i Publish/read back locked data/code/spec/output/provenance and
        verify independent reproduction within declared tolerances. Evidence:
        reproducibility gate.
  - [ ] V3.25.j Propagate corrections/retractions through claims, annotations,
        tables, embeds, images, downloads, caches, and archives. Evidence:
        correction journey.

### V4 — Verification, balance, attestation, and editorial gates

- [ ] V4.1 Implement canonical source-quality scoring with published factor
      weights and adjustments. Evidence: known-value suite per claim domain.
  - [ ] V4.1.a Implement versioned factor measurements from pinned source
        evidence with explicit missing/unknown/unavailable states. Evidence:
        factor unit tests.
  - [ ] V4.1.b Resolve applicable claim-domain weight profile and validate
        factor/weight ranges, completeness, normalization, and effective date.
        Evidence: profile tests.
  - [ ] V4.1.c Compute canonical composite/intermediate values/rounding/band/
        confidence deterministically and separately from manual adjustments.
        Evidence: algorithm tests.
  - [ ] V4.1.d Apply only authorized bounded reasoned adjustments with evidence,
        actor, expiry, conflict, and immutable raw score. Evidence: adjustment
        tests.
  - [ ] V4.1.e Persist all input/evaluator/weight/policy versions and recompute
        or mark stale on any change. Evidence: provenance tests.
  - [ ] V4.1.f Compare independently calculated boundary/missing/adversarial
        fixtures for every ratified claim domain. Evidence: known-value suite.
- [ ] V4.2 Implement claim extraction quality evaluation against a labeled
      corpus. Evidence: precision/recall and missed-high-risk analysis.
      <!-- SPEC 2026-09-11: §2.5.5 — no labeled representative corpus exists; .a
      builds a synthetic one with ground truth fixed by the fixture and says so,
      .c runs on the bound cheap model, .f's named review stays human. -->
  - [ ] V4.2.a Build versioned representative corpus with exact draft blocks/
        ranges, claim classes, severities, locales/desks, negatives, and expert
        labels. Evidence: corpus manifest.
  - [ ] V4.2.b Define matching rules for span overlap, normalized proposition,
        duplicate/merged/split claims, class, severity, and anchor correctness.
        Evidence: scoring spec.
  - [ ] V4.2.c Run pinned model/provider/prompt/config against held-out corpus
        with deterministic request/output provenance. Evidence: evaluation run.
  - [ ] V4.2.d Report precision/recall/F-score, calibration,
        duplicate/bad-anchor rate, latency/cost, and subgroup/locale/desk
        slices. Evidence: metrics.
  - [ ] V4.2.e Review every missed high-risk and dangerous false-positive claim,
        classify causes/severity, and assign mitigation. Evidence: error
        analysis.
  - [ ] V4.2.f Enforce thresholds/regression comparisons/stop-the-line and named
        human approval before deployment. Evidence: evaluation gate.
- [ ] V4.3 Implement claim-citation support and locator verification. Evidence:
      supporting, irrelevant, contradicted, and unreachable source fixtures.
  - [ ] V4.3.a Resolve exact authorized source revision/locator/content/hash and
        classify unreachable/changed/ambiguous/protected state. Evidence:
        locator tests.
  - [ ] V4.3.b Measure proposition-level entailment/support/contradiction/
        relevance/context and coverage without treating source quality as
        support. Evidence: measurement tests.
  - [ ] V4.3.c Combine multiple citations with independence/duplication/
        conflicts and explicit insufficient/contested/unknown state. Evidence:
        multi-source tests.
  - [ ] V4.3.d Persist claim/source/locator/evaluator/model/prompt/corpus/policy
        versions, raw findings, evidence excerpts, uncertainty, and freshness.
        Evidence: result provenance.
  - [ ] V4.3.e Apply versioned verdict thresholds separately and invalidate on
        claim/source/locator/policy change or retraction. Evidence: verdict
        tests.
  - [ ] V4.3.f Verify supporting/partial/irrelevant/contradicted/unreachable/
        injection/multilingual fixtures with expert expected results. Evidence:
        known-value suite.
- [ ] V4.4 Implement confidence-band rules, including source-count and
      counter-source invariants. Evidence: canonical contract fixtures.
  - [ ] V4.4.a Define versioned inputs for source quality/independence/count,
        citation support, counter-sources, contradiction, attestation, and
        uncertainty. Evidence: confidence contract.
  - [ ] V4.4.b Implement band thresholds and minimum independent-source/
        specialist/evidence requirements by claim class/severity/domain.
        Evidence: rule tests.
  - [ ] V4.4.c Enforce counter-source and
        contested/unsupported/stale/unavailable rules that cap, block, or
        require review; never count duplicates as independence. Evidence:
        invariant tests.
  - [ ] V4.4.d Persist raw inputs/calculation separately from policy verdict and
        recompute when any dependency/policy changes. Evidence: recomputation
        tests.
  - [ ] V4.4.e Test exact boundaries, missing data, many low-quality dependent
        sources, one high-quality source, contradictions, and evaluator failure.
        Evidence: canonical fixtures.
- [ ] V4.5 Implement statistical-claim checks for denominator, population,
      period, method, uncertainty, and source dataset. Evidence: crafted corpus.
  - [ ] V4.5.a Parse or author-review statistic value/unit/direction/comparison,
        numerator/denominator, population/sample, geography, period, and claim
        anchor. Evidence: statistic schema/tests.
  - [ ] V4.5.b Resolve exact dataset snapshot/table/query/row/column/cell and
        verify value/denominator/population/period match. Evidence: data locator
        tests.
  - [ ] V4.5.c Check sampling/design/method/weighting/adjustment/base rate,
        uncertainty/interval/significance, missingness, and limitations.
        Evidence: method checks.
  - [ ] V4.5.d Check chart/text/headline transformations, relative versus
        absolute change, percentage points, causal language, rounding, and
        cherry-picked windows. Evidence: presentation checks.
  - [ ] V4.5.e Return findings/evidence/uncertainty separately from domain-
        versioned risk verdict and specialist requirements. Evidence: result
        contract.
  - [ ] V4.5.f Validate correct/error/ambiguous/adversarial and subgroup-
        sensitive crafted fixtures with expert review. Evidence: corpus report.
- [ ] V4.6 Implement quote checks for source, speaker, context, edit, locator,
      and verification. Evidence: altered-context fixtures.
  - [ ] V4.6.a Resolve exact source revision/locator and compare
        quote/paraphrase/ translation text to the immutable
        excerpt/recording/transcript. Evidence: text-match tests.
  - [ ] V4.6.b Verify speaker identity/public label/protected projection and
        distinguish reporter attribution from source assertion. Evidence:
        speaker tests.
  - [ ] V4.6.c Check surrounding context, question/answer, temporal setting,
        selective omission, sentence joins, and meaning-changing truncation.
        Evidence: context tests.
  - [ ] V4.6.d Validate edit/ellipsis/interpolation/translation notation,
        verification status, consent/terms, and rights. Evidence: policy checks.
  - [ ] V4.6.e Persist raw match/context findings and versions separately from
        editorial verdict; mark stale on source/draft change. Evidence:
        provenance tests.
  - [ ] V4.6.f Run exact/edited/paraphrased/translated/misattributed/altered-
        context/unreachable fixtures. Evidence: quote corpus.
- [ ] V4.7 Implement legal/scientific/high-risk severity and specialist-review
      floors. Evidence: no general source policy discharges specialist gates.
  - [ ] V4.7.a Define versioned legal/scientific/public-health/election/safety/
        financial/other high-risk classes and severity indicators. Evidence:
        risk taxonomy.
  - [ ] V4.7.b Classify applicable risk from exact claims/sources/media/context
        with evidence/uncertainty and authorized human correction. Evidence:
        classification tests.
  - [ ] V4.7.c Map class/severity/jurisdiction/target to named
        credential/domain/ independence/quorum/recusal/expiry specialist floors.
        Evidence: review matrix.
  - [ ] V4.7.d Enforce specialist review as an independent blocking gate that
        source score, general fact-check, AI result, waiver, or urgency cannot
        satisfy. Evidence: bypass tests.
  - [ ] V4.7.e Invalidate review on relevant claim/evidence/media/policy/
        jurisdiction/target change and retain immutable decisions. Evidence:
        freshness tests.
- [ ] V4.8 Implement contradiction detection against current and archived
      claims. Evidence: known contradiction fixtures and uncertainty.
  - [ ] V4.8.a Build permission-aware candidate retrieval over current/archived
        claims by entities, normalized proposition, time, location, units, and
        topic. Evidence: retrieval tests.
  - [ ] V4.8.b Compare exact claim revisions for contradiction/qualification/
        update/different scope/uncertain relation with evidence and confidence.
        Evidence: relation evaluator.
  - [ ] V4.8.c Account for temporal change, definitions, population, units,
        negation, modality, attribution, and correction/retraction state.
        Evidence: semantic fixtures.
  - [ ] V4.8.d Attribute model/rules/corpus/index/source versions and keep
        candidate measurements separate from reviewed relationships. Evidence:
        provenance tests.
  - [ ] V4.8.e Require human confirm/reject/defer and prevent
        protected/embargoed archived claim leakage through results or counts.
        Evidence: review/auth tests.
  - [ ] V4.8.f Evaluate known contradiction/noncontradiction/ambiguous/changed-
        world/multilingual fixtures and calibration. Evidence: corpus report.
- [ ] V4.9 Implement counterclaim surfacing through the canonical policy.
      Evidence: surface, suppress-false-balance, require-more-evidence, and
      editorial-review outcomes.
  - [ ] V4.9.a Gather exact claim/counterclaim/evidence/quality/independence/
        consensus/harm/context measurements with policy version. Evidence: input
        contract.
  - [ ] V4.9.b Compute all four canonical outcomes without reducing them to a
        binary “both sides” or popularity rule. Evidence: decision tests.
  - [ ] V4.9.c Return reason codes, contributing measurements, uncertainty,
        required evidence/reviewer, and safe presentation constraints. Evidence:
        result contract.
  - [ ] V4.9.d Require authorized human disposition for editorial-review and
        bounded override with rationale/evidence/conflict/expiry. Evidence:
        override tests.
  - [ ] V4.9.e Recompute on claim/counter-source/source-quality/consensus/policy
        change and show contested/stale/evaluator-failure explicitly. Evidence:
        lifecycle tests.
  - [ ] V4.9.f Validate each outcome across science/legal/election/identity/
        opinion and adversarial false-balance fixtures. Evidence: canonical
        suite.
- [ ] V4.10 Implement bias/loaded-language assist with uncertainty and human
      review. Evidence: calibrated corpus and subgroup report.
  - [ ] V4.10.a Define limited assist scope, taxonomy, context-sensitive labels,
        locale/desk/genre applicability, and prohibited ideology/author scoring.
        Evidence: assist policy.
  - [ ] V4.10.b Return anchored language findings with rationale, alternatives,
        uncertainty, model/prompt/corpus versions, and no silent edit. Evidence:
        response tests.
  - [ ] V4.10.c Distinguish loaded wording from accurate quotation, legal/
        scientific terminology, reclaimed terms, direct attribution, and
        necessary specificity. Evidence: context fixtures.
  - [ ] V4.10.d Present accept/edit/reject/false-positive feedback to authorized
        humans with per-change provenance and stale-anchor handling. Evidence:
        review tests.
  - [ ] V4.10.e Measure calibration/error severity and subgroup/dialect/locale/
        identity/desk differences with expert review. Evidence: evaluation
        report.
  - [ ] V4.10.f Block use as author/reporter/source/subject reputation or
        publish score and define regression/rollback thresholds. Evidence:
        misuse tests.
- [ ] V4.11 Implement headline/dek accuracy versus body and evidence. Evidence:
      omission, exaggeration, certainty, and clickbait fixtures.
  - [ ] V4.11.a Extract or compare headline/dek propositions, entities, numbers,
        time/location, causal/attribution/modality/certainty against exact body
        and verified claims. Evidence: comparison tests.
  - [ ] V4.11.b Detect unsupported additions, material omissions, scope drift,
        exaggeration, certainty inflation, contradiction, and misleading
        question framing. Evidence: finding tests.
  - [ ] V4.11.c Evaluate sensational/clickbait cues separately from factual
        accuracy and respect genre/locale/desk context. Evidence: style tests.
  - [ ] V4.11.d Persist anchored findings/evidence/model/rule/policy versions,
        uncertainty, and raw measurements separately from verdict. Evidence:
        provenance.
  - [ ] V4.11.e Require edit/review according to risk, invalidate on body/claim/
        evidence/headline changes, and prohibit automatic selection. Evidence:
        gate tests.
  - [ ] V4.11.f Run omission/exaggeration/certainty/clickbait/accurate-strong/
        multilingual/adversarial fixtures. Evidence: known-value suite.
- [ ] V4.12 Implement plagiarism/similarity and attribution checks. Evidence:
      quoted, licensed, common-phrase, and copied fixtures.
  - [ ] V4.12.a Define permissioned comparison corpus, retention/license/
        protected-source exclusions, normalization, and versioned index.
        Evidence: corpus policy.
  - [ ] V4.12.b Detect exact/near/paraphrase/structural overlap with anchored
        matching passages, source candidates, score/uncertainty, and provenance.
        Evidence: similarity tests.
  - [ ] V4.12.c Classify quotation with valid citation, licensed/reused copy,
        wire/syndication, press/common phrases, self-reuse, and unattributed
        copy separately. Evidence: classification tests.
  - [ ] V4.12.d Check exact attribution/quotation/rights/transform requirements
        for source kind and target. Evidence: attribution gate.
  - [ ] V4.12.e Require human review of findings, false positives, contested
        ownership, and protected comparisons; never auto-accuse. Evidence:
        review tests.
  - [ ] V4.12.f Evaluate quoted/licensed/common/copied/paraphrased/multilingual/
        adversarial fixtures and corpus leakage. Evidence: evaluation report.
- [ ] V4.13 Implement rights, media, accessibility, disclosure, and localization
      gates. Evidence: gate registry coverage.
  - [ ] V4.13.a Implement
        rights/license/consent/territory/channel/locale/expiry/ attribution
        checks per source/media/target. Evidence: rights gate tests.
  - [ ] V4.13.b Implement media
        scan/integrity/provenance/caption/credit/privacy/ harm/rendition/target
        eligibility checks. Evidence: media gate tests.
  - [ ] V4.13.c Implement structure/semantics/alt text/transcript/captions/audio
        description/contrast/reflow/keyboard checks with manual-review floors.
        Evidence: accessibility gate tests.
  - [ ] V4.13.d Implement required AI/methodology/correction/conflict/
        sponsorship/limitation type/content/placement/reviewer checks. Evidence:
        disclosure tests.
  - [ ] V4.13.e Implement locale completeness/terminology/name/number/date/
        claim-evidence parity/layout/media-text/cultural-review checks.
        Evidence: localization tests.
  - [ ] V4.13.f Register applicability/input/output/remediation/owner/version/
        failure state for every gate and prove each retained target is covered.
        Evidence: gate registry report.
- [ ] V4.14 Implement attestation validity, credential, conflict, disagreement,
      expiry, and independence rules. Evidence: canonical tests.
  - [ ] V4.14.a Resolve exact attestation scope and reject mismatched or stale
        claim/source/evidence-pack/draft revisions. Evidence: scope tests.
  - [ ] V4.14.b Verify attestor identity/credential/issuer/domain/jurisdiction/
        validity/revocation with pinned evidence and verification method.
        Evidence: credential tests.
  - [ ] V4.14.c Evaluate declared/detected conflicts, prior involvement,
        organizational relationships, compensation, recusal, and required
        independence. Evidence: conflict tests.
  - [ ] V4.14.d Aggregate agree/disagree/qualified/abstain/unable responses
        under quorum/specialist/independence rules without hiding disagreement.
        Evidence: decision tests.
  - [ ] V4.14.e Enforce expiry and invalidation on scope/evidence/credential/
        policy/conflict changes; prohibit automation impersonation. Evidence:
        lifecycle tests.
  - [ ] V4.14.f Verify canonical valid/invalid/conflicted/disagreeing/expired/
        revoked/insufficient fixtures. Evidence: invariant suite.
- [ ] V4.15 Implement evidence-pack completeness and integrity validation.
      Evidence: missing/member/hash mismatch fixtures.
  - [ ] V4.15.a Resolve exact pack version and required members by claim class/
        severity/desk/target/policy. Evidence: completeness rules.
  - [ ] V4.15.b Validate every member identity/version/tenant/story/compartment/
        permission/reference and required citation locator. Evidence:
        referential tests.
  - [ ] V4.15.c Recompute canonical serialization/member/root hashes and
        signatures, detecting reorder/replace/duplicate/missing/tampered data.
        Evidence: integrity tests.
  - [ ] V4.15.d Check source/claim/attestation/review freshness, retraction/
        correction state, and applicable quality/specialist floors. Evidence:
        semantic validation.
  - [ ] V4.15.e Return separate missing/unauthorized/stale/unreachable/hash-
        mismatch/unsupported-version results and block unverifiable packs.
        Evidence: failure-state tests.
  - [ ] V4.15.f Run missing/member/hash/cross-tenant/protected/expired/reordered
        fixture corpus. Evidence: canonical suite.
- [ ] V4.16 Persist raw measurements and evidence separately from policy
      verdicts. Evidence: policy recomputation.
  - [ ] V4.16.a Define immutable measurement record with subject/input
        revisions, metric values/units, evidence locators, uncertainty,
        evaluator/model/ corpus version, and time. Evidence: measurement schema.
  - [ ] V4.16.b Define separate verdict record with measurement references,
        applicable policy/rules/thresholds, outcome/reason/remediation, and
        time. Evidence: verdict schema.
  - [ ] V4.16.c Prevent verdict fields from overwriting/denormalizing raw
        measurements and prevent missing/error measurements from defaulting to
        pass. Evidence: storage invariants.
  - [ ] V4.16.d Recompute new verdicts from retained measurements after policy/
        threshold changes without rerunning evaluators when inputs remain valid.
        Evidence: recomputation tests.
  - [ ] V4.16.e Rerun measurements only on input/evaluator invalidation and
        retain prior measurement/verdict lineage for audit. Evidence: lifecycle
        tests.
  - [ ] V4.16.f Compare old/new policy decisions and explain deltas to reviewers
        without rewriting historical approvals/releases. Evidence: policy change
        report.
- [ ] V4.17 Version every evaluator, model, domain-weight table, threshold, and
      corpus. Evidence: result provenance.
  - [ ] V4.17.a Define immutable identities/digests for rules/code/config,
        provider/model/prompt/tool, weight tables, thresholds/policies, and
        corpora/splits/labels. Evidence: version contracts.
  - [ ] V4.17.b Resolve and pin every dependency before evaluation; prohibit
        mutable aliases as sole provenance. Evidence: dispatch tests.
  - [ ] V4.17.c Persist complete dependency graph plus
        runtime/build/environment/ locale/randomness/input/output hashes on
        every result. Evidence: provenance tests.
  - [ ] V4.17.d Define approval/effective/retired/blocked states, compatibility,
        rollback, retention, and artifact availability for versions. Evidence:
        lifecycle policy.
  - [ ] V4.17.e Detect missing/unknown/revoked/unavailable versions and make
        results unverifiable/stale rather than current. Evidence: failure tests.
  - [ ] V4.17.f Reproduce representative results from provenance and report any
        nondeterminism/substitution explicitly. Evidence: reproducibility gate.
- [ ] V4.18 Make unavailable, insufficient-evidence, contested, stale, and
      evaluator-failure states explicit. Evidence: none renders as pass.
  - [ ] V4.18.a Define canonical distinct measurement/execution/verdict states,
        reason codes, timestamps, owners, safe messages, and retry/remediation.
        Evidence: state taxonomy.
  - [ ] V4.18.b Map dependency outage/not-configured/unauthorized/timeout/rate/
        malformed/unknown version to unavailable or evaluator-failure. Evidence:
        execution tests.
  - [ ] V4.18.c Map missing/weak/unreachable/conflicting evidence and
        disagreement/ambiguity to insufficient or contested with uncertainty.
        Evidence: evidence-state tests.
  - [ ] V4.18.d Mark stale with exact invalidating subject/dependency/policy/
        evaluator version and prohibit silent use after change. Evidence:
        freshness tests.
  - [ ] V4.18.e Define gate/review/publish behavior per state and require human
        authority for any bounded exception with scope/expiry. Evidence: policy
        tests.
  - [ ] V4.18.f Verify API/UI/export/notifications/analytics never serialize or
        display any nonpass state as pass/healthy/current. Evidence: projection
        suite.
- [ ] V4.19 Implement Ghana NMC complaint/content checks and remedy workflow
      with current rule/category, evidence, response deadline, transition,
      decision, correction/apology/right-of-reply, escalation, and audit.
      Evidence: legal/regulatory decision remains human and versioned.
      <!-- SPEC 2026-09-11: extend
      `apps/veritas/api/src/domain/compliance/nmc.ts` and the `nmc_complaints`
      table rather than starting a second complaint model; .a's jurisdiction
      profile is pinned with a version and an open-questions list, .f is human
      (§2.5.9). -->
  - [ ] V4.19.a Pin current NMC/jurisdiction profile, rule/category identifiers,
        effective dates, authoritative references, counsel owner, and review
        cadence. Evidence: regulatory profile.
  - [ ] V4.19.b Define content-check applicability/inputs/findings/evidence/
        uncertainty/remediation for ratified editorial categories without
        encoding legal conclusion as model output. Evidence: check registry.
  - [ ] V4.19.c Intake complaint with complainant/privacy/consent,
        story/release/ claim/target, allegation/category, evidence, received
        channel/time, and authenticity. Evidence: complaint schema.
  - [ ] V4.19.d Calculate statutory/policy response deadline/calendar/time-zone,
        assign accountable owner/specialists, acknowledge safely, and escalate
        approaching breach. Evidence: SLA tests.
  - [ ] V4.19.e Implement
        triage/investigate/response-draft/review/decide/remedy/ appeal/close
        transitions with roles, evidence, concurrency, and immutable audit.
        Evidence: workflow tests.
  - [ ] V4.19.f Require named human legal/editorial decision with exact rule/
        evidence/rationale/uncertainty/conditions and prohibit automated
        disposition. Evidence: authority tests.
  - [ ] V4.19.g Execute approved correction/apology/right-of-reply/takedown/
        retraction/escalation through canonical release/cascade workflows and
        verify downstream notice. Evidence: remedy journey.
  - [ ] V4.19.h Preserve protected/legal notes and complainant/source identity
        through redacted notifications/exports; retain/reopen/appeal per policy.
        Evidence: privacy/audit tests.
- [ ] V4.20 Implement election-coverage gates for phase, equal opportunity,
      silence periods, results/source verification, polling methodology,
      advertising/sponsorship, incitement, misinformation, candidate treatment,
      and current jurisdictional rules. Evidence: election and phase fixtures
      plus specialist signoff.
  - [ ] V4.20.a Pin election/jurisdiction/contest/phase/calendar/time-zone/rule
        profile, authoritative sources, effective dates, and specialist owner.
        Evidence: election profile.
  - [ ] V4.20.b Determine phase/silence/emergency/result-certification state
        from trusted inputs and handle uncertain/changed/court-ordered
        calendars. Evidence: phase tests.
  - [ ] V4.20.c Check candidate/party treatment, access/equal-opportunity/
        response records, coverage context, and documented editorial basis
        without imposing false numerical equivalence. Evidence: treatment gate.
  - [ ] V4.20.d Check polling/survey source, sponsor, field dates, population/
        sample/method/weighting/question/order/margin/uncertainty/disclosure.
        Evidence: polling gate.
  - [ ] V4.20.e Check result
        source/status/geography/contest/precinct/denominator/ timestamp and
        prohibit unverified projections or certification claims. Evidence:
        result gate.
  - [ ] V4.20.f Check advertising/sponsorship identity/disclosure/separation and
        silence-period/channel/territory scheduling constraints. Evidence:
        campaign-content gate.
  - [ ] V4.20.g Check incitement/violence/doxxing/manipulated media/
        misinformation risks with uncertainty, escalation, and harm-minimizing
        distribution treatment. Evidence: safety gate.
  - [ ] V4.20.h Persist raw findings separately from human legal/editorial
        verdict, require specialist signoff, invalidate on phase/rule/content/
        source changes, and test edge fixtures. Evidence: signed phase suite.
- [ ] V4.21 Implement data-protection consent, lawful basis, purpose, subject
      request, redaction, retention, and disclosure checks across sources, tips,
      media, community, analytics, subscriptions, and B2B products. Evidence:
      end-to-end DPA request and legal-hold conflict tests.
  - [ ] V4.21.a Build processing inventory per named domain with data subjects/
        fields/sensitivity/source/controller-processor/purpose/basis/recipient/
        region/retention. Evidence: processing register.
  - [ ] V4.21.b Validate consent where used for specificity, informed choice,
        granularity, evidence, withdrawal, age/guardian, and no bundled dark
        patterns; validate alternate lawful bases separately. Evidence: basis
        tests.
  - [ ] V4.21.c Enforce purpose limitation/minimization/access/field projection/
        model-use/analytics cohort/export/disclosure rules at collection and
        reuse boundaries. Evidence: policy tests.
  - [ ] V4.21.d Intake authenticated access/correct/delete/restrict/object/
        portability requests with scope, jurisdiction, deadlines, identity
        minimization, and protected third-party review. Evidence: request
        schema.
  - [ ] V4.21.e Discover subject data across ordinary/protected stores, blobs,
        search, logs, analytics, backups, channels, payment, B2B, and processors
        without creating a new leakage index. Evidence: discovery tests.
  - [ ] V4.21.f Apply authorized
        redaction/correction/deletion/restriction/export and propagate to
        processors/consumers while preserving public-interest journalism and
        correction evidence as legally justified. Evidence: remedy tests.
  - [ ] V4.21.g Resolve retention/legal-hold/editorial-record/source-safety/
        third-party-right conflicts through named human legal decision, partial
        action, rationale, and appeal. Evidence: conflict tests.
  - [ ] V4.21.h Verify response/read-back/deadline/audit and no
        protected-source/ other-subject disclosure in an end-to-end DPA journey.
        Evidence: DPA E2E.
- [ ] V4.22 Implement trauma, graphic-content, child, vulnerable-person,
      location, doxxing, suicide/self-harm, and public-safety review with
      warnings, minimization, specialist escalation, and distribution-specific
      treatment. Evidence: harm-reduction policy and redacted-preview suite.
  - [ ] V4.22.a Define versioned harm categories/severity/context/uncertainty/
        jurisdiction/age/vulnerability/public-interest factors and trained
        reviewer roles. Evidence: harm taxonomy.
  - [ ] V4.22.b Identify exact text/quote/media/audio/video/data/location/
        metadata risks with evidence and protected reviewer annotations.
        Evidence: finding contract.
  - [ ] V4.22.c Apply identity/location/contact/image/voice/metadata
        minimization, pseudonymization/redaction, delayed publication, and safe
        source linking. Evidence: minimization tests.
  - [ ] V4.22.d Define consent/guardian/public-interest/safety exception and
        independent specialist/legal/source-protection review floors. Evidence:
        authority matrix.
  - [ ] V4.22.e Define content warnings/help context, thumbnail/autoplay/
        notification/search/social/channel/locale treatment, access/age gates,
        and no gratuitous repetition. Evidence: distribution matrix.
  - [ ] V4.22.f Define suicide/self-harm language/method/location/detail/
        imagery/link treatment with current specialist policy and escalation.
        Evidence: self-harm gate.
  - [ ] V4.22.g Define imminent public-safety escalation and emergency authority
        without exposing victims/sources or creating a generic publish bypass.
        Evidence: emergency tests.
  - [ ] V4.22.h Preview redacted versus authorized views for every target and
        test copy/download/cache/search/log/AI context/correction propagation.
        Evidence: redacted-preview suite.
  - [ ] V4.22.i Persist human decisions/rationale/conditions/expiry and
        invalidate on content/media/target/policy changes. Evidence:
        harm-reduction approval.

### V5 — Review, legal, standards, and protected collaboration

- [ ] V5.1 Build review templates by desk, claim risk, source sensitivity,
      media, audience, locale, and distribution target. Evidence: role/quorum
      matrix.
  - [ ] V5.1.a Define versioned template identity/applicability/priority and
        required checklist/gate/evidence/reviewer/decision fields. Evidence:
        template schema.
  - [ ] V5.1.b Model desk/claim class-risk/source sensitivity/media/audience/
        locale/territory/target dimensions and deterministic template
        composition. Evidence: applicability tests.
  - [ ] V5.1.c Define reviewer roles/credentials/domains/independence/recusal/
        quorum/order/parallelism/deadlines and delegates. Evidence: role/quorum
        matrix.
  - [ ] V5.1.d Define tighten-only local customization versus code/policy-owned
        floors, governance, migration, approval, effective date, and expiry.
        Evidence: governance tests.
  - [ ] V5.1.e Preview exact resolved review plan on a pinned candidate and fail
        missing/ambiguous/conflicting/unversioned templates. Evidence:
        resolution tests.
- [ ] V5.2 Build anchored comments and suggestions on blocks, ranges, claims,
      citations, sources, headlines, media, and disclosures. Evidence: anchor
      survival.
  - [ ] V5.2.a Define comment/thread/suggestion identity, subject kind/version/
        anchor, author, visibility, state, mentions, and audit. Evidence:
        collaboration schema.
  - [ ] V5.2.b Support each named subject with kind-specific semantic anchor and
        protected-source/legal-note visibility rules. Evidence: anchor registry.
  - [ ] V5.2.c Implement reply/mention/resolve/reopen/edit-with-history/delete-
        tombstone and accept/reject/edit suggestion transitions. Evidence:
        workflow tests.
  - [ ] V5.2.d Transform anchors through revisions/concurrent edits or mark
        orphaned/ambiguous with candidates and no silent retarget. Evidence:
        anchor survival corpus.
  - [ ] V5.2.e Send redacted permission-filtered notifications/deep links and
        restore focus/subject context. Evidence: notification tests.
  - [ ] V5.2.f Test cross-role/tenant/compartment visibility, export, audit,
        archive/legal hold, and responsive keyboard/screen-reader interaction.
        Evidence: collaboration E2E.
- [ ] V5.3 Build fact-check assignments and signoff. Evidence: claim-level
      coverage and separation of duties.
  - [ ] V5.3.a Resolve in-scope claims by class/severity/status and create
        assignments pinned to exact claim/draft/source/evidence revisions.
        Evidence: assignment contract.
  - [ ] V5.3.b Assign qualified independent fact-checker, due date/priority/
        checklist, conflict/recusal, backup, and escalation. Evidence: role
        tests.
  - [ ] V5.3.c Review claim text/anchor/citations/locators/source quality/
        counter-sources/contradictions/uncertainty and request evidence or
        edits. Evidence: review journey.
  - [ ] V5.3.d Record claim-level verified/unsupported/contested/needs-change/
        unable decision with evidence/rationale/conditions/signature. Evidence:
        decision schema.
  - [ ] V5.3.e Compute complete coverage and block signoff for missing/stale/
        orphaned/high-risk claims or self-review. Evidence: coverage tests.
  - [ ] V5.3.f Invalidate signoff on relevant revision/source/policy changes and
        preserve prior immutable decisions. Evidence: freshness/SoD tests.
- [ ] V5.4 Build standards review for ethics, balance, harm, conflicts, and
      disclosure. Evidence: checklist and decision record.
  - [ ] V5.4.a Resolve versioned checklist from desk/risk/audience/target and
        pin exact draft/claims/media/disclosures/policies. Evidence: review
        plan.
  - [ ] V5.4.b Review ethics/public interest/method/source treatment,
        counterclaims/false balance, conflicts/recusals, and AI/commercial
        independence. Evidence: ethics checklist.
  - [ ] V5.4.c Review harm/minimization/warnings/children/vulnerable persons/
        location/privacy and distribution-specific treatment. Evidence: harm
        checklist.
  - [ ] V5.4.d Review required methodology/AI/correction/conflict/sponsorship/
        limitation disclosures for truth, placement, and target parity.
        Evidence: disclosure checklist.
  - [ ] V5.4.e Record approve/reject/request-changes/conditional/abstain with
        anchored findings, evidence, rationale, conditions, and dissent.
        Evidence: decision record.
  - [ ] V5.4.f Enforce qualification/recusal/quorum, stale invalidation, and no
        automated standards approval. Evidence: authorization tests.
- [ ] V5.5 Build legal review with compartmented notes and retention. Evidence:
      field-level authorization and redacted notifications.
  - [ ] V5.5.a Define legal
        matter/review/note/attachment/privilege/jurisdiction/ hold/decision
        schema pinned to exact candidate/evidence. Evidence: legal contract.
  - [ ] V5.5.b Store notes/attachments/communications in a separately authorized
        encrypted compartment with restricted search/export/provider access.
        Evidence: storage tests.
  - [ ] V5.5.c Build request/accept/investigate/request-information/advise/
        decide/escalate/close workflow with qualified counsel and conflicts.
        Evidence: workflow tests.
  - [ ] V5.5.d Expose only safe legal-review status/blocker/required action to
        ordinary users; redact content in UI/API/events/logs/notifications/
        audit exports. Evidence: projection tests.
  - [ ] V5.5.e Apply retention/legal hold/access review/revocation/export/
        destruction with immutable decision audit. Evidence: lifecycle tests.
  - [ ] V5.5.f Invalidate decision on scoped
        content/evidence/jurisdiction/policy changes and test
        cross-role/tenant/compartment access. Evidence: security tests.
- [ ] V5.6 Build source-protection review without exposing identity to
      unauthorized reviewers. Evidence: role projections.
  - [ ] V5.6.a Resolve source-sensitivity/protection checklist and exact
        ordinary/protected records under review. Evidence: review contract.
  - [ ] V5.6.b Present pseudonymized source role/relationship/risk/consent/
        evidence needs to ordinary reviewers with no inferable protected data.
        Evidence: redacted projection.
  - [ ] V5.6.c Present identity/contact/communications/location/safety/grants
        only to source-protection officers after step-up and purpose check.
        Evidence: protected projection.
  - [ ] V5.6.d Record minimize/redact/restrict/delay/approve/request-changes/
        emergency/escalate decisions with separate protected and redacted audit.
        Evidence: decision tests.
  - [ ] V5.6.e Apply approved display/distribution/search/notification/export/
        model-use controls and invalidate on source/content/target changes.
        Evidence: enforcement tests.
  - [ ] V5.6.f Test unauthorized UI/API/network/log/cache/count/timing/
        notification/audit access and role revocation mid-review. Evidence:
        non-leakage suite.
- [ ] V5.7 Build attestor invitation, verification, response, disagreement, and
      expiry workflows. Evidence: end-to-end attestation journey.
  - [ ] V5.7.a Create invitation for exact scope/question/domain/credential/
        independence requirements, expiry, locale, and safe evidence package.
        Evidence: invitation contract.
  - [ ] V5.7.b Deliver a signed single-use/nonenumerable invitation with
        identity proofing, consent, authentication, and protected-source
        redaction. Evidence: invitation security tests.
  - [ ] V5.7.c Verify identity/credential/issuer/domain/validity/revocation and
        collect conflict/independence declarations. Evidence: eligibility tests.
  - [ ] V5.7.d Capture agree/disagree/qualified/abstain/unable response with
        rationale/evidence/uncertainty/signature and no broader approval power.
        Evidence: response tests.
  - [ ] V5.7.e Surface disagreement without suppression, apply quorum/specialist
        rules, and route clarification/replacement/escalation. Evidence:
        disagreement tests.
  - [ ] V5.7.f Expire/revoke/invalidate on time/credential/scope/input/conflict
        change and notify affected review. Evidence: lifecycle tests.
  - [ ] V5.7.g Run invite→verify→respond→review→expire/reinvite across success,
        decline, bad credential, conflict, and stale scope. Evidence: E2E.
- [ ] V5.8 Build conflict-of-interest declaration and recusal. Evidence:
      incompatible conflict blocks approval.
  - [ ] V5.8.a Define versioned conflict categories, subject/scope/severity,
        private versus reviewer-visible details, evidence, update/expiry, and
        false-declaration policy. Evidence: declaration schema.
  - [ ] V5.8.b Prompt declaration at assignment/review/attestation/publish/
        correction and when relevant relationships change. Evidence: trigger
        tests.
  - [ ] V5.8.c Let actors declare/no-conflict/update with protected details and
        route uncertain/material conflicts to authorized standards review.
        Evidence: declaration journey.
  - [ ] V5.8.d Compute recusal/incompatibility effects on access, assignment,
        contribution, quorum, decision, reassignment, and notification.
        Evidence: recusal rules.
  - [ ] V5.8.e Block approval/signoff/attestation where conflict or missing
        declaration violates independence and prevent self-clearance. Evidence:
        separation tests.
  - [ ] V5.8.f Preserve immutable decisions and redacted audit; invalidate
        affected approvals when declarations change. Evidence: lifecycle tests.
- [ ] V5.9 Build copy-edit and accessibility review. Evidence: signed exact
      revision and locale.
  - [ ] V5.9.a Resolve copy/style/locale and accessibility checklist/templates
        for exact draft/media/target revision. Evidence: review plan.
  - [ ] V5.9.b Review
        grammar/style/names/numbers/dates/links/headlines/captions/ consistency
        without silently changing claims/quotes/meaning. Evidence: copy-edit
        tests.
  - [ ] V5.9.c Review headings/lists/tables/link purpose/alt text/transcripts/
        captions/descriptions/reading order/language and semantic alternatives.
        Evidence: accessibility tests.
  - [ ] V5.9.d Submit anchored suggestions, accept changes as new revision, and
        re-run claim/evidence/accuracy gates for material edits. Evidence:
        revision tests.
  - [ ] V5.9.e Record separate qualified copy and accessibility decisions with
        exact revision/locale/target, findings, evidence, rationale, and
        conditions. Evidence: signed reviews.
  - [ ] V5.9.f Invalidate on relevant content/media/locale/template/policy
        change and prohibit inherited signoff across locales. Evidence:
        freshness tests.
- [ ] V5.10 Build publisher signoff with fresh evidence recomputation. Evidence:
      stale dependency race test.
  - [ ] V5.10.a Create immutable release-review candidate pinning draft/claims/
        sources/evidence/media/disclosures/locales/targets/policies/gates/reviews.
        Evidence: candidate manifest.
  - [ ] V5.10.b Recompute applicable raw measurements and verdicts against
        pinned inputs, resolving unavailable/contested/stale/failure explicitly.
        Evidence: fresh gate run.
  - [ ] V5.10.c Verify required role/quorum/independence/legal/standards/fact-
        check/accessibility/rights approvals are current and candidate-scoped.
        Evidence: approval check.
  - [ ] V5.10.d Present target eligibility, embargo/schedule, corrections/
        conflicts, residual risks, exceptions, and read-back plan to publisher.
        Evidence: signoff UI.
  - [ ] V5.10.e Record named approve/reject/request-changes with reason,
        conditions, exact manifest hash, step-up, and immutable signature.
        Evidence: signoff record.
  - [ ] V5.10.f Atomically recheck manifest/dependency versions at signoff and
        publish dispatch; fail injected last-moment changes. Evidence: stale-
        dependency race test.
- [ ] V5.11 Build emergency publication and correction policy with higher
      authority and post-event review. Evidence: no generic gate bypass.
  - [ ] V5.11.a Define narrowly enumerated emergency types,
        activation/deactivation authority, jurisdiction/target scope, duration,
        and required evidence. Evidence: emergency policy.
  - [ ] V5.11.b Define nonwaivable protected-source/legal/safety/authenticity/
        human authority floors and specifically deferrable checks. Evidence:
        gate matrix.
  - [ ] V5.11.c Require step-up and higher/dual authority, exact impact preview,
        reason, conditions, expiry, and immutable incident linkage. Evidence:
        authorization tests.
  - [ ] V5.11.d Publish/correct through the same canonical adapters with
        explicit emergency label, target state, monitoring, read-back, and
        rollback/ recall. Evidence: emergency journey.
  - [ ] V5.11.e Queue deferred reviews/evidence/disclosures/corrections with
        owners/deadlines and block ordinary closure until complete. Evidence:
        follow-up tests.
  - [ ] V5.11.f Run mandatory independent post-event review, revoke emergency
        mode, remediate controls, and preserve full audit. Evidence: game-day.
- [ ] V5.12 Build review SLA, escalation, after-hours, and notification routing.
      Evidence: embargo/source data is redacted.
  - [ ] V5.12.a Define SLA by review type/risk/desk/priority/phase/target with
        business calendars/time zones, pause conditions, and measurement events.
        Evidence: SLA contract.
  - [ ] V5.12.b Calculate due/breach state deterministically from immutable
        events and current assignments; handle reassign/reopen/change priority.
        Evidence: timing tests.
  - [ ] V5.12.c Define primary/backup/on-call/escalation roles, schedules,
        acknowledgment, handoff, higher authority, and stop-the-line actions.
        Evidence: routing policy.
  - [ ] V5.12.d Send channel-appropriate reminders/escalations with dedupe,
        quiet-hours/emergency rules, delivery/acknowledgment, and retry.
        Evidence: notification tests.
  - [ ] V5.12.e Redact story/embargo/source/legal/complainant detail based on
        recipient/channel/device while retaining safe deep link after auth.
        Evidence: redaction tests.
  - [ ] V5.12.f Report SLA with privacy floors and no reviewer productivity or
        editorial-quality proxy; test after-hours/time-zone/outage cases.
        Evidence: operations report.
- [ ] V5.13 Build immutable approval and waiver records. Evidence: automation
      cannot impersonate a human approver.
  - [ ] V5.13.a Define approval/waiver identity/type/scope/subject manifest/
        decision/conditions/reason/evidence/actor-role/time/signature/version.
        Evidence: record schema.
  - [ ] V5.13.b Require current authenticated human identity, step-up where
        needed, qualified role, independence/recusal/quorum, and explicit
        intent. Evidence: authority tests.
  - [ ] V5.13.c Restrict waiver to policy-allowed gate/risk/target scope with
        compensating controls, owner, expiry, review, and nonwaivable floors.
        Evidence: waiver policy tests.
  - [ ] V5.13.d Store append-only/tamper-evident records with supersession/
        revocation but no mutation/deletion of prior decisions. Evidence:
        integrity tests.
  - [ ] V5.13.e Invalidate effective approval/waiver on manifest/input/policy/
        role/conflict/condition/expiry change and expose stale state. Evidence:
        lifecycle tests.
  - [ ] V5.13.f Attempt service account/model/agent/header/audit-field
        impersonation, replay, forged signature, and direct DB/API creation.
        Evidence: non-impersonation suite.
- [ ] V5.14 Build newsroom audit export with protected-source redaction.
      Evidence: authorization and tamper hash.
  - [ ] V5.14.a Define export purpose/scope/time/story/actor/event/data-class/
        format/version request and authorized requester/approver policy.
        Evidence: export contract.
  - [ ] V5.14.b Select immutable ordered audit events and related decision/
        revision/job/release references with stable cursor and completeness
        manifest. Evidence: selection tests.
  - [ ] V5.14.c Apply field/event/object redaction or pseudonymization for
        protected source, legal, tipster, child/vulnerable, security, and third-
        party data. Evidence: redaction matrix.
  - [ ] V5.14.d Generate readable and machine-verifiable output with schema,
        reason codes, gaps, time zone, chain/manifest hash, signature, and
        encryption. Evidence: export fixture.
  - [ ] V5.14.e Deliver through expiring scoped secure download with step-up,
        watermark if appropriate, rate/size limits, and export audit. Evidence:
        delivery tests.
  - [ ] V5.14.f Verify authorization, two-tenant/compartment nonleakage,
        completeness, tamper detection, replay consistency, and legal-hold
        behavior. Evidence: audit export suite.

### V6 — Persistence, APIs, search, and distribution integration

- [ ] V6.1 Decide authoritative stores for stories, drafts, sources, claims,
      evidence, attestations, reviews, schedules, releases, and cascades.
      Evidence: ownership ADR.
      <!-- SPEC 2026-09-11: the decision is among the three persistence stories
      in the Phase V header note (2). The in-memory CMS cannot be authoritative
      for anything; the raw-SQL API and the Prisma schema disagree on at least
      `user_subscriptions`. Draft
      `docs/adr/ADR-V6.1-veritas-persistence-ownership.md` (Proposed, like
      ADR-M0.5), build .b–.e's tests against the option it proposes, and leave
      the parent open for the named approver. -->
  - [x] V6.1.a Inventory current
        tables/documents/blobs/indexes/caches/queues/CMS records and
        writers/readers for each named aggregate. Evidence: storage inventory.
        <!-- veritas-storage-inventory.json: storage-layer inventory of Veritas. Of
                                                                                                                                                                                                                                                                        the 7 named layers, 6 are PRESENT — tables (67 relational Prisma aggregates
                                                                                                                                                                                                                                                                        over 4 PrismaClients), blobs (MediaAsset object storage / storageBucket),
                                                                                                                                                                                                                                                                        indexes (ArticleEmbedding vector index), caches (Redis), queues (transactional
                                                                                                                                                                                                                                                                        outbox PushNotificationOutbox/PushNotificationDelivery + BullMQ), CMS — and 1
                                                                                                                                                                                                                                                                        is ABSENT: documents (no MongoDB/document store; Veritas is relational-only).
                                                                                                                                                                                                                                                                        FINDING: Veritas is a RICH MULTI-STORE server platform, contrasting Euterpe's
                                                                                                                                                                                                                                                                        desktop-file model (E7.1.a) — same storage-layer method, opposite architecture.
                                                                                                                                                                                                                                                                        0 repository classes (service-direct, like Isis). Used PRECISE per-layer
                                                                                                                                                                                                                                                                        patterns (new Redis, Outbox, ArticleEmbedding, MediaAsset) scoped to the
                                                                                                                                                                                                                                                                        Veritas tree. generate-veritas-storage-inventory.mjs + 6 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. V6.1.b (system/record boundary) + later V6.1.* are
                                                                                                                                                                                                                                                                        design and stay unchecked. -->
  - [ ] V6.1.b Define authoritative system/record boundary, aggregate/
        transaction ownership, immutable versus mutable data, and durability
        class per entity. Evidence: ownership matrix.
  - [ ] V6.1.c Define derived search/read-model/cache/CMS/distribution
        projections, freshness/rebuild/reconciliation, and forbidden reverse
        writes. Evidence: projection contracts.
  - [ ] V6.1.d Define protected-compartment, encryption/key/region, retention/
        hold/backup, and access-audit storage constraints. Evidence: data
        placement review.
  - [ ] V6.1.e Resolve split/duplicate ownership, failure boundaries, outbox/
        saga needs, migration, recovery, and accountable service/team. Evidence:
        approved ownership ADR.
- [ ] V6.2 Add missing schemas with explicit queryable columns and safe
      migrations. Evidence: production-size rehearsal.
  - [ ] V6.2.a Derive missing tables/columns/types/constraints/indexes/
        partitions/foreign keys from canonical contracts and required queries.
        Evidence: schema diff.
  - [ ] V6.2.b Keep identity/state/tenant/desk/story/compartment/version/time/
        relationship fields explicit and queryable; avoid opaque core JSON.
        Evidence: schema review.
  - [ ] V6.2.c Design expand/backfill/dual-read-write/switch/contract migrations
        with chunking, checkpoints, idempotency, throttling, and rollback/
        restore boundaries. Evidence: migration plan.
  - [ ] V6.2.d Preserve immutable revisions/hashes/audit/protected encryption
        and validate references/state mappings during backfill. Evidence:
        migration tests.
  - [ ] V6.2.e Rehearse production-volume/cardinality/skew/concurrency on real
        engine, record locks/runtime/storage/query plans, and meet budgets.
        Evidence: rehearsal report.
  - [ ] V6.2.f Deploy with observability/reconciliation/stop conditions and
        remove old schema only after all consumers prove compatibility.
        Evidence: rollout gate.
- [ ] V6.3 Implement immutable revision and evidence stores with
      durable-before-ack behavior. Evidence: real-database tests.
  - [ ] V6.3.a Implement append-only revision/evidence/member/blob-reference/
        hash/signature schemas with unique identity and parent/version
        constraints. Evidence: persistence tests.
  - [ ] V6.3.b Write record, required references, blob completion, and outbox
        atomically or through a recoverable protocol before acknowledging.
        Evidence: durability tests.
  - [ ] V6.3.c Enforce optimistic concurrency/idempotency/deduplication and
        immutable historical content under retry/race/replay. Evidence:
        concurrency tests.
  - [ ] V6.3.d Verify canonical hashes/signatures on read and detect missing/
        changed/mismatched blobs or members without returning approved state.
        Evidence: integrity tests.
  - [ ] V6.3.e Inject database/blob/network/process failure at each boundary,
        restart/reconcile, and prove acknowledged writes survive. Evidence:
        fault suite.
  - [ ] V6.3.f Run backup/restore/read-back against real database/blob services
        with tenant/compartment isolation. Evidence: durable round-trip.
- [ ] V6.4 Implement pitch/assignment/story/draft APIs with optimistic
      concurrency and property-level authorization. Evidence: contract suite.
  - [ ] V6.4.a Define versioned CRUD/query/transition/command contracts, stable
        IDs, errors, pagination/filter/sort, idempotency, and deprecation.
        Evidence: OpenAPI schemas.
  - [ ] V6.4.b Implement request identity/tenant/desk/story/role/state/
        capability authorization for every object and function. Evidence: route
        auth tests.
  - [ ] V6.4.c Apply field-level read/write/patch/filter/sort/export projection,
        including embargo/protected/legal/commercial constraints. Evidence:
        property auth tests.
  - [ ] V6.4.d Require expected version/ETag for mutation and return structured
        conflict/current revision without silent overwrite. Evidence:
        concurrency tests.
  - [ ] V6.4.e Implement idempotent create/commands/transitions with
        transactional audit/outbox and safe retry. Evidence: idempotency tests.
  - [ ] V6.4.f Test positive/negative/cross-tenant/stale-role/direct-object/
        mass-assignment/unknown-field/oversized requests. Evidence: contract
        suite.
- [ ] V6.5 Implement compartmented source APIs and access-grant audit. Evidence:
      protected identity negative tests.
  - [ ] V6.5.a Separate ordinary source endpoints/contracts from protected
        identity/contact/communications/safety/grant endpoints and credentials.
        Evidence: API boundary.
  - [ ] V6.5.b Implement pseudonymous safe projection and opaque linkage without
        protected field/count/timing/existence leaks. Evidence: projection
        tests.
  - [ ] V6.5.c Require current nominative purpose/time/story-scoped grant,
        step-up/device/session policy, and field/function authorization on every
        protected call. Evidence: access tests.
  - [ ] V6.5.d Implement request/approve/deny/revoke/expire/emergency grant
        workflow with separation, reason, alerts, and post-review. Evidence:
        grant tests.
  - [ ] V6.5.e Emit tamper-evident redacted attempt/access/export audit without
        copying secrets into ordinary systems. Evidence: audit tests.
  - [ ] V6.5.f Test enumeration, BOLA, mass assignment, filter/sort/error/cache/
        log leakage, token replay, revoked/mid-session grants, and two tenants.
        Evidence: protected identity negative suite.
- [ ] V6.6 Implement claim/citation/evidence/attestation APIs. Evidence:
      canonical invariant route tests.
  - [ ] V6.6.a Define versioned resource/command/query/batch contracts and
        explicit claim/citation/evidence-pack/attestation state transitions.
        Evidence: API schemas.
  - [ ] V6.6.b Validate exact story/draft/revision/anchor/source/locator/member/
        scope references and canonical class/state/value vocabularies. Evidence:
        invariant tests.
  - [ ] V6.6.c Enforce tenant/story/desk/role/field/function/compartment/
        independence authorization and protected redacted projections. Evidence:
        authorization tests.
  - [ ] V6.6.d Implement optimistic concurrency/idempotency/transactional audit/
        outbox and immutable revision or decision records. Evidence: mutation
        tests.
  - [ ] V6.6.e Invalidate dependent gates/reviews/releases on relevant mutation
        and publish versioned events with no protected payload. Evidence:
        dependency tests.
  - [ ] V6.6.f Run valid/invalid/stale/duplicate/orphan/hash/scope/cross-tenant/
        mass-assignment route fixtures. Evidence: canonical contract suite.
- [ ] V6.7 Implement verification/gate jobs with current policy versions.
      Evidence: stale input and rerun tests.
  - [ ] V6.7.a Define job request/run/attempt/result contracts pinning subject/
        inputs/evaluator/model/corpus/weight/policy versions and requested
        gates. Evidence: job schema.
  - [ ] V6.7.b Resolve applicability/current approved versions at dispatch and
        reject mutable aliases, missing configuration, or unauthorized inputs.
        Evidence: dispatch tests.
  - [ ] V6.7.c Implement durable queue/lease/heartbeat/timeout/retry/backoff/
        cancellation/idempotency/resource/budget behavior per gate. Evidence:
        worker tests.
  - [ ] V6.7.d Persist raw measurements/evidence before separate verdicts,
        terminal state, audit, and dependency invalidation events. Evidence:
        result tests.
  - [ ] V6.7.e Detect subject/input/policy version changes before execution and
        commit; mark stale/superseded and require explicit rerun. Evidence:
        stale input tests.
  - [ ] V6.7.f Inject provider/evaluator/queue/database/blob failure, cancel,
        replay, duplicate, and out-of-order events. Evidence: fault/rerun suite.
- [ ] V6.8 Implement protected, permission-aware newsroom search. Evidence:
      titles, counts, facets, and snippets do not leak.
  - [ ] V6.8.a Define per-document/field classification, tenant/desk/story/
        compartment/role ACL, reveal/embargo/public state, locale, freshness,
        and deletion schema. Evidence: search security model.
  - [ ] V6.8.b Index only allowlisted safe projections from authoritative events
        with content/version hash, current ACL, and protected fields excluded.
        Evidence: indexing tests.
  - [ ] V6.8.c Apply authorization before candidate retrieval/aggregation/
        ranking/highlighting and stable cursor pagination. Evidence: query
        tests.
  - [ ] V6.8.d Prevent query suggestion/autocomplete/spelling/semantic vector/
        title/snippet/count/facet/timing/cache/log leaks. Evidence: leakage
        suite.
  - [ ] V6.8.e Process access revoke/embargo/retraction/correction/delete/
        compartment change promptly and reconcile index to source of truth.
        Evidence: update tests.
  - [ ] V6.8.f Run two-tenant/multi-role/existence-oracle/BOLA/injection/
        high-volume/stale-index adversarial tests. Evidence: protected search
        report.
- [ ] V6.9 Implement CMS plan/publish/read-back/compensate adapter. Evidence:
      idempotent partial-failure recovery.
      <!-- SPEC 2026-09-11: the adapter targets whichever store V6.1 makes
      authoritative; if that is the Fastify CMS, its `memory-storage.ts` must
      first be replaced by a durable store and its routes given auth, and those
      are new cells under V6.2/V6.4, not side effects of this one. Read-back
      (.d) is an HTTP read from a separately started process, never the same
      in-memory object. -->
  - [ ] V6.9.a Define target/versioned adapter capabilities, field/asset/locale/
        rights/correction mappings, credentials, rate limits, and unsupported
        behavior. Evidence: adapter profile.
  - [ ] V6.9.b Build a read-only publication plan from immutable candidate with
        target operations/dependencies/idempotency keys/expected current state/
        loss/impact. Evidence: plan tests.
  - [ ] V6.9.c Execute create/update/assets/link/schedule/publish actions in
        dependency order with retries, acknowledgments, and per-action durable
        status. Evidence: publish tests.
  - [ ] V6.9.d Read back public/CMS canonical content, metadata, assets,
        visibility, timestamps, IDs, version, and correction state; semantic-
        compare to plan. Evidence: verification tests.
  - [ ] V6.9.e Compensate only reversible actions, otherwise retain partial
        release with operator remediation; never report whole success. Evidence:
        compensation tests.
  - [ ] V6.9.f Test timeout after commit, duplicate request, reordered callback,
        rate/auth loss, asset failure, schedule race, read-back mismatch, and
        retry. Evidence: partial-failure suite.
- [ ] V6.10 Implement NewsML-G2 package generation and validation. Evidence:
      golden News Item, Package, Planning, update, and correction fixtures.
      <!-- SPEC 2026-09-11: greenfield (V1.14 note); .e validates against the
      vendored IPTC XSDs and .f's "supported consumers" means at least one
      independent NewsML-G2 reader run over our output with its output retained
      (§2.5.6). -->
  - [ ] V6.10.a Resolve exact pinned release/story/source/claim/media/rights/
        topic/planning/correction data and profile/code-list versions. Evidence:
        generator inputs.
  - [ ] V6.10.b Generate deterministic News Items with identity/version/status/
        metadata/content/rights/subjects/associations and supported extensions.
        Evidence: News Item goldens.
  - [ ] V6.10.c Generate deterministic Package and Planning Items with ordered/
        role-qualified membership, schedules/embargoes, and protected redaction.
        Evidence: package/planning goldens.
  - [ ] V6.10.d Generate initial/update/correction/replacement/retraction chains
        with prior references, signals, notices, and target actions. Evidence:
        lifecycle goldens.
  - [ ] V6.10.e Validate schema/profile/code list/cardinality/URI/date/time/
        rights/language/ID rules using pinned validator. Evidence: validation
        tests.
  - [ ] V6.10.f Round-trip through supported consumers and semantic-compare
        without silently dropping unsupported data. Evidence: interoperability
        report.
- [ ] V6.11 Implement newsletter, social, audio, video, wire, and archive
      distribution adapters as target-specific jobs. Evidence: target matrix.
  - [ ] V6.11.a Define shared job/attempt/artifact/ack/read-back/correction
        envelope plus per-target capabilities, credentials, limits, and version.
        Evidence: adapter contracts.
  - [ ] V6.11.b Implement newsletter provider payload/delivery/archive/
        unsubscribe/correction mapping from an approved edition. Evidence:
        newsletter tests.
  - [ ] V6.11.c Implement each social/channel adapter with text/media/link/
        disclosure/schedule/edit-delete/correction/moderation capability and
        explicit unsupported cells. Evidence: social tests.
  - [ ] V6.11.d Implement audio/video platform feed/media/metadata/captions/
        rights/provenance/replace/correct behavior. Evidence: AV adapter tests.
  - [ ] V6.11.e Implement wire NewsML/package acknowledgement/update and archive
        immutable deposit/public status/correction/legal-hold behavior.
        Evidence: wire/archive tests.
  - [ ] V6.11.f Run durable idempotent target jobs with per-target status,
        retry/backoff/rate/cancel/read-back and no aggregate false success.
        Evidence: target matrix.
- [ ] V6.12 Implement C2PA signing/validation for supported media/package
      outputs. Evidence: tamper and trust-chain tests.
      <!-- SPEC 2026-09-11: sign with `c2pa-node@0.5.26` through
      `libs/veritas/content-auth`; .c's "HSM or equivalent" is a test signing
      certificate generated in the harness and named as such; .f's tamper cases
      mutate bytes, not our own status field. -->
  - [ ] V6.12.a Define supported C2PA
        versions/algorithms/assertions/ingredients/ actions, claim generators,
        signing identities, trust stores, and key lifecycle. Evidence: C2PA
        profile.
  - [ ] V6.12.b Build deterministic manifest from exact immutable media/article
        package/rendition/edit/AI/rights provenance. Evidence: manifest tests.
  - [ ] V6.12.c Sign in isolated authorized service/HSM or equivalent with
        idempotency, timestamp, certificate chain, audit, and no private-key
        exposure. Evidence: signing tests.
  - [ ] V6.12.d Embed or associate manifest per format/target, preserve hashes
        through supported transforms, and record stripped provenance. Evidence:
        packaging tests.
  - [ ] V6.12.e Validate cryptographic integrity, ingredient/action lineage,
        signer trust/revocation/expiry, and expected content binding. Evidence:
        validation tests.
  - [ ] V6.12.f Test valid/tampered/replaced/stripped/unknown/untrusted/expired/
        revoked/malformed chains and present precise nontruth status. Evidence:
        trust-chain suite.
- [ ] V6.13 Implement correction and retraction cascade orchestration through
      outbox/saga. Evidence: duplicate and out-of-order delivery tests.
      <!-- SPEC 2026-09-11: REUSE, do not rebuild:
      `executeVeritasSourceRetractionCascade` in `libs/oshun/domain-veritas`,
      `libs/oshun/veritas-cascade-worker` and
      `apps/oshun/bff/src/veritas/retraction-cascade-runtime.ts` already plan →
      persist (Postgres) → publish (Redis) → re-ground Sophia/Metis with
      live-infra tests. This cell extends that saga to corrections and to the
      targets V7.15 registers. -->
  - [ ] V6.13.a Create immutable cascade/plan/actions from a pinned dependency
        graph, approved remedy, target capabilities, and desired states.
        Evidence: saga contract.
  - [ ] V6.13.b Commit authoritative correction/retraction state and outbox
        event atomically with unique cascade/action/idempotency identities.
        Evidence: transaction tests.
  - [ ] V6.13.c Execute dependency-aware per-target update/replace/remove/
        notify/reindex/invalidate actions with leases/retry/backoff/timeouts.
        Evidence: worker tests.
  - [ ] V6.13.d Handle duplicate/out-of-order/late callbacks/events using
        expected versions and monotonic state without undoing newer remedies.
        Evidence: delivery tests.
  - [ ] V6.13.e Read back every target, reconcile mismatch/unknown/unsupported/
        partial state, retry or escalate, and never close unresolved targets.
        Evidence: verification tests.
  - [ ] V6.13.f Compensate only safe reversible actions, preserve audit/history,
        and replay after outage without duplicate notices. Evidence: fault
        suite.
- [ ] V6.14 Implement two-tenant and protected-compartment isolation across API,
      search, blobs, jobs, exports, and audit. Evidence: integration suite.
  - [ ] V6.14.a Create two tenants with overlapping IDs/names/content plus
        ordinary/protected stories, sources, grants, blobs, jobs, and events.
        Evidence: isolation fixture.
  - [ ] V6.14.b Test API object/property/function/filter/sort/batch/cache/
        concurrency access under direct and stale identifiers. Evidence: API
        isolation.
  - [ ] V6.14.c Test search
        indexing/query/suggestion/snippet/count/facet/vector/ timing and
        deletion/revocation. Evidence: search isolation.
  - [ ] V6.14.d Test blob keys/URLs/ranges/proxies/metadata/uploads/downloads/
        transforms/backups and signed-link substitution. Evidence: blob
        isolation.
  - [ ] V6.14.e Test queue payloads/workers/callbacks/artifacts/logs/metrics/
        retries/dead letters across tenant/compartment context. Evidence: job
        isolation.
  - [ ] V6.14.f Test audit/export/report/notification/webhook/CMS/distribution
        projections and redactions. Evidence: output isolation.
  - [ ] V6.14.g Attempt service account, admin, emergency grant, role revoke,
        cache poisoning, guessed ID, and cross-tenant relationship attacks.
        Evidence: adversarial integration suite.
- [ ] V6.15 Generate OpenAPI and consumer contracts for Studio, CMS, reader,
      archive, and distribution consumers. Evidence: drift gate.
      <!-- SPEC 2026-09-11: the API app has
      `apps/veritas/api/src/interfaces/http/openapi.ts`; generate from the
      served router, not by hand, and treat the CMS routes and BFF
      `/v1/veritas/*` as separate audiences. Follow M8.15's register shape
      (every operation classified, unclassified = failure). -->
  - [ ] V6.15.a Generate canonical versioned OpenAPI from authoritative route/
        schema/error/auth/pagination/idempotency contracts with stable operation
        IDs. Evidence: OpenAPI artifact.
  - [ ] V6.15.b Produce audience-specific Studio/CMS/reader/archive/distribution
        contract projections without publishing protected internal fields.
        Evidence: consumer schemas.
  - [ ] V6.15.c Add representative requests/responses/errors/events and
        generated clients/types where owned, pinned to exact spec digest.
        Evidence: contract fixtures.
  - [ ] V6.15.d Run schema lint/breaking-change/unknown route/unimplemented
        operation and server-versus-spec conformance tests. Evidence: provider
        gate.
  - [ ] V6.15.e Run consumer compile/fixture/semantic contract tests for each
        supported version and target behavior, including corrections. Evidence:
        consumer gate.
  - [ ] V6.15.f Fail CI on unreviewed drift, protected-field exposure, orphan
        operation, stale generated client, or unsupported breaking change.
        Evidence: drift gate.

### V7 — Calendar, publication, correction, retraction, and curation

- [ ] V7.1 Build an editorial calendar for assignments, embargoes, coverage
      plans, review, publication, newsletters, and distribution windows.
      Evidence: accessible time-zone journey.
  - [ ] V7.1.a Define calendar item/reference contracts for each named entity
        with start/end/deadline/window/time zone/recurrence/status/owner and
        exact source revision. Evidence: calendar schema.
  - [ ] V7.1.b Build permission-filtered day/week/month/agenda views with stable
        filters by desk/owner/type/state/target/locale and protected redaction.
        Evidence: view tests.
  - [ ] V7.1.c Create/edit/reschedule/assign from owned workflows with
        validation, dependency/embargo/conflict/impact preview, concurrency, and
        audit. Evidence: action tests.
  - [ ] V7.1.d Normalize storage and display correctly across newsroom/user/
        target time zones, DST transitions, ambiguous/nonexistent times, and
        locale calendars. Evidence: temporal tests.
  - [ ] V7.1.e Provide table/agenda and form alternatives to visual calendar/
        drag operations with keyboard/screen-reader parity. Evidence:
        accessibility tests.
  - [ ] V7.1.f Exercise assignment→review→embargo→publish→distribution journey
        across time zones and responsive web/desktop. Evidence: E2E.
- [ ] V7.2 Model embargo as an independent policy, not a publish status.
      Evidence: NewsML-G2 and CMS mapping tests.
  - [ ] V7.2.a Implement embargo policy entity/versions independently of story,
        editorial, release, schedule, and target execution states. Evidence:
        state model.
  - [ ] V7.2.b Evaluate access/display/export/model-use/notification/search/
        preview/publish constraints from current time/event/party/territory/
        channel and authorized exceptions. Evidence: policy tests.
  - [ ] V7.2.c Handle set/update/lift/expire/breach/dispute with authority,
        reason, source agreement, affected objects, audit, and notifications.
        Evidence: lifecycle tests.
  - [ ] V7.2.d Recompute release eligibility/schedules on policy changes without
        changing editorial approval or falsely marking published. Evidence:
        independence tests.
  - [ ] V7.2.e Map embargo fields/actions to NewsML-G2 and each CMS/distribution
        target, preserving unsupported or lossy behavior explicitly. Evidence:
        adapter matrix.
- [ ] V7.3 Build desk capacity and specialist-review reservation. Evidence:
      committed slots cannot be double-booked.
  - [ ] V7.3.a Define staff/role/skill/credential/desk/time-zone/availability/
        capacity/calendar and privacy-safe display contracts. Evidence: capacity
        schema.
  - [ ] V7.3.b Forecast assignment/review workload by type/risk/effort/deadline/
        phase without turning output volume into performance scoring. Evidence:
        workload model.
  - [ ] V7.3.c Create tentative/held/committed/released/expired reservation with
        story/review/owner/window/capacity units, priority, and dependencies.
        Evidence: reservation state machine.
  - [ ] V7.3.d Commit atomically under concurrent booking, enforce conflicts/
        leave/on-call/quorum/credential constraints, and prevent double booking.
        Evidence: concurrency tests.
  - [ ] V7.3.e Support reschedule/reassign/cancel/overbook-exception with
        impact, authority, notifications, SLA recalculation, and audit.
        Evidence: operations tests.
- [ ] V7.4 Build release candidates locked to exact draft, source, claim,
      evidence, media, attestation, policy, and locale versions. Evidence:
      mutation invalidation.
  - [ ] V7.4.a Resolve complete transitive candidate dependency manifest across
        exact draft/blocks/claims/sources/citations/evidence/media/attestations/
        reviews/disclosures/locales/targets/policies. Evidence: manifest schema.
  - [ ] V7.4.b Freeze canonical serialized references/hashes/signatures and
        target/template/config/adapter versions without copying protected
        plaintext. Evidence: candidate artifact.
  - [ ] V7.4.c Verify every dependency exists/is authorized/current/approved/
        integrity-valid and record missing/stale/contested/unsupported state.
        Evidence: validation tests.
  - [ ] V7.4.d Allow explicit candidate create/supersede/abandon only; prohibit
        mutation of locked manifests or historical approvals. Evidence:
        immutability tests.
  - [ ] V7.4.e Subscribe/reconcile relevant mutations and invalidate candidate,
        previews, signoffs, schedules, and jobs before dispatch. Evidence:
        invalidation tests.
  - [ ] V7.4.f Recheck manifest transactionally at signoff and execution to
        close dependency races. Evidence: race tests.
- [ ] V7.5 Build target preview and eligibility diagnostics. Evidence:
      unsupported or missing fields never silently drop.
  - [ ] V7.5.a Resolve exact release candidate/target/account/locale/audience/
        template/adapter/policy versions and current credentials/capabilities.
        Evidence: preview inputs.
  - [ ] V7.5.b Render production-equivalent semantic/visual/payload preview with
        target limits/transforms/fallbacks/disclosures/correction behavior.
        Evidence: preview tests.
  - [ ] V7.5.c Validate required/optional fields,
        types/lengths/media/renditions/
        rights/accessibility/embargo/schedule/approval/channel policies.
        Evidence: diagnostics.
  - [ ] V7.5.d Report unsupported/dropped/truncated/transformed/lossy fields
        individually with severity, effect, remediation, and no false eligible
        state. Evidence: loss tests.
  - [ ] V7.5.e Recompute on candidate/target capability/template/policy/account
        changes and label stale/fixture/degraded/unavailable state. Evidence:
        freshness tests.
  - [ ] V7.5.f Compare preview semantic output to actual read-back for supported
        cells. Evidence: parity gate.
- [ ] V7.6 Build schedule, publish, read-back verification, and multi-target
      result views. Evidence: one failed target cannot make the release wholly
      successful.
  - [ ] V7.6.a Create per-target scheduled action with candidate/eligibility/
        account/locale/time zone/window/idempotency/dependency and authorized
        publisher. Evidence: schedule contract.
  - [ ] V7.6.b Recheck embargo/candidate/gates/approvals/rights/credentials/
        target capability immediately before durable dispatch. Evidence:
        dispatch guard tests.
  - [ ] V7.6.c Execute target-specific publish jobs with queue/lease/retry/rate/
        cancel/callback and persist target ID/URL/version/acknowledgment.
        Evidence: job tests.
  - [ ] V7.6.d Read back public/target content, metadata, assets, visibility,
        correction/disclosure/provenance and semantic-compare to candidate.
        Evidence: read-back tests.
  - [ ] V7.6.e Present queued/running/scheduled/published/verified/degraded/
        failed/unknown/compensated per target plus aggregate partial status.
        Evidence: result view.
  - [ ] V7.6.f Retry/remediate/recall selected targets with impact/concurrency/
        audit, never converting one success/failure into whole-release success.
        Evidence: partial failure E2E.
- [ ] V7.7 Build visible correction authoring with prior/new text, rationale,
      affected claims, and reader notice. Evidence: canonical correction
      invariants.
  - [ ] V7.7.a Start correction from exact public release/target or verified
        feedback with type/severity/discovery/source/owner and preserve prior
        version. Evidence: correction intake.
  - [ ] V7.7.b Select exact prior blocks/ranges/media/captions/metadata and
        author replacement/removal/addition as a new revision with semantic
        diff. Evidence: change editor.
  - [ ] V7.7.c Link affected claims/citations/sources/packages/targets and
        recompute evidence/gates/reviews plus blast radius. Evidence: impact
        tests.
  - [ ] V7.7.d Require factual rationale, discovery/correction time, responsible
        editor, applicable approvals, and target/locale-specific reader notice.
        Evidence: notice contract.
  - [ ] V7.7.e Preview prior/new content and notice at every target, including
        archive/history and accessible semantic output. Evidence: preview tests.
  - [ ] V7.7.f Enforce prior text/version/hash match, immutable chronology,
        concurrency, correction-of-correction, and no silent overwrite.
        Evidence: canonical invariant suite.
- [ ] V7.8 Build source/claim/story retraction intake and impact preview.
      Evidence: complete dependency graph.
  - [ ] V7.8.a Intake source/claim/story retraction candidate with issuer/
        evidence/cause/scope/severity/verification/time and exact subject
        revision. Evidence: intake schema.
  - [ ] V7.8.b Authenticate/verify external retraction or require authorized
        internal human decision; classify disputed/partial/unknown state.
        Evidence: verification tests.
  - [ ] V7.8.c Traverse full versioned dependency graph through citations/
        claims/stories/packages/locales/media/newsletters/social/channels/audio/
        video/archive/search/B2B. Evidence: impact query.
  - [ ] V7.8.d Show each affected node/edge/current release/target, proposed
        remedy, urgency/owner/state, protected redaction, and unknown consumers.
        Evidence: preview UI.
  - [ ] V7.8.e Freeze a read-only impact snapshot and reconcile against consumer
        registry; never mutate during preview. Evidence: completeness tests.
  - [ ] V7.8.f Require separately approved cascade plan and fresh graph check
        before execution. Evidence: authorization/race tests.
- [ ] V7.9 Build retraction-cascade execution with per-target status and retry.
      Evidence: CMS, reader, newsletter, social, archive, search, and package
      fixtures.
  - [ ] V7.9.a Convert approved impact snapshot into dependency-ordered,
        target-specific retain/correct/withdraw/retract/remove/notify/reindex/
        invalidate actions. Evidence: execution plan.
  - [ ] V7.9.b Dispatch durable idempotent saga actions with expected versions,
        prerequisites, attempts, rate limits, retries, and operator ownership.
        Evidence: orchestration tests.
  - [ ] V7.9.c Implement CMS/reader and NewsML/package state/notice/version
        updates with preserved history and canonical URLs. Evidence: core target
        tests.
  - [ ] V7.9.d Implement newsletter/social/channel/audio/video/archive/search/
        B2B target capabilities with explicit unsupported/partial behavior.
        Evidence: channel target tests.
  - [ ] V7.9.e Show target/action queued/running/succeeded/verified/failed/
        unknown/unsupported/compensated and allow authorized safe retry.
        Evidence: status UI.
  - [ ] V7.9.f Handle duplicate/out-of-order/late callbacks, concurrent newer
        corrections, auth loss, outage, and replay without reversing remedies.
        Evidence: fault tests.
- [ ] V7.10 Build correction/retraction verification from downstream read-back.
      Evidence: stale dependent surface blocks closure.
  - [ ] V7.10.a Define expected semantic state per target/action, verification
        adapter/version, tolerated delay, retry policy, and closure criteria.
        Evidence: verification contract.
  - [ ] V7.10.b Read back public content/metadata/status/notices/history/assets/
        search/cache/feed/API/webhook state using independent paths. Evidence:
        read-back tests.
  - [ ] V7.10.c Compare identity/version/text/media/claim/correction/retraction/
        visibility/time/links semantically rather than only acknowledgment.
        Evidence: comparator tests.
  - [ ] V7.10.d Mark mismatch/stale/unreachable/unauthorized/unsupported/unknown
        per dependent surface with evidence, owner, retry, and escalation.
        Evidence: failure states.
  - [ ] V7.10.e Prevent cascade/correction closure while any required registered
        surface is unresolved or registry reconciliation is incomplete.
        Evidence: closure gate.
  - [ ] V7.10.f Reverify after caches/indexes/feeds propagate and retain signed
        final reconciliation plus later drift monitoring. Evidence: verification
        report.
- [ ] V7.11 Build section, topic, package, timeline, related, and featured
      curation via versioned bundles. Evidence: no direct production writes.
  - [ ] V7.11.a Define typed versioned bundle identity/scope/locale/territory/
        window/status/owner and ordered/grouped item references by curation
        kind. Evidence: bundle schemas.
  - [ ] V7.11.b Build draft editor for select/search/add/remove/order/group/
        annotate/schedule with non-drag controls and protected/embargo rules.
        Evidence: curation UI.
  - [ ] V7.11.c Validate item release/rights/correction/retraction/freshness/
        target/locale compatibility and required contextual notices. Evidence:
        eligibility tests.
  - [ ] V7.11.d Provide semantic diff/review/approval/preview for exact bundle
        revision and invalidate on item or policy changes. Evidence: review
        tests.
  - [ ] V7.11.e Publish through canonical target adapter/job/read-back with
        immutable prior versions, idempotency, and rollback/new revision.
        Evidence: bundle release tests.
  - [ ] V7.11.f Prohibit client/direct CMS/production mutations and audit every
        attempted bypass. Evidence: no-direct-write tests.
- [ ] V7.12 Build archive and legal-hold behavior. Evidence: public removal does
      not destroy required evidence.
  - [ ] V7.12.a Define archive tiers/state/retention/access/index/public
        visibility and legal-hold scope/reason/authority/effective/expiry.
        Evidence: lifecycle contract.
  - [ ] V7.12.b Archive immutable stories/revisions/sources/evidence/media/
        reviews/releases/corrections/audit with reference and integrity
        preservation. Evidence: archive tests.
  - [ ] V7.12.c Separate public unpublish/withdraw/retract/search removal from
        internal retention and show appropriate public notice. Evidence: removal
        tests.
  - [ ] V7.12.d Apply hold to records/blobs/indexes/backups/exports/processors,
        suspend conflicting deletion, and record conflicts. Evidence: hold
        tests.
  - [ ] V7.12.e Enforce archive/hold role/field/compartment access, protected
        search redaction, audited export, and secure restore. Evidence:
        authorization tests.
  - [ ] V7.12.f Test retention expiry/deletion request/correction/retraction/
        source protection/backup restore under and after hold. Evidence:
        lifecycle suite.
- [ ] V7.13 Build source and attestation expiry monitoring. Evidence: affected
      published claims enter review.
  - [ ] V7.13.a Define expiry/staleness triggers from source update/retraction/
        rights/review interval and attestor credential/conflict/validity/scope.
        Evidence: monitoring rules.
  - [ ] V7.13.b Schedule idempotent scans and event-driven checks against
        current versions with leases/cursors/rate limits and clock/time-zone
        handling. Evidence: monitor tests.
  - [ ] V7.13.c Traverse affected claim/story/release/target graph and calculate
        risk/urgency/deadline without exposing protected source details.
        Evidence: impact tests.
  - [ ] V7.13.d Mark dependent evidence/gates/approvals/releases as stale or
        review-required according to policy; never silently keep current pass.
        Evidence: invalidation tests.
  - [ ] V7.13.e Create deduplicated review assignments/notifications/escalations
        with safe context, owner, due date, and remediation. Evidence: workflow
        tests.
  - [ ] V7.13.f Track resolve/replace/renew/correct/retract/waive outcomes and
        reverify downstream state. Evidence: monitoring E2E.
- [ ] V7.14 Build post-publication audience feedback and complaint intake.
      Evidence: triage cannot directly rewrite published truth.
  - [ ] V7.14.a Provide accessible authenticated or permitted anonymous intake
        with channel/type/story/claim/target/locale/contact/consent/privacy/
        evidence fields. Evidence: intake contract.
  - [ ] V7.14.b Scan/quarantine attachments and rate-limit/spam/abuse protect
        without exposing reporter/source identities or creating a public
        existence oracle. Evidence: security tests.
  - [ ] V7.14.c Classify feedback, factual correction request, NMC/legal
        complaint, right-of-reply, safety/privacy, accessibility, and tip as
        distinct queues. Evidence: routing tests.
  - [ ] V7.14.d Assign owner/SLA/priority/acknowledgment/status/response/
        escalation with protected/redacted notes and audit. Evidence: triage
        workflow.
  - [ ] V7.14.e Link evidence to exact public version/claim without modifying
        canonical story, claim, correction, or public content. Evidence:
        immutability tests.
  - [ ] V7.14.f Convert only via authorized editorial/complaint/correction/tip
        workflow and send privacy-safe resolution. Evidence: handoff E2E.
- [ ] V7.15 Build cross-domain impact hooks for explainers, saved claims,
      Telegram cards, and Living Scenes. Evidence: correction cascade reaches
      registered consumers.
      <!-- SPEC 2026-09-11: the consumer registry must include the consumers
      that already exist — the Sophia re-grounder and Metis revalidator in
      `libs/oshun/veritas-cascade-worker` and the
      personal-retraction-acknowledgement store in the BFF — before adding new
      ones; X.21 later reads this registry. -->
  - [ ] V7.15.a Define versioned consumer registration with domain/owner/
        endpoint/event types/subject mappings/capabilities/auth/retry/read-back/
        SLA. Evidence: registry schema.
  - [ ] V7.15.b Publish correction/retraction/source/claim/story lifecycle
        events with opaque stable IDs, versions, reason/notice, and no protected
        data. Evidence: event contract.
  - [ ] V7.15.c Implement explainer, saved-claim, Telegram-card, and Living
        Scene adapters/projections with explicit correction/retraction behavior.
        Evidence: consumer tests.
  - [ ] V7.15.d Deliver through durable outbox with idempotency/order/retry/
        dead-letter/replay and per-consumer status. Evidence: delivery tests.
  - [ ] V7.15.e Require acknowledgment or independent read-back and keep
        unknown/ stale/unregistered/unsupported consumers visible. Evidence:
        verification tests.
  - [ ] V7.15.f Reconcile capability registry to observed consumers and fail
        missing owner/hook/proof or protected-field exposure. Evidence: cascade
        coverage gate.
- [ ] V7.16 Build emergency recall with step-up authorization and later review.
      Evidence: reversible, audited incident journey.
  - [ ] V7.16.a Define eligible emergency recall reasons/severity/scope/targets/
        authority/nonwaivable constraints and distinction from ordinary
        retraction. Evidence: recall policy.
  - [ ] V7.16.b Build read-only blast-radius/target-capability/impact preview,
        alternatives, expected public state, and rollback/restoration plan.
        Evidence: recall plan.
  - [ ] V7.16.c Require current publisher/corrections/emergency role, step-up
        and higher/dual approval as policy requires, reason, incident, and
        expiry. Evidence: authorization tests.
  - [ ] V7.16.d Dispatch idempotent per-target hide/withdraw/replace/notify/
        invalidate actions with progress, failures, and independent read-back.
        Evidence: execution tests.
  - [ ] V7.16.e Restore or replace only through new approved state after risk is
        addressed; retain recall notice/history and prevent stale reappearance.
        Evidence: reversibility tests.
  - [ ] V7.16.f Conduct mandatory post-event review of
        trigger/authority/actions/ effects/gaps, close unresolved targets, and
        preserve audit. Evidence: incident journey.
- [ ] V7.17 Build desk/edition/program steering overlays for sourcing,
      specialist review, risk, style, audience, distribution, and cadence.
      Evidence: tighten-only property tests and impact preview.
  - [ ] V7.17.a Define versioned overlay identity/scope/priority/effective
        window/ owner/reviewer and each allowed
        sourcing/review/risk/style/audience/ distribution/cadence property.
        Evidence: overlay schema.
  - [ ] V7.17.b Define code/regulatory/global policy floors and partial order
        for stricter versus weaker values per property. Evidence: lattice spec.
  - [ ] V7.17.c Resolve applicable overlays deterministically across desk/
        edition/program/locale/territory/phase/target with conflicts explicit.
        Evidence: resolution tests.
  - [ ] V7.17.d Preview affected drafts/gates/reviews/schedules/targets/users
        and newly blocked/required actions before activation. Evidence: impact
        preview.
  - [ ] V7.17.e Govern draft/review/approve/activate/supersede/rollback/migrate/
        expire with separation, audit, and no direct policy-floor editing.
        Evidence: governance tests.
  - [ ] V7.17.f Property-test that every overlay composition is equal or tighter
        than floors and cannot relax protected-source/safety/legal/human
        authority. Evidence: tighten-only suite.
- [ ] V7.18 Build UI-governed desk, topic, format, source, claim, risk,
      audience, territory, and catalog taxonomies mapped to IPTC vocabularies
      and code-owned floors. Evidence: versioned review, alias, deprecation,
      localization, and migration journey.
  - [ ] V7.18.a Define stable concept/code/set/version/label/definition/parent/
        relation/alias/status/locale/IPTC mapping and code-owned floor schema.
        Evidence: taxonomy contracts.
  - [ ] V7.18.b Build Veritas taxonomy draft editing for add/edit/reparent/
        relate/alias/merge/split/localize with cycle/duplicate/code/meaning
        validation. Evidence: editor tests.
  - [ ] V7.18.c Map to pinned IPTC Media Topics/code lists with exact/broader/
        narrower/related/unmapped relation, provenance, and review. Evidence:
        mapping tests.
  - [ ] V7.18.d Preview impact across stories/sources/claims/search/analytics/
        policies/routes/feeds/NewsML/targets before version activation.
        Evidence: impact analysis.
  - [ ] V7.18.e Govern review/approve/activate/supersede/deprecate with aliases/
        replacements, localization completeness, immutable versions, and no
        floor weakening. Evidence: governance tests.
  - [ ] V7.18.f Migrate references/read models/indexes atomically or
        recoverably, preserve historical meaning, and reject orphan/unknown
        values. Evidence: migration journey.

### V7A — Channel, audience, community, and business operations

<!-- SPEC 2026-09-11: V7A cells build on `apps/veritas/api` routes that already
exist (payments, subscriptions, ads, b2b-api, paywall, tips, ussd via
`libs/veritas/ussd`, notifications, social, live-stream libs) — inventory them
under V0.14 first and extend, never duplicate. Every provider-facing cell
(WhatsApp, payment rails, social platforms, telecom USSD) needs live credentials
that env-master does not carry; such a cell records the failed attempt and stays
open (§2.5.6). Commercial routes are already co-located with editorial routes in
one Hono app: V7A.10.a's separation is a real split, and V0.15 must name it. -->


- [ ] V7A.1 Build newsletter edition authoring for sections, story selection,
      summaries, subject/preheader variants, sponsorship disclosure,
      personalization limits, preview/test send, audience/consent, schedule,
      delivery, bounce/complaint, archive, correction, and unsubscribe.
      Evidence: edition-to-story revision lineage and downstream correction.
  - [ ] V7A.1.a Define newsletter/publication/edition/section/item/variant/
        audience/template/schedule/send/version/state contracts. Evidence:
        newsletter schemas.
  - [ ] V7A.1.b Build ordered section/story selection from exact eligible
        releases with semantic/non-drag controls, duplicate/conflict checks, and
        correction/retraction visibility. Evidence: edition editor.
  - [ ] V7A.1.c Author summaries/subject/preheader variants with factual
        comparison, locale/length/style rules, provenance, and human selection.
        Evidence: content tests.
  - [ ] V7A.1.d Insert sponsorship/advertising blocks with advertiser/campaign/
        rights/targeting, mandatory disclosure/separation, and editorial
        independence. Evidence: sponsorship gate.
  - [ ] V7A.1.e Apply bounded versioned personalization only to approved
        selection/order/content fields with privacy floors and no fact/
        correction/disclosure hiding. Evidence: personalization tests.
  - [ ] V7A.1.f Render production-equivalent web/email/client previews and
        authorized test sends with links/assets/UTM/accessibility/dark-mode/
        plain-text validation. Evidence: preview tests.
  - [ ] V7A.1.g Resolve consented audience/segment/suppression/unsubscribe/
        locale/frequency/quiet policy at send time without exporting protected
        newsroom data. Evidence: audience tests.
  - [ ] V7A.1.h Schedule and execute idempotent provider jobs with per-recipient
        privacy-safe delivery/bounce/complaint/suppression aggregates, retries,
        and read-back/archive. Evidence: send tests.
  - [ ] V7A.1.i Propagate story correction/retraction to edition archive, web
        copy, eligible follow-up notice, links/search, and provider state while
        preserving exact edition→story revision lineage. Evidence: correction
        E2E.
- [ ] V7A.2 Build social-channel planning for platform-specific copy/media,
      account, schedule, cadence, rights, disclosure, approval, preview,
      publish, acknowledgement, moderation handoff, retry, edit/delete where
      supported, and correction. Evidence: no optimizer can change verified
      meaning or silently publish.
  - [ ] V7A.2.a Define per-platform/account capability/version/credential/
        content-limit/media/rendition/link/schedule/edit-delete/moderation/
        correction contract. Evidence: channel matrix.
  - [ ] V7A.2.b Create post plan pinned to story release/claims/media/locale/
        target with account, objective, schedule/cadence, owner, and current
        correction state. Evidence: plan schema.
  - [ ] V7A.2.c Author platform copy/media/crops/captions/alt text/link/cards/
        hashtags with factual-proposition and rights checks. Evidence: authoring
        tests.
  - [ ] V7A.2.d Apply sponsorship/AI/correction/graphic-content disclosures and
        safe areas/length/format constraints without silent truncation/drop.
        Evidence: eligibility tests.
  - [ ] V7A.2.e Preview exact target payload/rendering and require named human
        approval of immutable version; optimizers remain proposal-only.
        Evidence: approval tests.
  - [ ] V7A.2.f Schedule/publish through durable idempotent jobs with account/
        rate/auth checks, acknowledgment/target ID/read-back, retry, and partial
        status. Evidence: publish tests.
  - [ ] V7A.2.g Hand off replies/comments/incidents to moderation with exact
        post/story context and no editorial-source leakage. Evidence: moderation
        handoff.
  - [ ] V7A.2.h Execute supported edit/delete/replace/correction or follow-up
        notice with prior history and read-back; keep unsupported targets
        unresolved. Evidence: correction E2E.
  - [ ] V7A.2.i Attempt optimizer/model/agent direct mutation/selection/
        scheduling/publish and meaning-changing rewrite; prove rejection.
        Evidence: independence tests.
- [ ] V7A.3 Build WhatsApp/channel ecosystem operations for subscription
      consent, segmentation, post preview, template/policy status, schedule,
      delivery, reply/escalation, unsubscribe, correction, and rate/quality
      limits. Evidence: current channel APIs and privacy rules.
  - [ ] V7A.3.a Pin current WhatsApp/other channel provider/API/template/policy/
        pricing/quality/rate/region capabilities and responsible owner.
        Evidence: channel profiles.
  - [ ] V7A.3.b Implement explicit opt-in/source/purpose/channel/topic/locale/
        time evidence, preference updates, opt-out/suppression, and consent
        expiry. Evidence: consent tests.
  - [ ] V7A.3.c Define privacy-preserving segments with minimum cohorts,
        allowlisted attributes, no protected-source/editorial-sensitive data,
        and reproducible membership snapshot. Evidence: segmentation tests.
  - [ ] V7A.3.d Author/select approved template and story/card/media/correction
        payload with locale, parameters, disclosure, rights, length, and policy
        status. Evidence: content tests.
  - [ ] V7A.3.e Preview exact recipient-visible message/conversation context,
        estimated audience/cost, quiet/frequency/rate/quality impact, and
        require approval. Evidence: preview tests.
  - [ ] V7A.3.f Schedule/send idempotently with consent/template/account/rate/
        quality recheck, per-message status, provider acknowledgments, retries,
        and aggregate privacy. Evidence: delivery tests.
  - [ ] V7A.3.g Route replies by intent to support/moderation/complaint/tip/
        correction workflows with safe identity/context and SLA; honor
        unsubscribe immediately. Evidence: reply tests.
  - [ ] V7A.3.h Propagate corrections/recalls with platform-supported update or
        follow-up, archive status/read-back, and unresolved recipient/target
        visibility. Evidence: correction E2E.
- [ ] V7A.4 Build push, email, in-app, topic/author/source follow,
      breaking-news, and emergency-broadcast operations with audience, severity,
      locale, quiet-hours override authority, dedupe, preview, delivery,
      acknowledgement, correction, expiry, and recall. Evidence: emergency
      rights and false-alarm review are separate from ordinary campaigns.
  - [ ] V7A.4.a Define notification/campaign/event/delivery/ack/recall contracts
        and per-channel capabilities for push/email/in-app. Evidence:
        notification schemas.
  - [ ] V7A.4.b Implement consented topic/author/public-source follows and
        audience/locale/device/entitlement/preferences/suppression resolution
        with protected-source exclusion. Evidence: audience tests.
  - [ ] V7A.4.c Define ordinary/breaking/emergency severity, eligibility,
        evidence, templates, authority, target scope, expiry, and quiet-hours
        policy separately. Evidence: authority matrix.
  - [ ] V7A.4.d Build factual title/body/link/media/disclosure/correction
        content pinned to exact public release and preview each channel/device/
        locale including accessibility. Evidence: preview tests.
  - [ ] V7A.4.e Deduplicate by event/story/claim/target/audience/window and
        apply frequency/quiet limits unless a specifically authorized emergency
        override exists. Evidence: dedupe/policy tests.
  - [ ] V7A.4.f Dispatch durable per-channel jobs with current
        consent/authority/ release checks, provider acknowledgment,
        delivery/open/ack privacy floors, retry, and partial state. Evidence:
        delivery tests.
  - [ ] V7A.4.g Correct/expire/recall notifications through target capability,
        in-app persistent notice, links/read-back, and no false success for
        delivered immutable messages. Evidence: remedy tests.
  - [ ] V7A.4.h Run emergency activation→broadcast→acknowledgment→recall/
        correction→deactivation plus mandatory false-alarm/post-event review,
        separately from campaigns. Evidence: emergency E2E.
- [ ] V7A.5 Build community/comment moderation for thread state, identity/
      pseudonymity, spam/abuse, misinformation links, source protection,
      appeals, rate limits, moderator notes, legal hold, transparency, and
      article correction synchronization. Evidence: community content is never
      presented as newsroom-attested fact.
  - [ ] V7A.5.a Define community/thread/comment/reaction/report/decision/appeal/
        moderator-note/version/state schemas separately from editorial claims.
        Evidence: community contracts.
  - [ ] V7A.5.b Define identity/pseudonymity/verification/consent/age/privacy/
        block/mute protections and prevent correlation with protected sources/
        tips. Evidence: identity tests.
  - [ ] V7A.5.c Implement submit/edit/delete/reply/report with rate/spam/bot/
        abuse/link/media scanning, quarantine, duplicate, and safe rendering.
        Evidence: intake tests.
  - [ ] V7A.5.d Run human-governed moderation queues for harassment/hate/threat/
        doxxing/misinformation/spam/legal/safety with evidence, uncertainty,
        consistency, and emergency escalation. Evidence: moderation workflow.
  - [ ] V7A.5.e Record allow/limit/label/remove/lock/suspend/escalate decisions,
        reason/policy version/actor/expiry, protected moderator notes, and
        transparent user notice. Evidence: decision tests.
  - [ ] V7A.5.f Implement appeal intake/independent
        review/reversal/confirmation/ notice and preserve immutable prior
        decisions. Evidence: appeals E2E.
  - [ ] V7A.5.g Apply retention/deletion/legal hold/audit/export and moderator
        access controls without leaking reporter/source/legal data. Evidence:
        lifecycle tests.
  - [ ] V7A.5.h Synchronize story correction/retraction context/links/thread
        state while never labeling community content as verified newsroom fact.
        Evidence: correction/parity tests.
- [ ] V7A.6 Build reader feedback, complaints, right-of-reply, tip conversion,
      fact-check requests, and correction requests as distinct queues with
      routing, evidence, SLA, privacy, status, response, and audit. Evidence:
      external submissions cannot directly mutate editorial records.
  - [ ] V7A.6.a Define distinct typed intake contracts, required/optional
        identity/consent/story/claim/evidence fields, and safe anonymous
        options. Evidence: queue schemas.
  - [ ] V7A.6.b Scan/quarantine attachments, validate target/version/link, apply
        rate/abuse protection, and prevent source/reporter existence leakage.
        Evidence: intake security tests.
  - [ ] V7A.6.c Route by type/rule/jurisdiction/desk/risk/locale to named owner,
        independent queue, SLA/calendar, escalation, and protected compartment.
        Evidence: routing tests.
  - [ ] V7A.6.d Track received/acknowledged/triaged/investigating/waiting/
        decided/remedied/appealed/closed with privacy-safe external status.
        Evidence: state machine.
  - [ ] V7A.6.e Preserve submitted evidence/communications/decisions/responses/
        deadlines/audit and apply minimization/retention/legal hold. Evidence:
        record tests.
  - [ ] V7A.6.f Convert only through explicit authorized handoff to anonymous
        tip, fact-check assignment, NMC/legal/right-of-reply, or correction
        workflow with lineage. Evidence: handoff tests.
  - [ ] V7A.6.g Respond/notify with reviewed redacted outcome and appeal path;
        prevent external submitters or intake workers from editing/publishing.
        Evidence: mutation boundary tests.
- [ ] V7A.7 Build multilingual production across the ratified Ghanaian and other
      locales for translation, transliteration, terminology, names, audio/voice,
      captions, media text, fact parity, cultural review, publication, and
      correction cascade. Evidence: each locale locks exact source-story and
      evidence versions.
  - [ ] V7A.7.a Ratify supported locale/script/region/fallback/direction/
        orthography/number/date/name/pronunciation/voice/channel capability
        matrix with owners. Evidence: locale profile.
  - [ ] V7A.7.b Create locale variant pinned to exact source story/claims/
        citations/evidence/media/disclosures/terminology/policy versions.
        Evidence: locale manifest.
  - [ ] V7A.7.c Build translation/transliteration editor with segment/semantic
        anchors, protected terms, names/entities, citations, uncertainty, and
        per-change human/AI provenance. Evidence: editor tests.
  - [ ] V7A.7.d Localize images/graphics/tables/maps/alt text/transcripts/
        captions/audio description/voice/music disclosures and generate target
        renditions. Evidence: media localization tests.
  - [ ] V7A.7.e Run claim/number/date/entity/quote/citation/uncertainty/
        correction/disclosure semantic parity plus terminology consistency.
        Evidence: fact parity gate.
  - [ ] V7A.7.f Run independent qualified language/cultural/editorial/
        accessibility review on exact locale revision with sensitive-context
        handling. Evidence: review signoff.
  - [ ] V7A.7.g Preview/publish/read back each locale/target independently with
        canonical/hreflang or equivalent links, rights, fallback, and no silent
        source-locale substitution. Evidence: release tests.
  - [ ] V7A.7.h Propagate source-story correction/retraction to every locale,
        invalidate approval, translate/review notices, and keep unresolved
        locales visible. Evidence: cascade E2E.
- [ ] V7A.8 Build B2B feed, search, fact-check, entity, media, monitoring, and
      alert product operations for API products, clients, keys, scopes, quotas,
      webhooks, schemas, sandbox, usage, billing, deprecation, revocation, and
      correction/retraction events. Evidence: developer SDK conformance and no
      protected/unlicensed field exposure.
  - [ ] V7A.8.a Define each API product/resource/action/version/audience/data/
        rights/freshness/SLO/pricing capability and explicit protected/
        unlicensed exclusions. Evidence: product catalog.
  - [ ] V7A.8.b Build client/application/service-account onboarding with owner/
        purpose/contract/region/entitlements/environments/approval/review/
        suspension state. Evidence: client lifecycle.
  - [ ] V7A.8.c Issue hashed/rotatable scoped keys or tokens with least
        privilege, tenant/product/environment/IP or mTLS constraints, expiry,
        last-used, and emergency revoke. Evidence: credential tests.
  - [ ] V7A.8.d Implement versioned feed/search/fact-check/entity/media/
        monitoring/alert schemas with stable IDs, pagination, field projection,
        attribution, rights, and current correction state. Evidence: API tests.
  - [ ] V7A.8.e Enforce per-client/product/operation quota/rate/concurrency/
        payload/retention/export policy with consistent headers/errors and safe
        analytics. Evidence: limit tests.
  - [ ] V7A.8.f Implement signed webhooks with subscription filters, secret
        rotation, replay protection, idempotent delivery/retry/dead letter, and
        correction/retraction events. Evidence: webhook tests.
  - [ ] V7A.8.g Provide isolated synthetic sandbox, versioned OpenAPI/SDK/
        examples/conformance, changelog/deprecation/migration, and no production
        protected data. Evidence: developer experience tests.
  - [ ] V7A.8.h Meter usage/billing/refunds/disputes separately from editorial
        ranking/access decisions and reconcile without placing payment data in
        newsroom content stores. Evidence: commerce tests.
  - [ ] V7A.8.i Run two-client scope/tenant/BOLA/filter/snippet/media
        URL/export/ log/cache/schema/version/revocation tests and prove no
        protected/ unlicensed exposure. Evidence: security/SDK conformance gate.
- [ ] V7A.9 Build USSD and voice-callback content operations for menu/routing,
      concise story/fact-check variants, language, consent, telecom status,
      session limits, accessibility, fallback, charge disclosure, emergency,
      correction, and analytics minimization. Evidence: feature-phone content
      retains canonical story/claim identity and current correction state.
  - [ ] V7A.9.a Define USSD menu/session/step/content/action and voice callback/
        script/recording/call state contracts with provider/network/version.
        Evidence: channel schemas.
  - [ ] V7A.9.b Author concise locale-specific story/fact-check/correction/
        emergency variants pinned to canonical release/claim/evidence and
        human-reviewed for meaning parity. Evidence: content tests.
  - [ ] V7A.9.c Build bounded numbered menu/routing/back/repeat/more/language/
        help/exit flows with session timeout/continuation and no inaccessible
        free-form dependency. Evidence: USSD journey.
  - [ ] V7A.9.d Build consented voice callback request/schedule/attempt/retry/
        answer/opt-out with reviewed voice/pronunciation/speed and keypad
        alternatives. Evidence: voice journey.
  - [ ] V7A.9.e Integrate current telecom template/short code/caller ID/routing/
        delivery/quality/rate/status/callback capabilities with explicit
        unavailable state. Evidence: provider tests.
  - [ ] V7A.9.f Disclose carrier/service charges, consent/purpose/privacy,
        emergency authority, accessibility/fallback, and support before
        commitment. Evidence: policy tests.
  - [ ] V7A.9.g Propagate correction/retraction/emergency recall by canonical ID
        to menu/voice content and prevent stale cached variants. Evidence:
        update tests.
  - [ ] V7A.9.h Minimize phone/session/location/content analytics, aggregate
        with privacy floors, retain briefly, and exclude protected
        source/editorial scoring. Evidence: privacy tests.
  - [ ] V7A.9.i Test session limits/drop/retry/duplicate/locale fallback/
        provider outage/role/consent/current-correction state on representative
        devices. Evidence: feature-phone E2E.
- [ ] V7A.10 Build subscription, payment, tipping, advertising, sponsorship,
      partnership, and entitlement administration in a separate commercial
      surface with newsroom-visible conflict/disclosure projections only.
      Evidence: commercial actors cannot suppress, prioritize, edit, correct, or
      retract editorial content, and payment data never enters drafts.
  - [ ] V7A.10.a Define separate commercial host/service/store/roles/routes/
        credentials/audit and explicit prohibited newsroom capabilities.
        Evidence: boundary architecture.
  - [ ] V7A.10.b Build product/plan/subscription/entitlement/trial/cancel/pause/
        renew/refund/support state with current terms/consent and provider
        reconciliation. Evidence: subscription tests.
  - [ ] V7A.10.c Build payment/tip intent/authorization/capture/failure/refund/
        dispute/payout/receipt/tax handling using tokenized provider data and
        idempotency. Evidence: payment tests.
  - [ ] V7A.10.d Build advertiser/campaign/inventory/creative/targeting/
        schedule/delivery/billing controls with privacy floors and no sensitive
        editorial/source targeting. Evidence: advertising tests.
  - [ ] V7A.10.e Build sponsorship/partnership contracts, deliverables, rights,
        conflicts, separation, disclosures, expiry, and audited exceptions.
        Evidence: partnership tests.
  - [ ] V7A.10.f Project only advertiser/sponsor/partner/conflict/disclosure/
        entitlement eligibility into newsroom workflows through typed redacted
        read models. Evidence: projection tests.
  - [ ] V7A.10.g Enforce newsroom independence server-side: commercial actors/
        metrics cannot assign, rank, suppress, edit, approve, publish, correct,
        retract, or alter gates. Evidence: negative authorization suite.
  - [ ] V7A.10.h Keep card/bank/mobile-money/payment/contact/billing data out of
        drafts/claims/search/logs/analytics/model context and inspect storage/
        network. Evidence: data-boundary tests.
  - [ ] V7A.10.i Reconcile entitlement/revenue/disclosure state across systems,
        handle provider outage/webhook replay/version skew, and preserve
        editorial access to required corrections. Evidence: commercial E2E.
- [ ] V7A.11 Build content, audience, search, recommendation, social, channel,
      subscription, revenue, correction, and public-interest analytics with
      metric definitions, cohort/privacy floors, bot filtering, attribution,
      latency/freshness, experiment governance, and export. Evidence: analytics
      cannot expose protected sources or become an automatic editorial score.
  - [ ] V7A.11.a Define versioned metric/event dimensions,
        numerator/denominator/ units/windows/attribution/source/owner/purpose
        for each named domain. Evidence: metric catalog.
  - [ ] V7A.11.b Instrument allowlisted pseudonymous events with schema/consent/
        purpose/data-class/retention and prohibit protected source/tip/legal/
        draft/payment content. Evidence: event contracts.
  - [ ] V7A.11.c Filter bots/fraud/internal/testing/retries and deduplicate/
        sessionize/attribute across channels with documented uncertainty.
        Evidence: pipeline tests.
  - [ ] V7A.11.d Enforce minimum cohort/noise/suppression/access/field/row/
        purpose/region/export controls and deletion/opt-out propagation.
        Evidence: privacy tests.
  - [ ] V7A.11.e Show ingestion/processing/index/report latency, freshness/
        completeness/unknown state, corrections to metrics, and data-quality
        incidents. Evidence: observability tests.
  - [ ] V7A.11.f Govern experiments with hypothesis/metrics/guardrails/audience/
        consent/randomization/power/duration/stop/review and no high-risk
        editorial manipulation. Evidence: experiment policy.
  - [ ] V7A.11.g Provide dashboards and purpose-bound audited exports with
        semantic labels/units/uncertainty/accessibility and safe small-cell
        behavior. Evidence: analytics UX/export tests.
  - [ ] V7A.11.h Block use of engagement/revenue/audience/source metrics as
        automatic story/reporter/source truth, quality, publish, correction, or
        suppression score. Evidence: editorial independence tests.
  - [ ] V7A.11.i Verify known-value attribution/freshness/bot/privacy/deletion/
        cross-tenant scenarios and reconcile to source systems. Evidence:
        analytics validation.
- [ ] V7A.12 Build SEO, search/recommendation, topic/entity pages, related
      coverage, archive, reading-list/follow, and content-discovery controls
      through versioned editorial policies. Evidence: ranking/metadata changes
      cannot fabricate facts, hide active corrections, or break canonical URLs.
  - [ ] V7A.12.a Define canonical public content/search/discovery document with
        stable URL/ID/version/locale/topic/entity/rights/release/correction/
        retraction/access fields. Evidence: discovery contract.
  - [ ] V7A.12.b Build versioned SEO metadata/structured data/sitemap/canonical/
        hreflang/robots/indexing policies derived from exact releases. Evidence:
        SEO tests.
  - [ ] V7A.12.c Build lexical/semantic search and topic/entity/archive pages
        with explainable facets/sorts/current state/accessibility and no stale
        correction hiding. Evidence: discovery tests.
  - [ ] V7A.12.d Build related/recommendation candidate/filter/rank/diversity/
        freshness/public-interest rules with model/features/policy attribution
        and editorial controls. Evidence: recommendation tests.
  - [ ] V7A.12.e Build consented reading-list/follow/save with privacy,
        entitlement, deletion/export, current correction/retraction notices, and
        no protected source following. Evidence: reader tests.
  - [ ] V7A.12.f Govern policy
        draft/review/approve/experiment/activate/rollback/ expire with impact
        preview and code-owned fact/correction/rights floors. Evidence:
        governance tests.
  - [ ] V7A.12.g Reindex/invalidate caches on publish/update/correction/
        retraction/rights/URL changes and preserve redirects/history/canonical
        identity. Evidence: lifecycle tests.
  - [ ] V7A.12.h Adversarially test fabricated metadata/snippets, correction
        demotion/hiding, broken canonical loops, filter bubbles, protected data,
        and ranking direct-write attempts. Evidence: independence gate.
- [ ] V7A.13 Build live-stream/channel operations for stream health, encoder/
      platform status, concurrent destinations, delay, moderation, Q&A,
      recording/clips, rights, incident, failover, and post-event archive.
      Evidence: operator controls are step-up authorized and editorial claims
      remain traceable.
  - [ ] V7A.13.a Define live channel/session/destination/encoder/feed/status/
        incident/action/recording contracts and exact program/story/rundown
        revision. Evidence: operations schema.
  - [ ] V7A.13.b Integrate encoder/input/output/provider health, bitrate/frame/
        audio/caption/delay/drop/keyframe metrics, heartbeat, and calibrated
        alerts. Evidence: health tests.
  - [ ] V7A.13.c Configure concurrent destinations with credentials/capability/
        rights/locale/schedule/acknowledgment and independent partial state.
        Evidence: destination tests.
  - [ ] V7A.13.d Implement preview/program/delay/fact-check hold/caption/
        translation/moderation/Q&A controls linked to rundown claims/evidence.
        Evidence: live control tests.
  - [ ] V7A.13.e Require step-up/current role/state/impact/confirmation for
        go-live, route change, delay dump, disconnect, emergency stop, failover,
        and restart. Evidence: authorization tests.
  - [ ] V7A.13.f Open incident automatically/manually with synchronized
        telemetry/actions/decisions/comms/evidence and safe escalation/
        postmortem. Evidence: incident tests.
  - [ ] V7A.13.g Record immutable program/clean/caption/transcript feeds and
        create reviewed rights-cleared clips with timecoded claim/source
        lineage. Evidence: recording tests.
  - [ ] V7A.13.h Build post-event archive/metadata/accessibility/rights/
        correction-retraction replacement and per-destination read-back.
        Evidence: archive E2E.
- [ ] V7A.14 Build governed training/evaluation data intake from public and
      licensed corpora, newsroom decisions, corrections, fact-check outcomes,
      language review, channel feedback, and model disagreements. Evidence:
      source protection, consent/license, purpose, minimization, retention,
      deletion, contamination, split integrity, provenance, opt-out, and
      editorial-independence controls precede any training or fine-tuning.
  - [ ] V7A.14.a Define dataset/use-case/version/owner/model-purpose/allowed-
        prohibited content/fields/subjects/sources/licence/consent/retention
        contract. Evidence: dataset policy.
  - [ ] V7A.14.b Intake only approved public/licensed/newsroom-decision/
        correction/fact-check/language/channel-feedback sources through
        quarantine and immutable provenance. Evidence: source ledger.
  - [ ] V7A.14.c Enforce protected source/tip/legal/embargoed/private draft/
        payment/child-sensitive/opted-out exclusions before extraction,
        annotation, export, or provider access. Evidence: exclusion tests.
  - [ ] V7A.14.d Validate licence/consent/purpose/territory/provider/model-use/
        derivative/attribution/deletion rights per item and dataset version.
        Evidence: rights gate.
  - [ ] V7A.14.e Minimize/redact/pseudonymize/deduplicate, detect secrets/PII/
        prohibited content, and retain transformations with hashes. Evidence:
        preprocessing tests.
  - [ ] V7A.14.f Detect cross-source contamination, benchmark memorization,
        near-duplicate train/eval/test leakage, temporal/entity/topic leakage,
        label disagreement, and poisoning. Evidence: integrity tests.
  - [ ] V7A.14.g Define annotation/adjudication/quality/subgroup/locale/split/
        version/reproducibility and prevent production feedback from silently
        entering training. Evidence: curation tests.
  - [ ] V7A.14.h Implement data subject/source/partner opt-out, deletion/
        restriction/licence expiry, derived dataset/model impact trace, and
        legal-hold conflict review. Evidence: deletion tests.
  - [ ] V7A.14.i Require privacy/security/legal/source-protection/editorial/
        model-governance approval with exact manifest, risks, provider terms,
        and expiry before use. Evidence: approval record.
  - [ ] V7A.14.j Prohibit model outputs/feedback/performance from automatically
        changing editorial policy, source scores, corrections, assignments, or
        publish decisions. Evidence: independence tests.
  - [ ] V7A.14.k Reproduce dataset build from immutable inputs/code/config and
        publish lineage, exclusions, counts, quality, split hashes, access, and
        current disposition. Evidence: reproducibility gate.

### V8 — Newsroom UI, accessibility, and resilience

- [ ] V8.1 Build the newsroom hub around assignments, embargoes, gate failures,
      fact-check queue, corrections, and publishing state. Evidence:
      utility-first design review.
  - [ ] V8.1.a Define primary triage questions and live view models for each
        named queue/state with owner/desk/priority/deadline/severity/freshness/
        blocker/eligible action. Evidence: hub contract.
  - [ ] V8.1.b Compose a restrained task-led hierarchy with urgent actionable
        work first, production context second, no decorative hero or repeated
        card mosaic. Evidence: design review.
  - [ ] V8.1.c Implement stable filters/sorts/grouping/search/URLs and live
        updates while redacting protected/embargo/legal details. Evidence: hub
        query tests.
  - [ ] V8.1.d Implement authorized claim/open/assign/escalate/remediate/retry/
        hold/review actions with state/version/reason/impact/confirmation.
        Evidence: action tests.
  - [ ] V8.1.e Render Veritas route
        loading/empty/stale/offline/degraded/partial/ unauthorized/fixture/error
        honestly with accessible recovery. Evidence: state tests.
  - [ ] V8.1.f Verify wide/compact/single-column, keyboard/screen reader, zoom/
        reflow, scale, live updates, and no sensitive count/snippet leaks.
        Evidence: utility-first E2E.
- [ ] V8.2 Build inbox, pitch board, story workspace, source registry, claim
      review, calendar, publishing, corrections, and archive routes. Evidence:
      route integrity.
      <!-- SPEC 2026-09-11: no route tree exists (Phase V header note). Register
      routes only after V0.1.d's host ADR is accepted; before that, components
      and read models may land behind the S7 shell primitives with no page. Do
      not put newsroom routes in the consumer room or the public reader. -->
  - [ ] V8.2.a Register each named route with canonical path/label/keywords/
        host/owner/audience/roles/capabilities/data class and project/story
        context. Evidence: route registry.
  - [ ] V8.2.b Define route-specific loaders/actions/read models/deep-link
        state, breadcrumbs/back paths, and safe unavailable/degraded states.
        Evidence: route contracts.
  - [ ] V8.2.c Reuse existing Studio/global/newsroom shell, identity/search/
        notifications/help/command palette, and remove duplicate navigation or
        mutations. Evidence: shell review.
  - [ ] V8.2.d Enforce direct server authorization and protected/public/
        commercial/operator boundary for every route/field/function. Evidence:
        route auth tests.
  - [ ] V8.2.e Test navigation/direct URL/refresh/back/forward/story switch/role
        revoke/not-found/archived/moved context across web/desktop. Evidence:
        route integrity E2E.
- [ ] V8.3 Use primary editor plus evidence inspector rather than nested card
      dashboards. Evidence: frontend conformance review.
  - [ ] V8.3.a Define shared story workspace shell with primary structured
        editor, contextual evidence inspector, story navigation, status rail,
        selection/focus/dirty/capability state. Evidence: workspace spec.
  - [ ] V8.3.b Compose source/claim/citation/evidence/attestation/media
        inspectors synchronized to selection without nested global shells or
        duplicate canonical data. Evidence: inspector tests.
  - [ ] V8.3.c Keep gate/review/embargo/save/job/publish/correction status and
        blockers/remediation visible without obscuring the article. Evidence:
        status tests.
  - [ ] V8.3.d Use typography, spacing, dividers, disclosure, and progressive
        panes for hierarchy; avoid decorative card grids, gradients, oversized
        titles, and controls in arbitrary containers. Evidence: UI review.
  - [ ] V8.3.e Define wide/compact/single-column layouts with inspector/status
        drawer/tab, stable story identity, safe focus, and no squashed precision
        editor. Evidence: responsive snapshots.
  - [ ] V8.3.f Test long story/claims/sources/live collaboration/protected
        redaction/zoom/keyboard/screen reader. Evidence: frontend conformance
        E2E.
- [ ] V8.4 Preserve story, revision, block, selection, tab, filters, and
      inspector in safe deep links. Evidence: protected links expire and do not
      reveal source identities.
  - [ ] V8.4.a Define versioned allowlisted URL state schema for route/story/
        revision/block/range/tab/filter/sort/inspector/compare/return with size
        and sensitivity limits. Evidence: link schema.
  - [ ] V8.4.b Encode stable opaque IDs and bounded range/filter values while
        excluding source identity/contact, legal notes, embargo secrets,
        excerpts, unsaved text, local paths, and tokens. Evidence: serialization
        tests.
  - [ ] V8.4.c Parse/validate/canonicalize and reauthorize every referenced
        object/field/action; handle stale/deleted/moved/retracted/revoked
        subjects. Evidence: restoration tests.
  - [ ] V8.4.d Use expiring signed capability/deep links only where ordinary
        route authorization is insufficient, with purpose/audience/single-use/
        revocation and no secret in URL. Evidence: protected-link tests.
  - [ ] V8.4.e Restore refresh/back/forward/share/new window/web-desktop context
        and reconcile unsaved/conflicting state safely. Evidence: navigation
        tests.
  - [ ] V8.4.f Test cross-tenant/story/compartment substitution, range/ID/
        encoding/oversize/replay/expiry/log/referrer/history leakage. Evidence:
        adversarial link suite.
- [ ] V8.5 Provide keyboard-first editing, claim attachment, source search,
      review, and publish journeys. Evidence: pointer-free Playwright.
  - [ ] V8.5.a Inventory every focusable/editor/selection/menu/dialog/drag/drop/
        resize/reorder/compare/review/publish operation and expected keyboard
        outcome. Evidence: interaction inventory.
  - [ ] V8.5.b Implement structured-editor navigation/selection/block commands/
        formatting/undo/redo/anchors without trapping standard text or assistive
        technology keys. Evidence: editor keyboard tests.
  - [ ] V8.5.c Implement keyboard claim creation and source search/select/
        locator/citation/counter-source attachment with context/focus return.
        Evidence: grounding journey.
  - [ ] V8.5.d Implement review comment/suggestion/evidence
        inspection/checklist/ decision and non-drag reorder/relationship
        alternatives. Evidence: review journey.
  - [ ] V8.5.e Implement preview/eligibility/signoff/schedule/publish/read-back/
        correction controls with state/impact/confirmation and focus recovery.
        Evidence: publication journey.
  - [ ] V8.5.f Run pointer-free Playwright across wide/compact/mobile review,
        web/desktop, dialogs/errors/conflicts/live updates, and role changes.
        Evidence: keyboard E2E.
- [ ] V8.6 Provide semantic alternatives for evidence graphs, timelines,
      analytics, media, and compare views. Evidence: screen-reader tests.
  - [ ] V8.6.a Define content/operation/selection/focus/status parity contract
        and synchronized state between each visual surface and its semantic
        alternative. Evidence: parity matrix.
  - [ ] V8.6.b Provide evidence graph table/tree/path/forms for entities/claims/
        sources/edges/types/confidence/provenance/retractions and all authorized
        edits. Evidence: graph tests.
  - [ ] V8.6.c Provide timeline ordered/grouped table/forms with exact/
        approximate/uncertain time, events/participants/location/evidence/
        conflicts, and edit/navigation. Evidence: timeline tests.
  - [ ] V8.6.d Provide analytics tables/text summaries with metric definitions,
        numerator/denominator/units/uncertainty/freshness/cohort privacy and
        accessible chart descriptions. Evidence: analytics tests.
  - [ ] V8.6.e Provide media native controls/transcripts/captions/descriptions/
        chapters/timecode/annotation lists and synchronized playback position.
        Evidence: media tests.
  - [ ] V8.6.f Provide compare semantic diff by block/range/claim/citation/
        media/disclosure with before/after/context and decision operations.
        Evidence: compare tests.
  - [ ] V8.6.g Verify keyboard/screen-reader/zoom/search/filter/edit/review
        parity on large data without protected redaction divergence. Evidence:
        semantic E2E.
- [ ] V8.7 Provide status announcements for save, conflict, source update, gate,
      review, embargo, publish, correction, and cascade progress. Evidence:
      announcement assertions.
  - [ ] V8.7.a Define event-to-message taxonomy with priority/politeness,
        dedupe/coalescing, context, units/progress, terminal state, and privacy
        redaction. Evidence: announcement spec.
  - [ ] V8.7.b Announce dirty/saving/saved/offline/error/conflict and semantic
        conflict resolution without noisy autosave repetition. Evidence: save
        tests.
  - [ ] V8.7.c Announce source change/retraction, gate/review status/blocker/
        staleness, embargo time/state, and required remediation. Evidence:
        editorial status tests.
  - [ ] V8.7.d Announce per-target queued/running/published/verified/partial/
        failed and correction/cascade progress with counts and unresolved
        targets. Evidence: release tests.
  - [ ] V8.7.e Preserve current focus/typing; pair announcements with persistent
        visible status/history and avoid exposing protected data. Evidence:
        accessibility/privacy tests.
  - [ ] V8.7.f Assert exact announcement order/count/content for success,
        retries, live updates, reconnect, duplicates, errors, and cancellation.
        Evidence: announcement suite.
- [ ] V8.8 Implement autosave/offline/conflict recovery without caching
      protected-source details in unsafe storage. Evidence: storage inspection.
  - [ ] V8.8.a Define eligible offline content and local operation/draft/
        checkpoint/schema/device/session/encryption/quota/expiry/access state;
        explicitly exclude protected identities/legal secrets. Evidence: cache
        policy.
  - [ ] V8.8.b Implement debounced idempotent autosave with expected revision
        and visible dirty/saving/saved/error/offline/conflict state. Evidence:
        autosave tests.
  - [ ] V8.8.c Store eligible drafts/operations/assets encrypted and partitioned
        by tenant/user/story with logout/revocation/archive/retention cleanup.
        Evidence: storage tests.
  - [ ] V8.8.d Render protected source references only as safe opaque/redacted
        placeholders offline and require online step-up to reveal. Evidence:
        protected-offline tests.
  - [ ] V8.8.e Reauthenticate/re-authorize/rebase/deduplicate on reconnect and
        present mine/theirs/base semantic conflict with copy/export safeguards.
        Evidence: recovery tests.
  - [ ] V8.8.f Inspect browser/desktop storage/cache/index/telemetry/crash/
        backups, then test crash/relaunch/two-client/role revoke/quota/schema
        update. Evidence: storage inspection E2E.
- [ ] V8.9 Implement protected-field copy/download/print controls. Evidence:
      policy and audit tests.
  - [ ] V8.9.a Inventory protected fields/surfaces/actions and resolve current
        role/grant/purpose/device/session/story/compartment/policy constraints.
        Evidence: control matrix.
  - [ ] V8.9.b Prevent selection/copy/cut/clipboard where policy requires,
        sanitize rich/plain representations, and avoid leaking through hidden/
        semantic DOM. Evidence: clipboard tests.
  - [ ] V8.9.c Gate downloads/exports/attachments/originals/proxies with
        step-up, scope/format/redaction/watermark/expiry/rate and signed
        authorization. Evidence: download tests.
  - [ ] V8.9.d Provide print-safe redacted layout or block printing, including
        browser/system preview, PDF, background, headers/URLs, and screenshots
        limitation disclosure. Evidence: print tests.
  - [ ] V8.9.e Handle drag/drop/share/open-external/devtools/accessibility/
        cache/log/crash edge paths without pretending client controls prevent
        all exfiltration. Evidence: threat tests.
  - [ ] V8.9.f Audit attempted/allowed copy/download/print with redacted
        subject/ purpose/result and test revoked/stale/cross-role grants.
        Evidence: policy and audit suite.
- [ ] V8.10 Test 200%/400% zoom, long stories, long locale, RTL, high contrast,
      theme, density, and reduced motion. Evidence: visual/a11y matrix.
  - [ ] V8.10.a Define browser/app/text zoom, viewport, story/source/claim
        volume, locale/script expansion, direction, theme, contrast, density,
        and motion test matrix. Evidence: coverage matrix.
  - [ ] V8.10.b Test 200% and 400% reflow across hub/editor/inspector/dialog/
        calendar/graph/timeline/preview with no clipped/hidden/unreachable
        controls. Evidence: zoom tests.
  - [ ] V8.10.c Test very long stories/blocks/headlines/source names/URLs/
        comments/errors/tables/media transcripts with virtualization and stable
        editing. Evidence: content stress tests.
  - [ ] V8.10.d Test long/CJK/complex-script/RTL/mixed-bidi
        names/quotes/numbers/ locators/timelines/forms/navigation and isolation
        against spoofing. Evidence: locale tests.
  - [ ] V8.10.e Test light/dark/system/high-contrast and compact/comfortable/
        touch density for contrast/focus/status/selection/target size and
        persistent preferences. Evidence: theme/density tests.
  - [ ] V8.10.f Honor reduced motion before first paint; remove parallax/spring/
        large translation/auto pan while preserving textual progress/playback
        state. Evidence: motion tests.
  - [ ] V8.10.g Run visual regression plus automated/manual accessibility on
        supported web/desktop/viewports and close all blocking findings.
        Evidence: visual/a11y report.
- [ ] V8.11 Define performance budgets for editor, evidence sidebar, large
      source registry, claim graph, search, compare, and cascade. Evidence:
      profiling.
  - [ ] V8.11.a Define supported device/browser/desktop/network/data-scale/
        concurrency scenarios and shared load/interaction/save/error/resource
        metrics. Evidence: performance profile.
  - [ ] V8.11.b Define editor open/type/input latency/autosave/diff/history/
        collaboration budgets for long structured stories. Evidence: editor
        budgets.
  - [ ] V8.11.c Define evidence sidebar and source registry query/facet/page/
        selection/update/render/memory budgets with protected authorization
        overhead. Evidence: evidence budgets.
  - [ ] V8.11.d Define claim graph retrieval/layout/semantic view/edit/
        validation and search indexing/query/suggestion/update visibility
        budgets. Evidence: graph/search budgets.
  - [ ] V8.11.e Define compare computation/render/navigation and correction/
        retraction cascade plan/progress/read-back/reconciliation budgets.
        Evidence: workflow budgets.
  - [ ] V8.11.f Profile production-scale fixtures with client/server traces/
        query plans/CPU/memory/network, retain baselines, and fail regressions.
        Evidence: profiling report.
- [ ] V8.12 Build honest fixture, stale, offline, degraded, not-configured,
      unauthorized, partial, and failed states. Evidence: state matrix.
  - [ ] V8.12.a Define canonical data-origin/service-state discriminators,
        timestamps/reasons/safe messages/eligible actions/recovery/telemetry/
        announcements. Evidence: state contracts.
  - [ ] V8.12.b Label fixture/demo/synthetic content persistently and prevent
        publishing, source-quality use, external delivery, or production
        promotion. Evidence: fixture tests.
  - [ ] V8.12.c Show stale subject/source/evidence/gate/review/candidate with
        exact invalidating version and block unsafe continuation. Evidence:
        stale tests.
  - [ ] V8.12.d Show offline cached versus unavailable, queued/prohibited edits,
        freshness/reconnect and protected fields not cached. Evidence: offline
        tests.
  - [ ] V8.12.e Show per-capability
        degraded/not-configured/unauthorized/partial/ failed/unknown with
        completed and unresolved suboperations; no simulated success. Evidence:
        service tests.
  - [ ] V8.12.f Exercise every state/recovery on critical routes and web/
        desktop/responsive projections with identical semantics and privacy.
        Evidence: state matrix E2E.
- [ ] V8.13 Build responsive reporter/reviewer mode while keeping protected
      controls safe. Evidence: mobile/tablet auth and workflow tests.
  - [ ] V8.13.a Define mobile/tablet reporter/reviewer versus precision-edit
        capability matrix, visible data, actions, step-up, device trust, and
        desktop handoff. Evidence: responsive spec.
  - [ ] V8.13.b Build
        assignment/pitch/story/source-redacted/claim/evidence/gate/
        review/calendar/publish/correction status projections with stable
        identity. Evidence: responsive views.
  - [ ] V8.13.c Build capture/note/photo/audio/upload/comment/request-change/
        approve/reject/escalate/hold/cancel actions only where role/state/device
        policy permits. Evidence: action tests.
  - [ ] V8.13.d Present graph/timeline/compare/analytics/media semantic views
        and signed open-on-desktop handoff rather than unusable precision
        editors. Evidence: semantic/handoff tests.
  - [ ] V8.13.e Require step-up/current grant for protected source/legal/
        publish/recall/export controls and prevent unsafe offline cache/copy/
        screenshot claims. Evidence: protected control tests.
  - [ ] V8.13.f Test touch/keyboard/screen reader/zoom/orientation/safe area/
        slow-offline/reconnect/direct authorization/role revoke. Evidence:
        mobile/tablet E2E.
- [ ] V8.14 Register permission-filtered Studio navigation and command palette.
      Evidence: route and role parity.
      <!-- SPEC 2026-09-11: registration goes through the same IA registry Metis
      used (M9.14 shape: `authorize` decides, `visible_to` is written in terms
      of it, mutating palette commands are unconstructible), and must respect
      `release-scope.ts` — a Veritas entry that appears in a V1.0 shell is a
      violation the route-integrity gate fails on. -->
  - [ ] V8.14.a Build canonical route/action registry with owner/host/path/
        labels/keywords/project-story context/roles/capabilities/data class and
        availability. Evidence: navigation manifest.
  - [ ] V8.14.b Register authorized Veritas hub and project/story entries in the
        existing Studio shell without a second global nav or duplicate action.
        Evidence: shell tests.
  - [ ] V8.14.c Filter navigation/palette results before display by current
        tenant/desk/story/role/compartment/entitlement/capability and avoid
        count/ label existence leaks. Evidence: visibility tests.
  - [ ] V8.14.d Preserve context/return path/dirty warnings and run server
        authorization on direct route/command execution. Evidence: action tests.
  - [ ] V8.14.e Compare expected route/role visibility and command execution
        across web/desktop/project/story/protected states. Evidence: parity
        matrix.
  - [ ] V8.14.f Fail CI on authorized orphan, reachable unauthorized item,
        duplicate registry/mutation, stale alias, or unowned route. Evidence:
        reachability gate.
- [ ] V8.15 Add permission-filtered routes or governed deep links for ingestion,
      tips/investigations, entity graph, multimedia/live, newsletters/social/
      messaging, localization, compliance, community/complaints, B2B/USSD,
      commercial projections, and analytics. Evidence: every retained feature is
      reachable by its authorized audience and no protected surface is reachable
      through reader navigation.
  - [ ] V8.15.a Reconcile retained atomic capability ledger to canonical host/
        route/role/audience/project/entitlement/capability and required
        contextual prerequisites. Evidence: reachability manifest.
  - [ ] V8.15.b Register newsroom routes for ingestion, tips/investigations,
        entity graph, multimedia/live, localization, and compliance with
        compartment-aware authorization. Evidence: editorial route tests.
  - [ ] V8.15.c Register channel/community routes for newsletters/social/
        messaging/community/complaints and typed redacted newsroom handoffs.
        Evidence: channel route tests.
  - [ ] V8.15.d Register separately hosted B2B/USSD/commercial/analytics routes
        or governed deep links with current audience/session/scope and safe
        return. Evidence: external surface tests.
  - [ ] V8.15.e Hide protected newsroom capabilities from reader/public/
        commercial navigation and reject direct URLs/commands/fields/functions
        server-side. Evidence: boundary tests.
  - [ ] V8.15.f Exercise positive authorized journey for every retained
        capability and every role/surface plus unauthorized cross-surface
        attempts. Evidence: route coverage.
  - [ ] V8.15.g Fail reconciliation on unreachable authorized feature, exposed
        protected surface, unknown/duplicate route, or handoff without
        redaction/ return contract. Evidence: completeness gate.

### V9 — Veritas verification, operations, and launch

<!-- SPEC 2026-09-11: V9 is bound by §2.5 exactly as M10 was: hosts named per
stage (there is none until V0.1.d), no `page.route` in journeys, real Postgres
created for `VERITAS_DATABASE_URL` plus MinIO/Redis/Elasticsearch from
`docker/docker-compose.dev.yml` (the search profile), model binding per the
Phase V header note (5), human cells packet-only. The 19 `apps/veritas/api`
tests mock the pg pool: they are contract tests, never integration evidence.
`next build && next start`, chromium only, memory checkpoint before every sweep.
-->


- [ ] V9.1 Run canonical contract, editorial state, source-quality,
      counterclaim, attestation, correction, and retraction suites. Evidence:
      current-branch reports.
  - [ ] V9.1.a Validate current/historical story/source/claim/evidence/
        attestation/correction/retraction schemas and version compatibility.
        Evidence: contract report.
  - [ ] V9.1.b Property-test canonical editorial transitions, orthogonal
        embargo/release/remedy states, invalid transitions, concurrency, and
        audit. Evidence: state report.
  - [ ] V9.1.c Run hand-calculated source-quality factors/weights/adjustments/
        bands and missing/unknown/domain cases. Evidence: scoring report.
  - [ ] V9.1.d Run all four counterclaim results across independence/quality/
        consensus/harm/false-balance and contested states. Evidence: balance
        report.
  - [ ] V9.1.e Run attestation scope/credential/conflict/disagreement/quorum/
        expiry/automation-impersonation invariants. Evidence: attestation
        report.
  - [ ] V9.1.f Run chronological prior-text correction and complete ordered
        retraction graph/action/read-back/closure invariants. Evidence: remedy
        report.
  - [ ] V9.1.g Pin reports to current branch commit/contracts/policies/corpora/
        engines and fail any skipped/unreviewed canonical invariant. Evidence:
        suite manifest.
- [ ] V9.2 Run known-value tests for claim, citation, statistical, quote,
      headline, source, balance, evidence, and cascade gates. Evidence: gate
      registry coverage.
  - [ ] V9.2.a Freeze expert-reviewed positive/boundary/negative/adversarial/
        unavailable/indeterminate fixtures with exact expected raw measurements
        and verdicts. Evidence: known-value corpus.
  - [ ] V9.2.b Verify claim extraction/class/severity/anchor and citation
        locator/support/contradiction/reachability gates. Evidence: grounding
        results.
  - [ ] V9.2.c Verify statistical denominator/population/period/method/
        uncertainty/dataset and quote speaker/context/edit/consent checks.
        Evidence: fact-check results.
  - [ ] V9.2.d Verify headline omission/exaggeration/certainty/clickbait, source
        composite, counterclaim/false-balance, and evidence-pack integrity.
        Evidence: editorial results.
  - [ ] V9.2.e Verify correction/retraction dependency/action/idempotency/
        ordering/read-back/closure gates across target fixtures. Evidence:
        cascade results.
  - [ ] V9.2.f Map every retained gate to applicability, fixtures, measurement/
        verdict assertions, remediation, owner, and current result; fail gaps.
        Evidence: gate registry coverage.
- [ ] V9.3 Run calibrated evaluations for extraction, contradiction, bias,
      similarity, and assist models. Evidence: precision/recall, subgroup,
      threshold, and uncertainty report.
      <!-- SPEC 2026-09-11: §2.5.4/§2.5.5. Tasks map to `libs/veritas/claims`
      (extraction), `libs/oshun/domain-veritas/src/contradiction`
      (contradiction), `libs/veritas/bias-detection` (bias — untested today),
      plagiarism/similarity (no implementation found; V4.12 builds it) and
      `libs/veritas/llm` assist. .a is synthetic and says so; .f's review is
      human. -->
  - [ ] V9.3.a Freeze representative held-out corpora/splits/labels for each
        named model task, risk class, desk, locale, subgroup, and adversarial
        condition. Evidence: evaluation manifest.
  - [ ] V9.3.b Pin model/provider/prompt/tool/config/index/code versions and run
        with complete input/output/cost/latency provenance. Evidence: run
        records.
  - [ ] V9.3.c Report task-appropriate precision/recall/F-score/calibration/
        ranking/span/meaning-preservation metrics, confidence intervals, and
        uncertainty/abstention. Evidence: metric report.
  - [ ] V9.3.d Report subgroup/locale/dialect/desk/source-kind/risk differences
        and severity-weighted false-positive/negative error analysis. Evidence:
        slice report.
  - [ ] V9.3.e Test prompt/source injection, fabricated/washed citations,
        protected leakage, correlated handoff errors, and misuse boundaries.
        Evidence: adversarial report.
  - [ ] V9.3.f Compare prior approved baseline, enforce per-severity thresholds/
        stop-the-line, assign mitigation/rollback, and obtain named review.
        Evidence: signed evaluation.
- [ ] V9.4 Run real-database, secure-store, blob, search, queue, CMS, and
      distribution integration suites. Evidence: durable round-trip.
      <!-- SPEC 2026-09-11: .a's environment: `createdb veritas_test` on the
      compose Postgres, `prisma migrate deploy` from `libs/veritas/database`,
      MinIO bucket per tenant, Elasticsearch from the compose search profile,
      Redis; the "secure store" for protected sources does not exist yet (V6.5
      builds it) — until then .c records the gap rather than testing a stand-in.
      Report passed/skipped/failed per suite. -->
  - [ ] V9.4.a Provision production-equivalent supported service versions with
        two tenants, protected compartments, representative records/blobs/
        indexes/jobs/targets, and isolated credentials. Evidence: environment
        manifest.
  - [ ] V9.4.b Exercise canonical write→outbox→blob→index→gate/review→queue→CMS/
        distribution→read-model flow with trace correlation. Evidence:
        integration trace.
  - [ ] V9.4.c Verify secure-store grants/revocation/redacted projections and no
        protected data in ordinary DB/index/queue/log/target systems. Evidence:
        compartment tests.
  - [ ] V9.4.d Inject transaction/blob/index/queue/CMS/target timeout/outage/
        duplicate/order/auth/version failures and prove idempotent recovery.
        Evidence: fault tests.
  - [ ] V9.4.e Reconcile authoritative records/blobs/indexes/jobs/CMS/public/
        distribution state after create/update/correction/retraction/archive/
        restore. Evidence: reconciliation report.
  - [ ] V9.4.f Restart/replay/backup/restore and verify durable IDs/hashes/
        provenance/audit plus tenant isolation. Evidence: durable round-trip.
- [ ] V9.5 Run NewsML-G2 golden validation for initial, update, correction,
      package, planning, rights, and embargo scenarios. Evidence: pinned
      validator.
      <!-- SPEC 2026-09-11: same gate as V1.14.f/V6.10.e (§2.5.6): vendored IPTC
      XSDs + one independent reader; goldens under
      `libs/contracts/src/veritas/goldens/newsml/`. -->
  - [ ] V9.5.a Pin NewsML-G2/profile/schema/code-list/validator versions and
        canonical deterministic generator inputs. Evidence: validator manifest.
  - [ ] V9.5.b Validate initial/update/replacement/correction/retraction News
        Items for identity/version/status/prior links/content metadata.
        Evidence: News Item goldens.
  - [ ] V9.5.c Validate ordered/related Package Items and Planning Items for
        assignments/windows/embargoes without protected-source leakage.
        Evidence: package/planning goldens.
  - [ ] V9.5.d Validate rights/territory/language/syndication/subjects/concepts/
        media associations and supported extensions. Evidence: metadata goldens.
  - [ ] V9.5.e Test unknown/invalid codes, missing required fields, time zones,
        unsupported target loss, malformed/cross-version inputs. Evidence:
        negative corpus.
  - [ ] V9.5.f Round-trip through supported consumers, semantic-compare, and
        retain current-branch validation artifacts. Evidence: interoperability
        report.
- [ ] V9.6 Run C2PA valid, tampered, stripped, unknown-trust, and ingredient
      lineage tests. Evidence: validation reports.
      <!-- SPEC 2026-09-11: run `c2pa-node@0.5.26` as the validator with a
      harness-generated trust store; .a pins the SDK version and certificate
      fingerprints; "stripped" is proven by removing the JUMBF box from real
      bytes. -->
  - [ ] V9.6.a Pin C2PA/profile/SDK/algorithm/trust-store/certificate/revocation
        versions and representative media/package manifests. Evidence: test
        manifest.
  - [ ] V9.6.b Generate/sign/validate supported original and derived media with
        exact ingredient/edit/AI/action/package lineage. Evidence: valid report.
  - [ ] V9.6.c Tamper bytes/manifest/assertions/ingredient links/signatures/
        timestamps/content binding and assert precise integrity failure.
        Evidence: tamper report.
  - [ ] V9.6.d Strip manifests or use unsupported/malformed data and verify
        provenance-absent/unknown behavior, not false or verified. Evidence:
        absence tests.
  - [ ] V9.6.e Test unknown/untrusted/expired/revoked signer and trust-store/
        network/status uncertainty. Evidence: trust tests.
  - [ ] V9.6.f Verify renderer/gate/API/export/CMS display parity and
        correction/ replacement lineage across valid and failure states.
        Evidence: validation reports.
- [ ] V9.7 Run BFF auth, object/property authorization, idempotency,
      concurrency, pagination, rate, SSRF, and tenant tests. Evidence: route
      inventory complete.
      <!-- SPEC 2026-09-11: three route surfaces, all in scope: BFF
      `/v1/veritas/*` (`routes/veritas-briefing.ts`,
      `veritas/retraction-cascade-runtime.ts`, `routes/domain-stubs.ts`, gated
      by `middleware/release-scope.ts`), `apps/veritas/api` (Hono, JWT without
      roles), and the CMS (no auth). .a generates the inventory from the served
      routers; .b will be red on the CMS by construction and that red is the
      finding. -->
  - [ ] V9.7.a Generate complete route/method/operation/auth/scope/object/field/
        function/data-class/idempotency/pagination/rate/external-fetch
        inventory. Evidence: route manifest.
  - [ ] V9.7.b Test unauthenticated/expired/revoked/stale/session-fixation/CSRF/
        service-account/emergency/step-up cases. Evidence: authentication tests.
  - [ ] V9.7.c Test
        BOLA/property/mass-assignment/filter/sort/count/snippet/batch/
        export/direct-function access across roles/tenants/compartments.
        Evidence: authorization tests.
  - [ ] V9.7.d Test duplicate/replayed/conflicting idempotency keys, optimistic
        version races, timeout-after-commit, and concurrent transitions.
        Evidence: mutation tests.
  - [ ] V9.7.e Test stable cursor/page bounds/sort ties/filter auth/large
        inputs, per-principal/resource rates, quotas, and safe errors. Evidence:
        protocol tests.
  - [ ] V9.7.f Test URL import/webhook/callback SSRF/DNS rebinding/redirect/
        private network/credential/scheme/size/time attacks. Evidence: network
        tests.
  - [ ] V9.7.g Reconcile all implemented routes to inventory/OpenAPI and fail
        missing, unknown, untested, or protected-field-exposing operations.
        Evidence: completeness gate.
- [ ] V9.8 Run full-journey Playwright: pitch to grounded CMS publication.
      Evidence: live backing and read-back.
      <!-- SPEC 2026-09-11: there is no host, no pitch UI and no durable CMS
      today; this cell is reachable only after V0.1.d, V6.1, V6.9 and V8.2.
      Until then it stays open with that dependency named, and no consumer-room
      spec may be relabelled as this journey. -->
  - [ ] V9.8.a Create/submit/compare/commission a pitch and build an assignment
        with owner/deadlines/risks/embargo against live services. Evidence:
        pitch trace.
  - [ ] V9.8.b Ingest/quarantine/review real source artifacts and author draft/
        claims/citations/quotes/media/disclosures with immutable provenance.
        Evidence: grounding trace.
  - [ ] V9.8.c Run fact-check/gates/standards/legal/accessibility review, fix a
        failure, and prove exact-revision approval freshness. Evidence: review
        trace.
  - [ ] V9.8.d Create locked release candidate, preview/diagnose targets, sign
        off, schedule, and dispatch through real CMS adapter/job. Evidence:
        release trace.
  - [ ] V9.8.e Read back public content/metadata/media/disclosures/provenance/
        revision and semantic-compare to candidate. Evidence: read-back proof.
  - [ ] V9.8.f Run keyboard/screen-reader responsive journey and negative role/
        stale dependency/partial CMS failure paths. Evidence: Playwright report.
- [ ] V9.9 Run full-journey Playwright: protected source to redacted review.
      Evidence: no identity leak in UI, logs, network, notifications, or audit
      projection.
      <!-- SPEC 2026-09-11: depends on V6.5 (compartmented source APIs) and the
      V0.5 role model; .f's no-leak inspection includes the Elasticsearch index
      and Redis, which the current API writes to without any compartment. -->
  - [ ] V9.9.a Onboard a protected source after authorized step-up/grant and
        store identity/contact/communications only in compartment. Evidence:
        onboarding trace.
  - [ ] V9.9.b Link pseudonymous source/citations/evidence to a story and assign
        ordinary fact-check/standards/legal/source-protection reviews. Evidence:
        editorial trace.
  - [ ] V9.9.c Compare authorized officer and ordinary reviewer projections in
        UI/API/network, including counts/timing/errors/search/sidebar/comments/
        notifications. Evidence: role comparison.
  - [ ] V9.9.d Revoke/expire grant mid-session and verify reveal/cache/copy/
        download/offline/deep-link access closes safely. Evidence: revocation
        tests.
  - [ ] V9.9.e Exercise redacted audit/export and emergency access with higher
        authority/post-review; preserve immutable protected audit. Evidence:
        audit trace.
  - [ ] V9.9.f Inspect client/server logs/traces/analytics/crash reports/queues/
        ordinary DB/index/backups for identity linkage. Evidence: no-leak
        report.
- [ ] V9.10 Run full-journey Playwright: source retraction to verified cascade.
      Evidence: every registered target updated.
      <!-- SPEC 2026-09-11: the cascade already runs end to end for source
      retraction (Phase V header note 3) — start from
      `libs/oshun/veritas-cascade-worker`'s integration tests and drive them
      through the BFF route with real Redis/Postgres; .a's registered consumers
      are V7.15's registry; .e's deliberately failed target is a real registered
      consumer taken offline, not a mocked callback. -->
  - [ ] V9.10.a Publish a story/source/claim fixture to registered CMS/reader/
        search/archive/package/newsletter/social/channel/B2B consumers.
        Evidence: seeded target manifest.
  - [ ] V9.10.b Intake and verify source retraction, preview complete dependency
        graph, approve per-node/target remedy plan, and freeze it. Evidence:
        impact trace.
  - [ ] V9.10.c Execute durable cascade with duplicate/out-of-order/temporary-
        failure injection and observe truthful per-target progress/retry.
        Evidence: saga trace.
  - [ ] V9.10.d Read back each registered consumer semantically for
        state/notice/ content/index/cache/event/version and reconcile. Evidence:
        target proof.
  - [ ] V9.10.e Keep an intentionally failed target unresolved, verify closure
        blocked, repair/retry, then close only after successful read-back.
        Evidence: closure test.
  - [ ] V9.10.f Verify audit/provenance/prior versions/protected redaction and
        keyboard/screen-reader operations. Evidence: cascade Playwright report.
- [ ] V9.11 Run full-journey Playwright: correction and NewsML/CMS update.
      Evidence: reader-visible prior/new version.
      <!-- SPEC 2026-09-11: depends on V6.9, V6.10 and V7.7; read-back in .d is
      an HTTP read of the public reader (`apps/veritas/web`) and of the CMS,
      from separately started processes. -->
  - [ ] V9.11.a Seed a grounded published release with NewsML/CMS/public read-
        back, claims/citations/media, and downstream target identity. Evidence:
        baseline fixture.
  - [ ] V9.11.b Author correction against exact prior public block/text/version,
        affected claims, rationale, notice, locale/target, and approvals.
        Evidence: correction trace.
  - [ ] V9.11.c Generate/validate NewsML update/correction chain and execute
        idempotent CMS update through real adapter. Evidence: standards trace.
  - [ ] V9.11.d Read back prior/new public version, semantic diff, visible
        correction note/timestamps/history/canonical URL and NewsML references.
        Evidence: reader proof.
  - [ ] V9.11.e Test stale prior-text race, correction-of-correction, partial
        target failure/retry, and no historical overwrite. Evidence: edge tests.
  - [ ] V9.11.f Verify accessibility, locale and cache/search/archive
        propagation plus exact candidate/decision/audit lineage. Evidence:
        Playwright report.
- [ ] V9.12 Run WCAG 2.2 AA, keyboard, announcement, and semantic-equivalence
      suites. Evidence: themes/viewports/locales.
      <!-- SPEC 2026-09-11: §2.5.8 — 2.2 tags through the kit policy; .f's
      manual assistive-technology review is not claimable on this Mac and the
      matrix names it as not run. -->
  - [ ] V9.12.a Define critical route/workspace/dialog/action matrix across web/
        desktop/responsive, themes/density, 200–400% zoom, RTL/long locales, and
        assistive technologies. Evidence: coverage plan.
  - [ ] V9.12.b Run automated WCAG rules, name/role/value, landmark/heading/
        form/error, contrast, focus, target size, reflow, and motion checks.
        Evidence: automation report.
  - [ ] V9.12.c Run pointer-free editing/source/citation/review/publish/
        correction/protected-control journeys with logical focus and non-drag
        alternatives. Evidence: keyboard report.
  - [ ] V9.12.d Assert save/conflict/gate/review/embargo/publish/cascade/error
        announcements for content/order/deduplication/privacy. Evidence:
        announcement report.
  - [ ] V9.12.e Compare graph/timeline/analytics/media/diff visual and semantic
        paths for content/operations/selection/status/permissions. Evidence:
        equivalence report.
  - [ ] V9.12.f Run manual supported screen-reader/zoom/high-contrast/RTL review
        and close or block all findings. Evidence: signed matrix.
- [ ] V9.13 Run long-story, source-volume, search, claim-graph, autosave,
      concurrent edit, publish, and cascade performance/load tests. Evidence:
      SLO/budget reports.
      <!-- SPEC 2026-09-11: §2.5.7 — load runs on the Linux dev server; budgets
      are written before measuring; Elasticsearch on the Mac counts against the
      memory checkpoint and is torn down between scenarios. -->
  - [ ] V9.13.a Generate versioned production-scale fixtures for long blocks/
        claims/citations/comments/history, source registry, graph, releases,
        targets, corrections, and collaborators. Evidence: load manifest.
  - [ ] V9.13.b Measure web/desktop startup/story open/editor input/scroll/
        inspector/diff/history/autosave and sustained memory. Evidence: editor
        profile.
  - [ ] V9.13.c Measure source registry/search index/query/facets/pagination/
        update visibility and claim graph load/layout/semantic operations.
        Evidence: discovery profile.
  - [ ] V9.13.d Measure concurrent save/rebase/conflict/offline reconnect under
        multiple clients, network constraints, DB contention, and long sessions.
        Evidence: collaboration profile.
  - [ ] V9.13.e Measure candidate creation/preview/publish/read-back and large
        correction/retraction plan/dispatch/progress/verification/replay.
        Evidence: release profile.
  - [ ] V9.13.f Capture client/server/database/search/queue/target traces and
        CPU/memory/network/storage/query plans, compare to budgets, fail
        regressions. Evidence: SLO report.
- [ ] V9.14 Run source leak, prompt injection, fabricated citation, defamation
      guard, embargo leak, mass publish, auth, tenant, and audit tamper
      adversarial tests. Evidence: zero blockers.
      <!-- SPEC 2026-09-11: known findings to start red on: CMS routes accept
      unauthenticated writes; the API JWT carries no role so every "role" case
      is an "any token" case; the consumer room and `/domains/veritas/*` must
      never render protected fields (they have none today — assert that stays
      true). -->
  - [ ] V9.14.a Probe protected source/tip/legal/embargo identity/content
        through
        UI/API/search/counts/snippets/errors/logs/cache/notifications/exports/
        analytics/model context. Evidence: leakage report.
  - [ ] V9.14.b Inject instructions/active content/confusables through sources,
        documents, datasets, media metadata, comments, tips, provider outputs,
        and multi-agent handoffs. Evidence: injection report.
  - [ ] V9.14.c Attempt plausible fabricated/altered/washed citations, quotes,
        sources, claims, attribution, and evidence-pack hashes. Evidence:
        grounding report.
  - [ ] V9.14.d Exercise
        defamation/privacy/contempt/rights/high-risk/specialist/ false-balance
        guard bypass through draft, import, AI, emergency, target adapters.
        Evidence: harm/legal report.
  - [ ] V9.14.e Attempt embargo/schedule/deep-link/cache leak and bulk/mass/
        replayed publish, notification, correction suppression, and target
        credential abuse. Evidence: operations report.
  - [ ] V9.14.f Attempt
        auth/session/role/service-account/BOLA/property/function/
        tenant/compartment and audit event/hash/signature/order tamper.
        Evidence: authorization/integrity report.
  - [ ] V9.14.g Require zero severity-blocking findings; assign remediation,
        regression tests, owner, and approved expiring exception for lower risk.
        Evidence: adversarial gate.
- [ ] V9.15 Run backup/restore and correction/retraction replay drills.
      Evidence: CMS, search, archive, audit, and distributed targets reconcile.
      <!-- SPEC 2026-09-11: `pg_dump`/`pg_restore` of the Veritas database into
      a second database on the same instance, MinIO `mc mirror`, Elasticsearch
      snapshot to a filesystem repository, and replay through
      `libs/oshun/veritas-cascade-worker`; verify by hashes, not counts; .f's
      approval is human. -->
  - [ ] V9.15.a Define protected/ordinary data/services/backups/encryption/
        retention/region/recovery order/RPO/RTO and target reconstruction
        limits. Evidence: recovery contract.
  - [ ] V9.15.b Restore database/secure store/blob/search/queue/config/key
        references/audit/release artifacts into an isolated environment.
        Evidence: restore trace.
  - [ ] V9.15.c Reconcile records/blobs/indexes/projections/grants/hashes/audit/
        CMS/archive/public/target states and no cross-tenant exposure. Evidence:
        integrity report.
  - [ ] V9.15.d Replay correction/retraction outbox/saga from checkpoints with
        duplicate/out-of-order/partial prior actions and current expected
        versions. Evidence: replay tests.
  - [ ] V9.15.e Independently read back CMS/search/archive/NewsML/channel/B2B
        targets and repair or retain every unresolved mismatch. Evidence: target
        reconciliation.
  - [ ] V9.15.f Measure achieved loss/recovery time, record gaps/unsupported
        external restoration, update runbooks, and obtain operations/security
        approval. Evidence: signed drill report.
- [ ] V9.16 Publish reporter, fact-check, source-protection, legal, corrections,
      publishing, incident, and recovery runbooks. Evidence: game-day.
  - [ ] V9.16.a Publish reporter/research/source-ingest/authoring/offline/
        conflict runbooks with safe diagnostics, remediation, and escalation.
        Evidence: editorial runbooks.
  - [ ] V9.16.b Publish fact-check/gate/evidence/attestation/source-expiry/
        disputed-result runbooks with known-value comparisons. Evidence:
        verification runbooks.
  - [ ] V9.16.c Publish source-protection/tip/grant/revocation/emergency-access/
        suspected leak runbooks without copying protected details. Evidence:
        protection runbooks.
  - [ ] V9.16.d Publish legal/NMC/election/DPA/harm/complaint/hold runbooks with
        named counsel/specialist escalation and human decision boundaries.
        Evidence: compliance runbooks.
  - [ ] V9.16.e Publish correction/retraction/publish/target failure/embargo/
        emergency recall runbooks with read-back and rollback/repair. Evidence:
        distribution runbooks.
  - [ ] V9.16.f Publish security/availability/integrity/AI/channel incident and
        backup/restore/replay/reconciliation runbooks. Evidence: recovery
        runbooks.
  - [ ] V9.16.g Run timed cross-role game-days, revise unclear/unsafe/missing
        steps, verify contacts/access, and obtain named approval. Evidence:
        game-day report.
- [ ] V9.17 Instrument editorial funnel, gate failures, source freshness,
      corrections, cascades, distribution, and safe cost metrics. Evidence:
      privacy review and dashboards.
  - [ ] V9.17.a Define versioned privacy-safe events/metrics for pitch/
        assignment/draft/review/release/remedy stages with denominators,
        timestamps, owners, and no individual productivity score. Evidence:
        funnel contracts.
  - [ ] V9.17.b Instrument gate execution/outcome/reason/severity/remediation/
        retry/staleness without logging protected evidence or model prompts.
        Evidence: gate telemetry.
  - [ ] V9.17.c Instrument source review/freshness/expiry/retraction and
        affected claim workload using opaque IDs/cohort floors. Evidence: source
        telemetry.
  - [ ] V9.17.d Instrument correction/retraction detection-to-closure and per-
        target cascade/read-back/unresolved state. Evidence: remedy dashboards.
  - [ ] V9.17.e Instrument per-target
        publish/distribution/ack/read-back/failure/ rate/latency plus
        model/job/provider cost with currency/unit/budget. Evidence: operations
        dashboards.
  - [ ] V9.17.f Add SLO/quality/incident alerts with thresholds/uncertainty/
        owner/runbook and prohibit volume/engagement/revenue from overriding
        quality. Evidence: guardrail tests.
  - [ ] V9.17.g Validate known-value telemetry, freshness/completeness, access/
        redaction/retention/deletion/two-tenant privacy and obtain review.
        Evidence: privacy-approved observability.
- [ ] V9.18 Produce engineering-readiness evidence. Evidence: all requirements
      map to current proof.
  - [ ] V9.18.a Define required architecture/contracts/migrations/security/
        privacy/safety/accessibility/quality/standards/integration/operations
        evidence with owners/freshness. Evidence: readiness matrix.
  - [ ] V9.18.b Map every retained atomic capability and checklist parent/child
        to implementation, tests/evaluations/reports/runbooks/approvals.
        Evidence: evidence index.
  - [ ] V9.18.c Verify evidence provenance matches candidate commit/build/
        contracts/schema/policies/models/providers/dependencies/config/targets.
        Evidence: provenance check.
  - [ ] V9.18.d Classify missing/stale/failed/waived proof, block required
        cells, and record exception owner/reason/scope/compensation/expiry.
        Evidence: readiness gate.
  - [ ] V9.18.e Reconcile route/capability/gate/consumer/obligation/standard/
        channel inventories with no orphan or unknown rows. Evidence:
        completeness report.
  - [ ] V9.18.f Publish go/no-go summary, unsupported/degraded combinations,
        residual risks, support/rollback, and named decision. Evidence:
        readiness packet.
- [ ] V9.19 Obtain named editorial standards, legal, source-protection,
      accessibility, privacy/security, engineering, and product approvals.
      Evidence: human signoff.
      <!-- SPEC 2026-09-11: §2.5.9 — packet only; every child stays open until a
      named person acts. -->
  - [ ] V9.19.a Define each approver’s exact scope/criteria/required evidence/
        independence/delegate/expiry and nondelegable decisions. Evidence:
        approval policy.
  - [ ] V9.19.b Route immutable candidate/evidence to editorial standards and
        legal/jurisdictional specialists with open findings/exceptions.
        Evidence: editorial/legal decisions.
  - [ ] V9.19.c Route protected-source/tip/nonleakage/emergency-access evidence
        to source-protection authority through secure compartment. Evidence:
        protection decision.
  - [ ] V9.19.d Route WCAG/assistive-technology and privacy/security/DPA/threat/
        adversarial evidence to named specialists. Evidence: assurance
        decisions.
  - [ ] V9.19.e Route architecture/quality/operations/recovery/support and
        product scope/UX/rollout/residual-risk evidence. Evidence:
        engineering/product decisions.
  - [ ] V9.19.f Record approve/reject/request-changes/conditional decisions with
        identity/role/scope/candidate/reason/conditions/time/signature.
        Evidence: human signoff.
  - [ ] V9.19.g Invalidate affected signoffs on commit/evidence/policy/model/
        schema/dependency/config/target change and require renewal. Evidence:
        stale approval tests.
- [ ] V9.20 Obtain named launch approval for newsroom and public correction
      journey. Evidence: human governance record.
      <!-- SPEC 2026-09-11: §2.5.9 — packet only; .c's live smoke is runnable by
      an agent once V9.8 exists, .d–.f are human. -->
  - [ ] V9.20.a Assemble final immutable launch packet with newsroom authoring/
        protected-source/publish and public correction/retraction journey proof.
        Evidence: launch packet.
  - [ ] V9.20.b Verify candidate commit/build/config/migrations/contracts/
        standards/targets/runbooks/monitoring/on-call/rollback/support and open
        risks. Evidence: launch checklist.
  - [ ] V9.20.c Run final live-backed smoke from authorized newsroom entry to
        verified public publish and correction/read-back without fixture data.
        Evidence: launch smoke.
  - [ ] V9.20.d Hold named editorial/governance/engineering/operations/product
        review with independent correction authority and no automation vote.
        Evidence: decision meeting record.
  - [ ] V9.20.e Record go/no-go/conditional decision, exact scope/regions/
        channels/rollout/conditions/owners/times/expiry/rollback triggers.
        Evidence: governance record.
  - [ ] V9.20.f Verify rollout monitoring and ability to stop, recall, correct,
        restore, and communicate; invalidate approval on candidate change.
        Evidence: launch control test.
- [ ] V9.21 Run distinct full journeys for feed/social ingest, anonymous tip to
      protected investigation, entity merge/split, data visualization,
      podcast/video/short-form, live program, newsletter, social/WhatsApp,
      emergency alert, community moderation, multilingual publication, B2B
      client, USSD/voice, and commercial conflict projection. Evidence: no
      generic story-publish test substitutes for channel-specific proof.
      <!-- SPEC 2026-09-11: each journey names its owned live service (Phase V
      header note) and the external provider it needs; a journey whose provider
      has no credential in env-master (WhatsApp, telecom USSD, payment rails,
      social platforms) is recorded as attempted-and-blocked, never simulated to
      green (§2.5.6). .p is the ledger that says which of the fifteen ran for
      real. -->
  - [ ] V9.21.a Run feed/crawl/social-monitor ingest through policy/fetch/
        quarantine/normalize/language/dedupe/cluster/replay with live lineage.
        Evidence: ingestion journey.
  - [ ] V9.21.b Run anonymous submission/receipt/safety triage/protected
        assignment/research/source conversion and prove nonleakage. Evidence:
        tip journey.
  - [ ] V9.21.c Run entity candidate review/merge/redirect then split/
        reallocation/downstream invalidation with protected graph isolation.
        Evidence: entity journey.
  - [ ] V9.21.d Run dataset
        snapshot→query/transform→chart/map/table→methodology/ semantic
        alternative→publish/reproduce/update. Evidence: data-viz journey.
  - [ ] V9.21.e Run podcast/audio script→record/edit/mix/transcript/review/feed
        and timecoded correction propagation. Evidence: podcast journey.
  - [ ] V9.21.f Run video/short-form
        plan→ingest/edit/graphics/captions/rendition/ review→platform/embed and
        replacement. Evidence: video journey.
  - [ ] V9.21.g Run live rundown/preflight/hold/delay/captions/moderation/Q&A/
        failover/stop→record/clip/archive/correct. Evidence: live journey.
  - [ ] V9.21.h Run newsletter
        author/preview/test/audience/consent/schedule/send/
        bounce/archive/unsubscribe/correct. Evidence: newsletter journey.
  - [ ] V9.21.i Run social and WhatsApp plans through approval/publish/delivery/
        moderation or reply/unsubscribe/rate/correction with current APIs.
        Evidence: social/channel journeys.
  - [ ] V9.21.j Run emergency alert authority→preview→deduplicated multi-channel
        delivery→acknowledgment→recall/correction→post-event review. Evidence:
        emergency journey.
  - [ ] V9.21.k Run comment/report/moderate/appeal/legal hold/transparency and
        story correction context synchronization. Evidence: community journey.
  - [ ] V9.21.l Run multilingual translation/transliteration/media/captions/
        factual parity/cultural review/publish/read-back/cascade. Evidence:
        locale journey.
  - [ ] V9.21.m Run B2B client/key/scope/quota/feed/search/webhook/correction/
        revocation/SDK conformance with no protected fields. Evidence: B2B
        journey.
  - [ ] V9.21.n Run USSD menu and voice callback through consent/locale/session/
        provider/fallback/charge/emergency/correction state. Evidence:
        feature-phone journey.
  - [ ] V9.21.o Run commercial subscription/payment/ad/sponsorship conflict and
        newsroom redacted disclosure projection while denying editorial
        mutations. Evidence: commercial journey.
  - [ ] V9.21.p Verify every journey uses owned live services, target read-back,
        trace/audit, roles/tenants, failure recovery, and current correction
        identity; retain distinct evidence. Evidence: journey coverage ledger.
- [ ] V9.22 Run NMC complaint, election-phase, DPA subject-request, vulnerable
      person/child, graphic-content, right-of-reply, advertising disclosure,
      payment, and emergency-broadcast adversarial/compliance suites. Evidence:
      current jurisdictional profile and named specialist review.
      <!-- SPEC 2026-09-11: the code-owned floors are
      `apps/veritas/api/src/domain/compliance/{nmc,election}.ts` and
      `routes/v1/{compliance,election-coverage,data-protection}.ts`; fixtures
      exercise those, .a's jurisdiction profile carries a version and open
      questions, .k is human. -->
  - [ ] V9.22.a Pin current Ghana and applicable channel/payment jurisdictional
        profiles/versions/effective dates/authoritative sources and specialists.
        Evidence: compliance manifest.
  - [ ] V9.22.b Run NMC complaint intake/SLA/investigation/human decision/
        correction-apology-right-of-reply/escalation/appeal. Evidence: NMC
        suite.
  - [ ] V9.22.c Run pre-election/campaign/silence/polling/results/post-election
        phase fixtures for equal treatment/source verification/ads/incitement/
        misinformation. Evidence: election suite.
  - [ ] V9.22.d Run DPA access/correct/delete/restrict/object/portable discovery
        and remedy across ordinary/protected/channel/commercial/processors plus
        legal-hold conflict. Evidence: DPA suite.
  - [ ] V9.22.e Run child/vulnerable/trauma/location/doxxing/suicide/self-harm
        consent/minimization/specialist/distribution/correction fixtures.
        Evidence: vulnerable-person suite.
  - [ ] V9.22.f Run graphic-content media/thumbnail/autoplay/warning/age/access/
        notification/search/social/locale handling and redacted preview.
        Evidence: graphic-content suite.
  - [ ] V9.22.g Run right-of-reply authenticity/scope/deadline/editorial review/
        publication/notice/appeal without direct external mutation. Evidence:
        reply suite.
  - [ ] V9.22.h Run advertising/sponsorship identity/separation/disclosure/
        targeting/phase/target and attempted commercial interference. Evidence:
        advertising suite.
  - [ ] V9.22.i Run payment/subscription/tip consent/charge/tokenization/
        idempotency/refund/dispute/privacy and editorial-data separation.
        Evidence: payment suite.
  - [ ] V9.22.j Run emergency-broadcast activation/authority/quiet override/
        template/target/delivery/recall/false-alarm review and attempted
        ordinary campaign bypass. Evidence: emergency suite.
  - [ ] V9.22.k Require named legal/editorial/privacy/safety/accessibility/
        commercial/channel specialist review and zero unmitigated blockers.
        Evidence: signed compliance report.
- [ ] V9.23 Run correction/retraction propagation across reader web/mobile,
      search/recommendation, newsletter, social, WhatsApp, push/email, live
      archive/clips, community context, B2B APIs/webhooks, USSD/voice,
      analytics, and commercial placements. Evidence: every registered consumer
      proves read-back or remains visibly unresolved.
      <!-- SPEC 2026-09-11: the registered-consumer set is V7.15's registry;
      today it holds the Sophia re-grounder, the Metis revalidator and the BFF
      acknowledgement store. A consumer with no live target (WhatsApp, USSD, B2B
      webhooks without a client) is "visibly unresolved" by the cell's own
      clause, and .l blocks closure on it — that is the expected end state until
      those channels exist. -->
  - [ ] V9.23.a Seed one versioned story/claim/source/media/package into every
        registered supported consumer and record target IDs/versions/URLs.
        Evidence: propagation baseline.
  - [ ] V9.23.b Author/approve correction and source/claim retraction scenarios
        with exact dependency graph, desired target states, notices, and
        immutable plan. Evidence: remedy inputs.
  - [ ] V9.23.c Verify reader web/mobile, canonical/locale/history/cache and
        search/recommendation/topic/entity/related surfaces update. Evidence:
        reader/discovery read-back.
  - [ ] V9.23.d Verify newsletter archive/follow-up and social/WhatsApp/push/
        email update/delete/follow-up/recall capability-specific state.
        Evidence: messaging read-back.
  - [ ] V9.23.e Verify live recording/archive/clips/transcript/captions and
        podcast/video/article embeds carry corrected
        ranges/notices/replacements. Evidence: media read-back.
  - [ ] V9.23.f Verify community thread context/labels and complaint/feedback
        references update without rewriting community speech. Evidence:
        community read-back.
  - [ ] V9.23.g Verify B2B feeds/search/APIs/webhooks and developer
        cache/version expose current state and signed correction/retraction
        events. Evidence: B2B read-back.
  - [ ] V9.23.h Verify USSD/voice menu/script/cache and callbacks present
        current canonical story/claim/remedy state. Evidence: feature-phone
        read-back.
  - [ ] V9.23.i Verify analytics dimensions/metrics annotate or recompute
        corrected/retracted content and do not preserve false editorial scores.
        Evidence: analytics reconciliation.
  - [ ] V9.23.j Verify advertising/sponsorship/partnership/commercial placements
        stop or update links/content/disclosures without vetoing remedy.
        Evidence: commercial reconciliation.
  - [ ] V9.23.k Inject duplicate/out-of-order/outage/auth/version/unsupported/
        cache-lag failures, retry safely, and retain precise per-target state.
        Evidence: fault report.
  - [ ] V9.23.l Reconcile registered expected versus observed consumers and
        block closure until every required target proves semantic read-back or
        remains visibly owned/escalated/unresolved. Evidence: cascade gate.
- [ ] V9.24 Run training/evaluation-data provenance, license/consent,
      protected-source exclusion, contamination, deletion/opt-out, split
      leakage, poisoning, feedback manipulation, and reproducibility tests.
      Evidence: newsroom or audience data cannot enter a model pipeline merely
      because it exists in Veritas.
  - [ ] V9.24.a Build approved/denied/boundary fixtures across public/licensed/
        newsroom/audience/channel/protected/legal/embargoed/payment/child data
        and purposes. Evidence: policy corpus.
  - [ ] V9.24.b Verify item→source revision→licence/consent/purpose→transform/
        annotation→dataset/split→model/evaluation lineage with hashes/versions.
        Evidence: provenance tests.
  - [ ] V9.24.c Test protected-source/tip/legal/private-draft/payment/opted-out/
        disallowed licence/expired right exclusion at query/export/job/provider
        boundaries. Evidence: exclusion suite.
  - [ ] V9.24.d Detect near/exact duplicates, benchmark or production
        contamination, entity/topic/time/train-eval-test leakage, label leakage,
        and memorization canaries. Evidence: contamination report.
  - [ ] V9.24.e Execute deletion/withdrawal/opt-out/licence expiry/restriction
        through datasets/caches/providers/derived artifacts/model impact while
        handling legal hold. Evidence: deletion journey.
  - [ ] V9.24.f Inject poisoned labels/content/metadata and manipulated
        feedback/ brigading/bot/reward signals; verify isolation, detection,
        review, and no automatic ingestion. Evidence: poisoning tests.
  - [ ] V9.24.g Rebuild exact dataset/splits from immutable inputs/code/config/
        seeds, compare manifests/counts/hashes, and disclose nondeterminism.
        Evidence: reproducibility report.
  - [ ] V9.24.h Attempt unapproved direct DB/search/log/analytics/export and
        model-training job access simply because data exists; prove denied/no
        side effect. Evidence: purpose authorization tests.
  - [ ] V9.24.i Require current privacy/security/legal/source-protection/
        editorial/model-governance approval and fail stale/unlicensed/
        contaminated/unreproducible candidates. Evidence: data release gate.
- [ ] V9.25 EVE-SEAM-EXIT: require "required Eve reads and writes admitted with
      source and behavior evidence, or an exact task-0.8 explicit non-goal
      proves zero operations; a named defer preserves ownership but never passes
      completion" before this phase exits.
      Evidence: source-derived totality record and passing verifier.
      <!-- SPEC 2026-09-11: "task-0.8" is
      `EVE_SOTA_GAP_CLOSURE_TODOS_2026-09-01.md` 0.8 (the ratified charter
      workflow inventory); the totality verifier is
      `tools/eve-everywhere/generate-contextual-invocation-totality.mjs`, and
      the Metis admission gates
      (`tools/eve-everywhere/generate-metis-mutation-admission.mjs`,
      `verify-metis-view-admission.test.mjs`) are the precedent for what an
      admitted read/write looks like. Phase M carried no such cell, so this is
      the first domain to exit through it — do not carry a Metis disposition
      forward. -->
  - [ ] V9.25.a Re-run the route, bridge, state-signal, and complete domain-code
        envelope census; review every changed candidate instead of carrying a
        stale phase disposition forward. Evidence: current ratchets.
  - [ ] V9.25.b Bind the exact ratified task-0.8 workflow and requirement set;
        classify it as a required seam or an exact source-backed non-goal.
        Evidence: charter-bound disposition set.
  - [ ] V9.25.c For a required seam, admit both read and write operations with
        byte-pinned source and behavior evidence. A named defer retains its owner
        and unblock condition but fails completion. Evidence: operation proofs.
  - [ ] V9.25.d Permit a zero-operation boundary only for an exact ratified
        task-0.8 explicit non-goal; absence or unavailability is not a non-goal.
        Evidence: source-bound non-goal proof.
  - [ ] V9.25.e Run the totality verifier, every retained gate-case CLI, and the
        injected durable-candidate control; retain red and green receipts.
        Evidence: release gate logs.

---

## Phase E — Euterpe music production workbench

Euterpe already has a substantial DAW in `apps/euterpe/studio-web`; this phase
must extend and integrate it rather than create a second music editor. The
complete workflow spans concept, score, lyrics, arrangement, recording,
performance, mix, master, rights, provenance, release, distribution, and catalog
operations.

### E0 — Product boundary, existing DAW, and professional workflow

- [ ] E0.1 Ratify Euterpe's product host and decide explicitly whether it is
      outside or inside the Oshun V1 shell. Evidence: product/architecture ADR.
  - [x] E0.1.a Inventory existing Studio web/desktop/native entry points,
        identity/project contexts, navigation shells, deployment boundaries, and
        audience. Evidence: host inventory.
        <!-- evidence: euterpe-host-inventory.json — studio-web is a DUAL host: a
                                                                                                                                                                                                                                                                        Next.js app-router web frontend (3 routes, 1 nav shell) wrapped by a Tauri
                                                                                                                                                                                                                                                                        desktop shell (productName "Euterpe Studio", id app.oshun.euterpe-studio,
                                                                                                                                                                                                                                                                        4 native #[command] handlers load/save_project/export_stems/reveal_in_file_manager,
                                                                                                                                                                                                                                                                        1 capability file), single Nx project (targets lint/test). audience +
                                                                                                                                                                                                                                                                        inside-vs-standalone are the E0.1.b-e semantic half. generate-euterpe-host-
                                                                                                                                                                                                                                                                        inventory.mjs + 5 tests. -->
  - [ ] E0.1.b Evaluate inside-shell versus standalone host for audio lifecycle,
        permissions, latency, offline/native/plugin needs, ownership, and
        operational isolation. Evidence: option analysis.
  - [ ] E0.1.c Decide canonical host(s), route/deep-link/handoff boundaries,
        data ownership, shell reuse, and prohibited duplicate editor behavior.
        Evidence: target architecture.
  - [ ] E0.1.d Define migration/redirect/session/context/telemetry/support/
        rollout/rollback consequences for the chosen boundary. Evidence:
        delivery plan.
  - [ ] E0.1.e Obtain product/audio/architecture/security/accessibility
        ratification with alternatives and residual risks. Evidence: approved
        ADR.
- [ ] E0.2 Inventory every Euterpe Studio route, DAW module, component, test,
      API, Tauri/native bridge, and service dependency. Evidence:
      route-capability matrix.
      <!-- partial: route inventory (a) + module inventory (b) done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/euterpe-inventory.json. c-e (service dependency
                                                                                                                                                                                                                                                                      inventory, test-coverage ledger, route-capability reconciliation) are
                                                                                                                                                                                                                                                                      design/human and stay unchecked. -->
  - [x] E0.2.a Enumerate routes/navigation/commands/pages/dialogs and user/
        operator journeys across web and installed desktop. Evidence: route
        inventory.
        <!-- evidence: .routes — the 3 Euterpe Studio App-Router pages (/, /daw,
                                                                                                                                                                                                                                                                        /realtime) with source + test. The DAW is a small route surface over a
                                                                                                                                                                                                                                                                        large module surface (E0.2.b). generate-euterpe-inventory.mjs + 6 tests. -->
  - [x] E0.2.b Enumerate DAW engines/modules/worklets/workers/components/state/
        commands/assets/device/plugin/native bridge surfaces. Evidence: module
        inventory.
        <!-- evidence: .moduleInventory — 148 engine/logic modules + 58 components
                                                                                                                                                                                                                                                                        across 6 subsystems (daw=135 modules + 150 tests, plus components/identity-
                                                                                                                                                                                                                                                                        billing/realtime-mrt2/shell-runtime/telemetry-launch); nativeSignals detect
                                                                                                                                                                                                                                                                        worklet/worker/Tauri-bridge (0 in-app AudioWorklet processors or Web Workers —
                                                                                                                                                                                                                                                                        the audio engine lives outside the app shell; 1 Tauri-bridge file). -->
  - [ ] E0.2.c Enumerate APIs/realtime protocols/stores/blobs/search/queues/
        providers/distributors and service owners. Evidence: dependency
        inventory. <!-- design: service-owner mapping is semantic. -->
  - [ ] E0.2.d Map unit/integration/E2E/audio-golden/device/compatibility/
        security tests to each retained capability. Evidence: test coverage
        ledger.
  - [ ] E0.2.e Reconcile routes to capabilities/owners/hosts/data classes and
        flag duplicate/orphan/dead/fixture/unsafe paths. Evidence:
        route-capability matrix.
- [ ] E0.3 Map all `libs/euterpe/*` packages to workbench capabilities,
      services, consumer products, or out-of-scope. Evidence: complete ledger.
  - [x] E0.3.a Inventory every Euterpe package/export/audio consumer/runtime/
        test/documented purpose and dependency direction. Evidence: package
        <!-- evidence: .domains.euterpe.packages — 49 libs/euterpe/* packages with exports/consumer/dependency/test/disposition/direction. euterpe libs-sourced caps=0 (DAW logic lives in apps/euterpe — see E0.2). evidence/v1-workbenches/domain-package-inventories.json (254 pkgs; per-package exports/consumers/deps/disposition/tests + capability catalog with exact source locations). -->
        inventory.
  - [ ] E0.3.b Map retained exports to atomic production capabilities, canonical
        contracts, owning host/service, and accountable team. Evidence:
        capability mapping.
  - [ ] E0.3.c Classify consumer/conservatory/discovery/legacy/demo/duplicate/
        dead code with keep/merge/migrate/deprecate/remove/out-of-scope
        disposition. Evidence: disposition ledger.
  - [ ] E0.3.d Verify every package/export/consumer has one reviewed disposition
        and no forbidden dependency crosses product or native security
        boundaries. Evidence: completeness gate.
- [ ] E0.4 Classify existing DAW behavior as live, local-only, fixture-backed,
      service-backed, provider-gated, or incomplete. Evidence: browser/network
      walkthrough.
  - [ ] E0.4.a Build capability/scenario inventory for arrange/session/score/
        mixer/devices/media/rights/release across browser and desktop. Evidence:
        walkthrough plan.
  - [ ] E0.4.b Observe UI/network/storage/audio worklets/workers/native IPC/
        provider calls and trace authoritative state and side effects. Evidence:
        trace captures.
  - [ ] E0.4.c Classify each behavior by exact backing/runtime/feature flag/
        configuration/dependency and distinguish partial from live. Evidence:
        implementation ledger.
  - [ ] E0.4.d Exercise loading/empty/offline/not-configured/degraded/failure/
        permission/device-loss paths and identify simulated success. Evidence:
        state walkthrough.
  - [ ] E0.4.e Reconcile findings to code/tests/docs and assign remediation/
        owner/evidence freshness. Evidence: reviewed classification.
- [ ] E0.5 Define composer, lyricist, arranger, performer, recording engineer,
      editor, mix engineer, mastering engineer, producer, rights manager,
      label/publisher, distributor, reviewer, and administrator roles. Evidence:
      permission matrix.
  - [ ] E0.5.a Define duties and least-privilege object/field/function rights
        for every named role across project through catalog/remedy. Evidence:
        role definitions.
  - [ ] E0.5.b Define organization/project/song/session/release/catalog/
        territory scopes, delegation, guest, service account, and temporary
        grants. Evidence: scope model.
  - [ ] E0.5.c Define edit/record/render/device/plugin/review/approve/rights/
        deliver/takedown/admin rights by maturity state and host capability.
        Evidence: permission matrix.
  - [ ] E0.5.d Define separation of composition/mastering/rights/label/
        distribution approvals, conflicts/recusal, quorum, and nondelegable
        human actions. Evidence: SoD policy.
  - [ ] E0.5.e Verify client projection and server/native enforcement across
        positive/negative/cross-project/revoked/offline/direct command cases.
        Evidence: authorization suite.
- [ ] E0.6 Define project, song, composition, arrangement, session, recording,
      mix, master, release, and catalog maturity models separately. Evidence:
      state diagrams.
  - [ ] E0.6.a Define independent state vocabularies, actors, entry/exit
        criteria, terminal/archival conditions, and allowed transitions for each
        aggregate. Evidence: state definitions.
  - [ ] E0.6.b Define exact dependency relationships without collapsing creative
        version, technical readiness, rights approval, delivery, and catalog
        state into one status. Evidence: dependency model.
  - [ ] E0.6.c Define transition commands/guards/side effects/audit/invalidation
        for draft/review/approved/locked/released/replaced/taken-down flows.
        Evidence: transition tables.
  - [ ] E0.6.d Map existing DAW/API/storage/distributor states and ambiguous/
        unknown/in-flight historical values. Evidence: compatibility matrix.
  - [ ] E0.6.e Property-test valid/invalid/concurrent/stale transitions and
        cross-model invariants. Evidence: state diagrams and tests.
- [ ] E0.7 Define supported browser, desktop/Tauri, audio device, MIDI device,
      sample rate, channel, plugin, and offline configurations. Evidence:
      compatibility matrix.
  - [ ] E0.7.a Define supported OS/browser/desktop/app/runtime/CPU/architecture/
        memory/storage/GPU combinations and lifecycle. Evidence: host matrix.
  - [ ] E0.7.b Define audio
        API/driver/device/input-output/channel/buffer/sample-
        rate/bit-depth/clock/hot-plug configurations and limits. Evidence: audio
        matrix.
  - [ ] E0.7.c Define MIDI 1/2, MPE, control-surface, sync/clock,
        port/transport, permission, and reconnect combinations. Evidence: MIDI
        matrix.
  - [ ] E0.7.d Define native/browser plugin formats/architectures/signature/
        sandbox/licence/UI/state/sidechain/latency support and exclusions.
        Evidence: plugin matrix.
  - [ ] E0.7.e Define online/offline/local/remote/provider/render-worker feature
        availability, storage quotas, graceful degradation, and export escape.
        Evidence: offline matrix.
  - [ ] E0.7.f Publish supported/conditional/unsupported cells with known loss,
        evidence, owner, version, expiry, and no silent fallback. Evidence:
        compatibility profile.
- [ ] E0.8 Define collaboration, latency, clock, sync, and ownership boundaries
      between browser audio, realtime services, and stored projects. Evidence:
      architecture diagram.
  - [ ] E0.8.a Define authoritative ownership for media bytes, project graph,
        edit operations, transport, device state, render artifacts, presence,
        and reviews. Evidence: ownership table.
  - [ ] E0.8.b Define browser/desktop audio render/control threads and real-time
        safety boundaries from UI/network/storage/provider work. Evidence:
        runtime diagram.
  - [ ] E0.8.c Define musical time/sample/timecode/tempo clock authority,
        synchronization/drift/latency measurement/compensation, and failover.
        Evidence: timing architecture.
  - [ ] E0.8.d Define collaboration operations/checkpoints/ordering/CRDT or
        locking/conflicts/offline queue/reconnect and transport-leader
        semantics. Evidence: realtime protocol.
  - [ ] E0.8.e Define durable-before-ack save/upload/render/job boundaries and
        recovery/reconciliation after browser/service/native failure. Evidence:
        durability model.
  - [ ] E0.8.f Validate architecture against solo, concurrent, remote recording,
        stage, offline, and render-worker scenarios. Evidence: architecture
        review.
- [ ] E0.9 Threat-model malicious audio/plugins/presets/projects, voice
      impersonation, sample theft, prompt injection, rights fraud, unreleased
      leaks, device permission abuse, desktop bridge, and distribution fraud.
      Evidence: mitigation/test map.
  - [ ] E0.9.a Map actors/assets/trust boundaries/data flows/privileged commands
        across web/audio worklets/native host/plugins/providers/workers/
        distributors. Evidence: threat diagrams.
  - [ ] E0.9.b Model malformed/polyglot/bomb media/project/preset, plugin code/
        license/update/sandbox escape, unsafe DSP/feedback, and parser attacks.
        Evidence: content/code threats.
  - [ ] E0.9.c Model voice/performer impersonation, stolen samples/stems/
        masters, fabricated rights/splits/consent/provenance, and similarity
        evasion. Evidence: identity/rights threats.
  - [ ] E0.9.d Model prompt/source/preset injection, model/tool misuse, provider
        leakage/training, generated malware/audio abuse, and agent privilege
        expansion. Evidence: AI threats.
  - [ ] E0.9.e Model unreleased/contract/voiceprint leakage through search/log/
        cache/collaboration/review links/support/distribution and cross-tenant
        access. Evidence: confidentiality threats.
  - [ ] E0.9.f Model device/microphone/MIDI/OSC permission abuse, desktop IPC/
        filesystem/deep-link/update attacks, mass delivery/takedown suppression,
        and account compromise. Evidence: platform threats.
  - [ ] E0.9.g Assign preventive/detective/recovery controls, residual risk,
        abuse tests, monitoring, owners, and review cadence. Evidence:
        mitigation/test map.
- [ ] E0.10 Define rights and privacy classifications for demos, voice prints,
      stems, sessions, performer data, contracts, splits, and unreleased
      masters. Evidence: retention/access rules.
  - [ ] E0.10.a Inventory named data plus derivatives/proxies/fingerprints/
        embeddings/logs/caches/backups/exports/provider/model inputs. Evidence:
        data catalog.
  - [ ] E0.10.b Assign sensitivity, rights kind, data subject/owner, purpose/
        lawful basis, project/territory scope, and permitted roles/actions.
        Evidence: classification matrix.
  - [ ] E0.10.c Define encryption/key/region/provider/device/offline/clipboard/
        download/share/watermark and collaboration restrictions. Evidence:
        handling rules.
  - [ ] E0.10.d Define active/archive/contract/legal-hold/backup retention,
        deletion/revocation/takedown, derivative impact, and conflict handling.
        Evidence: lifecycle schedule.
  - [ ] E0.10.e Define redacted projections for search/logs/analytics/
        notifications/support/review/distributors and prohibit hidden training.
        Evidence: projection rules.
  - [ ] E0.10.f Validate representative workflows and obtain rights/privacy/
        security/production approval. Evidence: signed policy.
- [ ] E0.11 Define professional loudness, format, metadata, provenance, and
      distribution profiles by target. Evidence: ratified delivery matrix.
  - [ ] E0.11.a Inventory supported streaming/download/broadcast/podcast/video/
        physical/spatial/distributor targets and current authoritative specs.
        Evidence: target inventory.
  - [ ] E0.11.b Define loudness/true-peak/dynamics/channel/layout/downmix/
        duration/gap/fade constraints and pinned measurement standard per
        target. Evidence: audio profiles.
  - [ ] E0.11.c Define container/codec/sample-rate/bit-depth/dither/naming/
        artwork/lyrics/captions/file-size checks and accepted alternatives.
        Evidence: format profiles.
  - [ ] E0.11.d Define title/version/contributor/identifier/genre/language/
        advisory/territory/rights/pricing/date metadata and DDEX mapping.
        Evidence: metadata profiles.
  - [ ] E0.11.e Define C2PA/external provenance/signing/ingredient/AI disclosure
        requirements and unsupported/stripped states. Evidence: provenance
        profiles.
  - [ ] E0.11.f Pin versions/owners/effective dates, validate goldens with
        distributors, and publish supported/conditional/unsupported delivery
        cells. Evidence: ratified matrix.
- [ ] E0.12 Define success measures for creative latency, glitch rate, save
      durability, collaboration, rights completeness, master compliance,
      delivery acceptance, and release correctness. Evidence: metric spec.
  - [ ] E0.12.a Define
        operation-to-audible-response/input-monitor/transport/edit latency
        distributions by supported host/device/buffer. Evidence: creative
        latency metrics.
  - [ ] E0.12.b Define dropout/underrun/overrun/click/xrun/glitch metrics with
        sampling, false-positive controls, severity, and privacy. Evidence:
        engine quality metrics.
  - [ ] E0.12.c Define acknowledged-save survival, autosave/recovery loss,
        collaboration convergence/conflict/reconnect, and media upload
        durability. Evidence: durability metrics.
  - [ ] E0.12.d Define rights/consent/split/provenance completeness and master
        target/QC conformance without reducing creativity to one score.
        Evidence: compliance metrics.
  - [ ] E0.12.e Define distributor acceptance plus semantic read-back/catalog
        identity/territory/correction/takedown correctness. Evidence: delivery
        metrics.
  - [ ] E0.12.f Define units/denominators/windows/slices/uncertainty/budgets/
        alerts/owners and balanced quality guardrails. Evidence: metric spec.
- [ ] E0.13 Author and ratify `V1/EUTERPE_MUSIC_WORKBENCH_DESIGN_2026-07-23.md`
      with current-state, product-host decision, DAW/audio architecture, data,
      APIs, rights, standards, UI, migration, rollout, and decision records.
      Evidence: approved doc and resolved review comments.
  - [ ] E0.13.a Document current routes/modules/engine/native/packages/services/
        formats/workflows/tests, backing state, constraints, and gaps. Evidence:
        current-state chapter.
  - [ ] E0.13.b Document chosen host/ownership/roles/capability profiles and
        DAW/ audio/realtime/device/plugin/offline/render architecture. Evidence:
        target architecture chapters.
  - [ ] E0.13.c Document contracts/stores/APIs/events/jobs/providers/
        distributors, compatibility/migrations, and recovery. Evidence: platform
        chapters.
  - [ ] E0.13.d Document rights/consent/splits/provenance/AI/safety/privacy/
        standards/target profiles and approval boundaries. Evidence: governance
        chapters.
  - [ ] E0.13.e Document extension of existing DAW, task-led operations UI,
        responsive/accessibility/semantic/offline/performance states. Evidence:
        experience chapter.
  - [ ] E0.13.f Document rollout/rollback/observability/runbooks/verification/
        evidence/launch criteria and all decision alternatives/owners/dates.
        Evidence: delivery chapters.
  - [ ] E0.13.g Resolve every substantive review comment and obtain named
        product/music/audio/rights/security/accessibility/engineering
        ratification. Evidence: approved design.
- [ ] E0.14 Reconcile every `libs/euterpe/*` package, Studio route/component,
      Web Audio/native/Tauri operation, provider, agent, realtime protocol,
      workflow, test, documented feature, and distribution target at
      atomic-feature level. Evidence: explicit dispositions cover foundations/
      theory, composition/score/lyrics, instruments/effects/samples, recording/
      DAW, AI audio/voice, mix/master, spatial, stage/realtime, podcast/video,
      rights/provenance, marketing/discovery, distribution/catalog, learning,
      and operations.
  - [x] E0.14.a Inventory code/routes/components/commands/audio/native/provider/
        agent/protocol/jobs/tests/docs/flags/deployed integrations and targets.
        Evidence: reconciliation inputs.
        <!-- evidence: euterpe-code-census.json — comprehensive launch census: 49 libs
                                                                                                                                                                                                                                                                        packages + 2 app projects, 918 code files + 497 tests, 3 routes over 148 DAW
                                                                                                                                                                                                                                                                        modules + 58 components, dual Next.js+Tauri host (4 native #[command]s, 5 rust
                                                                                                                                                                                                                                                                        files), 52 docs, and DAW-specific deploy targets (build/build:native/
                                                                                                                                                                                                                                                                        build:worklet/lint/test/typecheck). Consolidated from git counts + committed
                                                                                                                                                                                                                                                                        E0.1/E0.2 evidence. Atomic-feature split + dispositions are E0.14.b+ (semantic).
                                                                                                                                                                                                                                                                        generate-euterpe-code-census.mjs + 4 tests. -->
  - [ ] E0.14.b Split sources into atomic user/operator/system capabilities with
        host/audience/owner/state/dependencies/data class/profile. Evidence:
        atomic ledger.
  - [ ] E0.14.c Cover foundations/theory, composition/score/lyrics,
        instruments/effects/samples, recording/DAW, AI audio/voice, mix/master,
        and spatial explicitly. Evidence: production coverage.
  - [ ] E0.14.d Cover stage/realtime, podcast/video, rights/provenance,
        marketing/discovery, distribution/catalog, learning, developer/provider,
        and operations explicitly. Evidence: extended coverage.
  - [ ] E0.14.e Assign each Euterpe item keep/consolidate/migrate/replace/
        deprecate/remove/externalize with target owner/host/profile/
        dependencies/delivery/expiry. Evidence: disposition ledger.
  - [ ] E0.14.f Reconcile to routes/packages/contracts/stores/tests and fail
        orphan/duplicate/unknown/unreviewed capabilities. Evidence: completeness
        gate.
- [ ] E0.15 Define the boundary among professional production, live/stage,
      podcast/video, label/marketing/catalog operations, artist collaboration,
      developer/provider administration, conservatory/ear-training, discovery,
      sacred/philosophy experiences, and other consumer products. Evidence:
      every capability has an authorized host/audience, handoff, or explicit
      exclusion from the production workbench.
  - [ ] E0.15.a Define audience/trust/data/action boundaries and product goals
        for each named surface. Evidence: surface matrix.
  - [ ] E0.15.b Assign every retained capability/mutation to one canonical host/
        owner/profile/role, distinguishing production core from projections and
        separate products. Evidence: host ledger.
  - [ ] E0.15.c Define typed redacted handoffs for live/stage, podcast/video,
        marketing/catalog, collaboration/review, providers/developers, learning,
        and consumer discovery. Evidence: handoff contracts.
  - [ ] E0.15.d Define
        identity/session/token/deep-link/storage/media/provenance/
        rights/logging separation and step-up transitions. Evidence: security
        boundary.
  - [ ] E0.15.e Define shared asset/release/correction/takedown identity without
        granting consumer/marketing/learning surfaces production authority.
        Evidence: control-flow diagrams.
  - [ ] E0.15.f Test authorized cross-surface journeys and attempted object/
        field/function/media/device access; eliminate duplicate mutations.
        Evidence: boundary suite.
- [ ] E0.16 Define capability profiles for browser, installed desktop/Tauri,
      native audio/plugin host, remote collaboration, live stage, offline, and
      render worker. Evidence: unsupported audio/MIDI/plugin/spatial/voice
      behavior is explicit instead of silently degraded.
  - [ ] E0.16.a Define profile identity/version/detection/prerequisites and
        authoritative capability flags/limits for every named host/mode.
        Evidence: profile schema.
  - [ ] E0.16.b Map audio input/output/latency/channel/sample rate/render/
        filesystem/media/device permissions by profile. Evidence: audio
        capability matrix.
  - [ ] E0.16.c Map MIDI/MPE/sync/control/plugin/native IPC/multi-window/stage
        command availability and safety boundaries. Evidence: device matrix.
  - [ ] E0.16.d Map collaboration/provider/AI/offline storage/render-worker/
        distribution features, data movement, rights/consent, and fallbacks.
        Evidence: service matrix.
  - [ ] E0.16.e Specify unavailable/degraded/read-only/handoff/export behavior,
        user messaging, telemetry, and no silent quality/state loss. Evidence:
        fallback contracts.
  - [ ] E0.16.f Detect at runtime, revalidate on device/network/config changes,
        and test every supported/conditional/unsupported cell. Evidence:
        capability profile suite.

### E1 — Music, session, rights, and release contracts

- [ ] E1.1 Define project, composition, work, recording, version, arrangement,
      section, tempo map, meter map, key map, marker, and cue entities.
      Evidence: schema fixtures.
  - [ ] E1.1.a Define stable identity/tenant/project/owner/lifecycle/version/
        timestamps/audit and distinguish abstract work, composition, recording,
        and project. Evidence: core schemas.
  - [ ] E1.1.b Define arrangement/version parentage and ordered/nested section
        identity, range, repetition, alternative, and reference semantics.
        Evidence: structure fixtures.
  - [ ] E1.1.c Define piecewise tempo/meter/key maps with musical/sample/
        absolute-time anchors, ramps, pickup, polymeter/polyrhythm, and
        microtonal context. Evidence: map fixtures.
  - [ ] E1.1.d Define marker/cue types, position/range/label/action/target/
        performer/control links and arrangement/version scope. Evidence: cue
        fixtures.
  - [ ] E1.1.e Define cross-reference, archive/delete, transition, unknown enum,
        tenant, and immutability constraints plus API/event serialization.
        Evidence: invariant tests.
- [ ] E1.2 Define track, bus, return, clip, take, comp, lane, region,
      automation, device, preset, routing, send, and render entities. Evidence:
      DAW contract compatibility.
  - [ ] E1.2.a Define stable graph identity/order/type/role/channel/layout/state
        for track/bus/return/group/folder and device/routing ownership.
        Evidence: mixer graph schema.
  - [ ] E1.2.b Define immutable media source and clip/take/lane/region
        placement, offset/range/loop/stretch/fade/gain/channel/version
        semantics. Evidence: edit schemas.
  - [ ] E1.2.c Define comp segment/source/take/range/crossfade/order/revision
        and no-destructive-source invariants. Evidence: comp schema.
  - [ ] E1.2.d Define automation lane/parameter/path/timebase/point/curve/mode/
        scope/version and interaction with clips/tempo. Evidence: automation
        schema.
  - [ ] E1.2.e Define typed device/preset/state/version/latency/tail/bypass/
        missing/sandbox and routing/send/sidechain graph edges. Evidence: device
        contracts.
  - [ ] E1.2.f Define render request/range/source revision/profile/dependencies/
        outputs/manifest/measurements/status/provenance. Evidence: DAW
        compatibility fixtures.
- [ ] E1.3 Define score parts, notation events, articulation, dynamics, lyrics,
      chord, lead sheet, MIDI, and interchange metadata. Evidence: round-trip
      fixtures.
  - [ ] E1.3.a Define score/part/staff/voice/measure/time-position/event
        identity and pitched/unpitched/rest/grace/tuplet/tie/slur/beaming
        semantics. Evidence: notation schema.
  - [ ] E1.3.b Define pitch spelling/frequency/tuning/transposition/clef/key/
        range plus articulation/ornament/dynamic/expression relationships.
        Evidence: musical event fixtures.
  - [ ] E1.3.c Define chord symbol/voicing/bass/function/scale/lead-sheet/
        repeat/form/roadmap and explicit analytical assumptions. Evidence:
        harmony schema.
  - [ ] E1.3.d Link lyric syllables/melisma/elision/translation/pronunciation to
        notation events and timing without collapsing lyric revisions. Evidence:
        underlay fixtures.
  - [ ] E1.3.e Map MIDI/MIDI 2 note/controller/program/expression/articulation/
        tempo/meter events to notation with explicit quantization/loss.
        Evidence: MIDI fixtures.
  - [ ] E1.3.f Define MusicXML/notation/MIDI interchange provenance/version/
        unknown extensions/loss diagnostics and round-trip tolerances. Evidence:
        round-trip goldens.
- [ ] E1.4 Define lyric sections, lines, syllables, stress, rhyme, language,
      translation, explicit-content, and timing anchors. Evidence: schema.
  - [ ] E1.4.a Define lyric document/revision/section/line/token/syllable stable
        identity, order, repetition, performer/voice, and text/script/language.
        Evidence: lyric schema.
  - [ ] E1.4.b Define normalized/pronounced syllable, stress/weight/meter/
        elision/melisma and confidence/source fields. Evidence: prosody schema.
  - [ ] E1.4.c Define rhyme sound/family/scheme/position/exact-near/internal/
        multilingual relation with declared pronunciation assumptions. Evidence:
        rhyme fixtures.
  - [ ] E1.4.d Define translation/transliteration/adaptation relationship,
        segment alignment, terminology/names, semantic parity, and provenance.
        Evidence: locale schema.
  - [ ] E1.4.e Define explicit/advisory/sensitive-topic finding versus human
        decision, locale/target scope, evidence, and disclosure. Evidence:
        content policy schema.
  - [ ] E1.4.f Define musical/sample/time anchors to score/MIDI/audio/clip
        ranges and orphan/ambiguous mapping through edits. Evidence: timing
        anchor tests.
- [ ] E1.5 Define recording device, performer, microphone/input, room, take,
      latency, consent, and session provenance. Evidence: capture contract.
  - [ ] E1.5.a Define capture session/device/driver/input/channel/sample-rate/
        bit-depth/buffer/clock/calibration identity and capability snapshot.
        Evidence: device contract.
  - [ ] E1.5.b Define performer/public-versus-protected identity, role/
        instrument/voice, microphone/DI/preamp/input chain, room/location, and
        setup. Evidence: session schema.
  - [ ] E1.5.c Define recording/take/lane/file/channels/range/count-in/punch/
        loop/dropout/overdub identity with immutable source checksums. Evidence:
        take schema.
  - [ ] E1.5.d Define measured/compensated round-trip/input/plugin/network
        latency, confidence/calibration version, clock drift, and alignment.
        Evidence: latency schema.
  - [ ] E1.5.e Define consent purpose/scope/territory/term/media/model/voice/
        distribution/revocation/evidence separately from recording metadata.
        Evidence: consent contract.
  - [ ] E1.5.f Define complete app/device/audio-chain/operator/performer/time/
        source/import/edit provenance with redacted projections. Evidence:
        capture fixtures.
- [ ] E1.6 Define mix revision, plugin/device graph, automation, loudness,
      reference, notes, and approval metadata. Evidence: immutable revision
      tests.
  - [ ] E1.6.a Define immutable mix revision/parent/source session and exact
        track/media/arrangement/routing/device/preset/automation dependency
        manifest. Evidence: mix schema.
  - [ ] E1.6.b Define ordered typed device/routing graph, sidechains/sends/
        latency compensation/tails/missing state and complete state snapshots.
        Evidence: graph fixtures.
  - [ ] E1.6.c Define automation/time maps and recalled manual controls with
        touch/write/latch provenance and deterministic playback intent.
        Evidence: automation snapshot.
  - [ ] E1.6.d Define loudness/peak/dynamics/spectrum/phase/headroom
        measurements with units/channels/range/analyzer versions separate from
        verdicts. Evidence: measurement schema.
  - [ ] E1.6.e Define reference tracks/segments/level matching/rights/
        assumptions and anchored notes/comments/decisions. Evidence: reference
        contract.
  - [ ] E1.6.f Define review/approval/waiver scope/roles/exact revision/
        dependencies/conditions/expiry/invalidation and immutable audit.
        Evidence: revision invariants.
- [ ] E1.7 Define master spec, source mix, processing, measurements, format,
      delivery target, QC, and approval. Evidence: target profile invariants.
  - [ ] E1.7.a Define master job/revision identity and immutable source mix/
        stems/reference/profile/target/dependency manifest. Evidence: master
        schema.
  - [ ] E1.7.b Define processing graph/order/state/plugins/analog steps/
        calibration/latency/dither and deterministic or externally documented
        recall. Evidence: processing contract.
  - [ ] E1.7.c Define rendered master/container/codec/layout/sample-rate/bit-
        depth/channel/duration/gap/fade/checksum and alternate renditions.
        Evidence: format schema.
  - [ ] E1.7.d Define raw loudness/peak/dynamics/spectrum/phase/noise/artifact/
        null measurements with analyzer/calibration versions. Evidence: QC
        measurement schema.
  - [ ] E1.7.e Define target-specific verdict/findings/remediation/unsupported/
        waiver separately from measurements. Evidence: profile contract.
  - [ ] E1.7.f Define mastering review/approval/signature/exact output/
        conditions/expiry and invalidation on source/profile/rights change.
        Evidence: target profile invariants.
- [ ] E1.8 Define musical work, sound recording, sample, interpolation,
      performer, voice, model, and generated-stem rights records separately.
      Evidence: rights-kind validation.
  - [ ] E1.8.a Define common rights record identity/subject kind/version/
        claimant/owner/licensor/source/jurisdiction/territory/term/evidence/state.
        Evidence: base rights schema.
  - [ ] E1.8.b Define composition/work and sound-recording/master rights with
        distinct parties/shares/administration/licensing uses. Evidence:
        work/master fixtures.
  - [ ] E1.8.c Define sample versus interpolation source/use/range/
        recognizability/clearance/attribution/restrictions and derived works.
        Evidence: sample fixtures.
  - [ ] E1.8.d Define performer/neighboring/voice/likeness/session/union or
        contractual rights, consent, compensation, and revocation. Evidence:
        performer fixtures.
  - [ ] E1.8.e Define model/provider/training/inference/output/derivative/
        disclosure rights and generated-stem source/condition/run provenance.
        Evidence: AI rights fixtures.
  - [ ] E1.8.f Enforce kind-specific required fields/transitions/conflicts and
        prohibit conversion/collapse between records. Evidence: validation
        suite.
- [ ] E1.9 Define ownership splits with parties, roles, percentages,
      territories, rights types, dates, disputes, and signatures. Evidence:
      total/incompatibility invariants.
  - [ ] E1.9.a Define split sheet/version/subject work-or-recording/right type/
        territory/effective window/currency and status. Evidence: split schema.
  - [ ] E1.9.b Define party
        identity/role/capacity/publisher/label/administrator/
        representative/payment reference separately from public credit.
        Evidence: party schema.
  - [ ] E1.9.c Define percentage/fraction/points/recoupment/control/license/
        collection shares with exact decimal/rational arithmetic. Evidence:
        share contract.
  - [ ] E1.9.d Define proposal/counter/accepted/signed/disputed/superseded/
        expired state, comments/evidence, authority, and immutable revision
        lineage. Evidence: negotiation schema.
  - [ ] E1.9.e Define cryptographic/e-sign identity/intent/time/document hash,
        quorum, minors/representatives, revocation, and signature validity.
        Evidence: signature contract.
  - [ ] E1.9.f Enforce total exactly per right/territory/window, incompatible
        overlaps, duplicate parties, missing authority, and release snapshot
        invariants. Evidence: property tests.
- [ ] E1.10 Define identifiers and metadata: ISRC, ISWC, UPC/EAN, catalog
      number, contributor IDs, titles, versions, genres, languages, and
      advisories. Evidence: format/checksum validation.
  - [ ] E1.10.a Define identifier type/issuer/value/status/subject/version/
        territory/date/provenance and primary/alternate/replacement relations.
        Evidence: identifier schema.
  - [ ] E1.10.b Implement ISRC/ISWC/UPC/EAN syntax/check-digit/normalization/
        uniqueness/scope and explicit unassigned/pending/invalid state.
        Evidence: identifier tests.
  - [ ] E1.10.c Define internal catalog/contributor/party/artist IDs and
        external mappings without using mutable display names as identity.
        Evidence: mapping tests.
  - [ ] E1.10.d Define title/subtitle/version/display/phonetic/sort/localized
        fields, contributors/roles/order/credits, and language/script. Evidence:
        metadata schema.
  - [ ] E1.10.e Define governed genre/mood/advisory/explicit/territory/target
        vocabularies with source/version/alias/deprecation. Evidence: vocabulary
        tests.
  - [ ] E1.10.f Validate format/checksum/collision/duplicate/reassignment/
        locale/round-trip and distributor mapping. Evidence: metadata suite.
- [ ] E1.11 Define provenance ingredients and transformations for generated,
      recorded, edited, mixed, and mastered audio. Evidence: C2PA/external
      ledger mapping.
  - [ ] E1.11.a Define immutable ingredient identity/kind/hash/rights/owner/
        capture/import/generation source/version and protected/public metadata.
        Evidence: ingredient schema.
  - [ ] E1.11.b Define recording actions with performer/device/room/session/
        calibration/consent and original take relationships. Evidence: capture
        lineage.
  - [ ] E1.11.c Define edit/comp/stretch/pitch/repair/separation/generation/
        plugin/analog/mix/master/render actions with inputs/parameters/tools/
        versions/outputs. Evidence: transformation schema.
  - [ ] E1.11.d Represent DAG relationships through clips/stems/mixes/masters/
        artwork/packages while preserving ranges and many-to-many lineage.
        Evidence: provenance graph.
  - [ ] E1.11.e Map supported assertions/actions/ingredients/signers to C2PA and
        external ledgers with privacy minimization and explicit loss. Evidence:
        mapping matrix.
  - [ ] E1.11.f Define valid/tampered/missing/stripped/unknown/untrusted state,
        signature/hash verification, correction, and read-back. Evidence:
        provenance fixtures.
- [ ] E1.12 Define release, product, track sequence, territory, date, pricing,
      rights, assets, delivery, takedown, and correction entities. Evidence:
      DDEX profile mapping.
  - [ ] E1.12.a Define release/product/version/type/title/label/catalog/UPC/
        metadata/locale/status and immutable candidate relationship. Evidence:
        release schema.
  - [ ] E1.12.b Define ordered disc/side/track/hidden/bonus/component sequence
        with recording/version/ISRC/display/territory availability. Evidence:
        sequencing schema.
  - [ ] E1.12.c Define territory/window/street/preorder/embargo/time-zone/
        pricing/tier/currency/entitlement and rights eligibility. Evidence:
        market schema.
  - [ ] E1.12.d Define master/artwork/lyrics/credits/metadata/provenance/
        advisory/rendition assets with checksums and target requirements.
        Evidence: asset manifest.
  - [ ] E1.12.e Define per-distributor delivery/attempt/ack/content-status/
        read-back/error/retry identifiers and partial aggregate state. Evidence:
        delivery schema.
  - [ ] E1.12.f Define takedown/redelivery/metadata correction/audio
        replacement/ territory change
        causes/actions/notices/history/verification. Evidence: remedy schema.
  - [ ] E1.12.g Map every entity/lifecycle to pinned DDEX ERN profile with
        supported extensions and explicit loss. Evidence: DDEX matrix.
- [ ] E1.13 Define immutable revisions and semantic diffs for score, lyrics,
      arrangement, session, mix, master, rights, and release. Evidence: golden
      diff fixtures.
  - [ ] E1.13.a Define Euterpe's immutable audio revision/parent(s)/subject/
        schema/tool/actor/cause/time/hash/approval lineage and branch/merge
        semantics. Evidence: revision contract.
  - [ ] E1.13.b Define score/lyrics/arrangement diffs by stable musical/text/
        section identity, position, value, move, and semantic context. Evidence:
        creative diff fixtures.
  - [ ] E1.13.c Define session diffs for
        tracks/clips/takes/comp/routing/devices/ automation/tempo and preserve
        source media lineage. Evidence: session diff fixtures.
  - [ ] E1.13.d Define mix/master diffs for dependency graphs/plugin state/
        automation/settings/renders/measurements/profile verdicts. Evidence:
        audio revision fixtures.
  - [ ] E1.13.e Define rights/splits/consent/provenance/release/territory/target
        diffs with protected redaction and material-change classification.
        Evidence: business diff fixtures.
  - [ ] E1.13.f Define ambiguous/unknown plugin/opaque blob/schema-version loss
        and test add/delete/move/split/merge/restore/concurrent goldens.
        Evidence: semantic diff suite.
- [ ] E1.14 Define backward compatibility with current DAW project formats.
      Evidence: old-project migration corpus.
  - [ ] E1.14.a Inventory every deployed project/session/media/preset/plugin/
        desktop/browser schema version and producer/consumer behavior. Evidence:
        version inventory.
  - [ ] E1.14.b Build representative historical corpus including large, missing-
        plugin/media, unusual routing/tempo/automation, corrupt, and partial
        projects. Evidence: migration corpus.
  - [ ] E1.14.c Define read/write/support window and deterministic migrations
        for renamed/split/merged/timebase/device/state fields with explicit
        loss. Evidence: compatibility matrix.
  - [ ] E1.14.d Migrate one version at a time and supported direct paths,
        preserving musical timing/media/hashes/routing/automation/undo history
        or documented boundaries. Evidence: migration tests.
  - [ ] E1.14.e Test old/new browser/desktop/realtime/render combinations,
        interrupted migration/retry/rollback/export, unknown fields, and
        downgrade refusal. Evidence: skew suite.
  - [ ] E1.14.f Publish backup/restore/operator diagnostics and require golden
        semantic/audio comparison before marking migrated. Evidence: rehearsal
        report.
- [ ] E1.15 Define pitch spelling, frequency, octave, tuning/temperament,
      microtonal scale, key/tonality, interval, chord/voicing, rhythmic cycle,
      groove, form, dynamic, articulation, ornament, and cultural-source
      metadata. Evidence: notation/playback/analysis round-trips do not force
      all music into 12-tone equal temperament or Western common practice.
  - [ ] E1.15.a Define pitch as distinct written spelling, sounding frequency/
        ratio/cents, octave/register, reference pitch, transposition, and tuning
        context. Evidence: pitch schema.
  - [ ] E1.15.b Define arbitrary scale/temperament/tuning tables, period/
        nonoctave repetition, degrees/accidentals/glyphs/aliases, and Scala or
        ratified interchange. Evidence: tuning fixtures.
  - [ ] E1.15.c Define key/tonality/mode/raga or other system, interval/chord/
        voicing/function only when applicable, with cultural-source and analysis
        assumptions. Evidence: tonal schema.
  - [ ] E1.15.d Define rhythmic cycle/groove/pulse/subdivision/swing/polyrhythm/
        additive/free-time and meter representation without common-time floors.
        Evidence: rhythm fixtures.
  - [ ] E1.15.e Define form/dynamic/articulation/ornament/expression
        terminology, notation/playback mappings, locale, source tradition, and
        performer interpretation. Evidence: expression schema.
  - [ ] E1.15.f Round-trip representative culturally sourced/non-12-TET/free-
        rhythm notation→MIDI/control→playback→analysis with declared loss and
        expert review. Evidence: inclusive music goldens.
- [ ] E1.16 Define instrument, sampler, synthesizer, effect, plugin, device,
      patch/preset, sample zone, modulation, control surface, MIDI/MPE mapping,
      latency, license, and state-snapshot contracts. Evidence: deterministic
      recall or explicit non-portability diagnostics.
  - [ ] E1.16.a Define device/instrument/effect/plugin identity/kind/format/
        vendor/version/build/architecture/capabilities/ports/parameters and host
        support. Evidence: device schema.
  - [ ] E1.16.b Define synthesizer/sampler engine topology, voices/polyphony/
        tuning/sample zones/articulations/layers/round robins/streaming and
        source rights. Evidence: instrument schema.
  - [ ] E1.16.c Define patch/preset/state blob plus normalized parameters/
        resources/migrations/checksum/factory-user scope and portability.
        Evidence: state contract.
  - [ ] E1.16.d Define modulation source→target/polarity/depth/range/smoothing/
        rate/sync/scope and typed audio/CV/MIDI/MPE/control mappings. Evidence:
        mapping schema.
  - [ ] E1.16.e Define control-surface port/device/profile/learn/feedback/
        conflict and latency/tail/compensation/calibration state. Evidence:
        control contract.
  - [ ] E1.16.f Define license/signature/entitlement/install/update/quarantine/
        missing/crash/safe-mode state and protected credentials. Evidence:
        lifecycle schema.
  - [ ] E1.16.g Validate deterministic recall hashes/audio goldens when possible
        and emit exact missing/version/resource/licence/opaque-state loss
        diagnostics otherwise. Evidence: recall suite.
- [ ] E1.17 Define stem-separation, transcription, inpainting, continuation,
      style transfer, conditional generation, voice conversion, speech, singing,
      choir, and spatial-upmix jobs with source, mask/condition,
      identity/rights, model, parameters, output stems, confidence, and
      provenance. Evidence: job-kind fixtures and consent applicability.
  - [ ] E1.17.a Define shared immutable job request/run/attempt/status/budget/
        provider-model/version/hardware/input-output/provenance contract.
        Evidence: base job schema.
  - [ ] E1.17.b Define separation/transcription source ranges/channels/targets/
        notation/lyrics/speakers and output stems/events/alignments/confidence.
        Evidence: analysis job fixtures.
  - [ ] E1.17.c Define inpainting/continuation/variation/style-transfer exact
        mask/range/context/condition/reference/strength/preservation and
        generated outputs. Evidence: generative edit fixtures.
  - [ ] E1.17.d Define conditional/full generation prompt/score/MIDI/audio/
        structure/style/instrument/seed/negative constraints and editable
        intermediates. Evidence: generation fixtures.
  - [ ] E1.17.e Define voice conversion/speech/singing/choir identity/performer/
        voice model/locale/pronunciation/timing/pitch/harmony/direction and
        outputs. Evidence: voice job fixtures.
  - [ ] E1.17.f Define spatial-upmix source layout/object inference/target/
        renderer/room/trajectory/downmix constraints and outputs. Evidence:
        upmix job fixtures.
  - [ ] E1.17.g Resolve source/reference/sample/model/voice/performer rights and
        consent scope/revocation/disclosure before dispatch; define prohibited/
        nonapplicable states. Evidence: applicability tests.
  - [ ] E1.17.h Define measurements/confidence/uncertainty/quality/safety/raw
        findings separately from policy verdict/human selection. Evidence:
        result schema.
- [ ] E1.18 Define object/channel/bed-based spatial sessions, speaker layouts,
      binaural render settings, ambisonic order/normalization, position/
      trajectory/room metadata, head tracking, target device, downmix, and
      interchange. Evidence: Atmos/binaural/HOA/VR-AR profile goldens or
      explicit unsupported states.
  - [ ] E1.18.a Define spatial session/object/bed/channel/track identity,
        coordinate/frame/unit, position/orientation/spread/divergence/gain/
        priority and automation. Evidence: spatial schema.
  - [ ] E1.18.b Define named/custom speaker layout/labels/angles/elevation/
        distance/LFE/bed mapping, calibration, room, and target constraints.
        Evidence: layout schema.
  - [ ] E1.18.c Define ambisonic order/dimensionality/channel ordering/
        normalization/coordinate orientation and HOA encode/decode metadata.
        Evidence: ambisonic schema.
  - [ ] E1.18.d Define binaural renderer/HRTF/head size/room/head tracking/
        sensor/time alignment and device-specific settings. Evidence: binaural
        contract.
  - [ ] E1.18.e Define object/bed→speaker/binaural/ambisonic downmix/render
        matrices, metadata loss, loudness/peak, and fallback. Evidence: render
        profiles.
  - [ ] E1.18.f Define ADM-BWF/Atmos or ratified/VR-AR interchange mappings,
        profile/version/IDs/timing/objects and explicit unsupported fields.
        Evidence: interchange matrix.
  - [ ] E1.18.g Validate goldens per supported layout/renderer/device/target and
        serialize honest unsupported/degraded states. Evidence: spatial
        conformance suite.

### E2 — Core DAW, editing, and device workflow

- [ ] E2.1 Verify sample-accurate timeline, tempo, meter, marker, loop, and
      transport behavior. Evidence: deterministic timing tests.
  - [ ] E2.1.a Define musical-tick/sample/time conversion, rounding, reference
        sample rate, tempo ramps, meter changes, pickups, and negative/preroll
        positions. Evidence: timing spec.
  - [ ] E2.1.b Verify event/clip/automation/marker/cue placement and rendering
        across tempo/meter edits without cumulative drift. Evidence: alignment
        tests.
  - [ ] E2.1.c Verify play/stop/pause/seek/scrub/locate/record/pre-roll/count-in
        state and exact audible/record sample boundaries. Evidence: transport
        tests.
  - [ ] E2.1.d Verify loop/cycle range, wrap, punch, metronome, latency
        compensation, and automation at boundaries. Evidence: loop tests.
  - [ ] E2.1.e Run long-duration, high/low/variable tempo, odd/additive meter,
        sample-rate conversion, offline/realtime render, and repeated-start
        deterministic goldens. Evidence: timing suite.
- [ ] E2.2 Verify audio engine startup, suspend/resume, device change, sample
      rate, buffer size, dropout, and recovery. Evidence: device fault suite.
  - [ ] E2.2.a Define engine lifecycle/state/error contract and capability/
        permission/device selection before graph activation. Evidence: lifecycle
        spec.
  - [ ] E2.2.b Verify cold/warm startup, user-gesture resume, background/
        foreground, suspend/resume, interruption, and safe graph teardown.
        Evidence: lifecycle tests.
  - [ ] E2.2.c Verify input/output/default device add/remove/switch, channel-map
        change, permission revoke, and fallback without wrong-device capture.
        Evidence: hot-plug tests.
  - [ ] E2.2.d Verify sample-rate/buffer/block/clock change with resampling,
        latency recalibration, transport/recording handling, and explicit
        unsupported states. Evidence: configuration tests.
  - [ ] E2.2.e Inject CPU starvation/xruns/worklet crash/device loss/driver
        error/clock discontinuity and recover without corrupting project/media.
        Evidence: device fault suite.
- [ ] E2.3 Build/complete track, bus, return, group, folder, send, sidechain,
      mute, solo, arm, monitor, and routing operations. Evidence: signal-flow
      tests.
  - [ ] E2.3.a Implement create/delete/duplicate/rename/reorder/nest/unnest for
        audio/MIDI/instrument/aux/bus/return/group/folder with stable IDs and
        non-drag commands. Evidence: structure tests.
  - [ ] E2.3.b Implement typed input/output/channel/layout routing with graph
        validation, cycle/feedback safety, disconnected/missing endpoints, and
        visible signal path. Evidence: routing tests.
  - [ ] E2.3.c Implement pre/post-fader sends, return routing, sidechain source/
        destination/tap/channel mapping, gain/pan, enable, and automation.
        Evidence: send/sidechain tests.
  - [ ] E2.3.d Implement mute/solo/solo-safe/exclusive/group propagation with
        deterministic audible graph and no destructive state changes. Evidence:
        mute/solo goldens.
  - [ ] E2.3.e Implement record arm/input monitoring/auto/off/source selection
        with permissions, feedback prevention, latency, and multi-track capture.
        Evidence: monitor tests.
  - [ ] E2.3.f Cover every command with undo/redo/concurrency/persistence/
        accessible grid and offline/realtime/render signal-flow goldens.
        Evidence: operation coverage.
- [ ] E2.4 Build/complete clip create, import, move, trim, split, slip,
      duplicate, loop, stretch, reverse, fade, crossfade, consolidate, and
      bounce. Evidence: non-destructive edit tests.
  - [ ] E2.4.a Implement clip create/import/record-reference/duplicate with
        immutable source media, channel mapping, project rate, and provenance.
        Evidence: creation tests.
  - [ ] E2.4.b Implement move/copy across tracks/timebases/snap modes and trim
        start/end with bounded source offset and no media rewrite. Evidence:
        placement tests.
  - [ ] E2.4.c Implement split/join/slip/slide/duplicate/loop/repeat and
        preserve take/comp/anchor/automation relationships or explicit impact.
        Evidence: structural edit tests.
  - [ ] E2.4.d Implement nondestructive time stretch/pitch/formant/reverse with
        algorithm/version/quality/transient/tempo context and render provenance.
        Evidence: processing tests.
  - [ ] E2.4.e Implement fade/crossfade shapes/lengths/gains/equal-power/
        overlapping source limits and sample-continuity tests. Evidence: fade
        goldens.
  - [ ] E2.4.f Implement consolidate/bounce to immutable derived media with
        exact range/tails/routing/effects/automation/profile and source
        manifest. Evidence: render lineage.
  - [ ] E2.4.g Verify semantic diff/undo/redo/restore, pointer/non-drag parity,
        sample boundary/null tests, and source bytes unchanged. Evidence:
        non-destructive suite.
- [ ] E2.5 Build/complete MIDI record, draw, quantize, groove, velocity,
      articulation, expression, sustain, MPE, and humanize. Evidence: event
      round-trip.
  - [ ] E2.5.a Implement timestamped MIDI 1/MIDI 2 UMP/MPE recording with port/
        channel/group/note identity, latency compensation, overdub/replace/
        loop, and raw capture. Evidence: record tests.
  - [ ] E2.5.b Implement note/rest/controller/program/pitch/sustain/aftertouch/
        per-note expression draw/edit/delete/duplicate with musical/sample
        timing. Evidence: editor tests.
  - [ ] E2.5.c Implement quantize for
        start/end/duration/strength/swing/iterative rules and reversible
        source-versus-playback timing. Evidence: quantize tests.
  - [ ] E2.5.d Implement groove extraction/application, velocity curves,
        articulation maps, sustain interpretation, and instrument-specific
        expression. Evidence: performance tests.
  - [ ] E2.5.e Implement seeded bounded humanize for timing/velocity/duration/
        expression with preview, repeatability, and no note collisions/stuck
        notes. Evidence: humanize tests.
  - [ ] E2.5.f Round-trip record→edit→save→export/import→playback across MIDI/
        MPE devices with undo and explicit conversion loss. Evidence: event
        suite.
- [ ] E2.6 Build/complete score notation, parts, layout, transposition,
      articulation, lyric underlay, playback, and print/export. Evidence:
      notation goldens.
  - [ ] E2.6.a Implement score/part/staff/voice/measure/event authoring for
        pitched/unpitched/grace/tuplet/tie/slur/repeat/free-time structures.
        Evidence: notation editor tests.
  - [ ] E2.6.b Implement clef/key/tuning/accidental/spelling/transposition/
        instrument range and concert/written pitch without 12-TET-only
        assumptions. Evidence: pitch tests.
  - [ ] E2.6.c Implement articulation/ornament/dynamics/expression/technique and
        playback mapping with instrument/library capability and fallback.
        Evidence: expression goldens.
  - [ ] E2.6.d Implement lyric underlay for syllable/melisma/elision/verse/
        translation/pronunciation and synchronized lyric timing. Evidence: lyric
        tests.
  - [ ] E2.6.e Implement page/system/staff/spacing/break/visibility/cue/layout
        rules, extracted parts, accessibility semantics, and print preview.
        Evidence: layout tests.
  - [ ] E2.6.f Export/import MusicXML/PDF/print/MIDI or ratified formats with
        deterministic notation/playback comparison and loss diagnostics.
        Evidence: notation goldens.
- [ ] E2.7 Build/complete arrangement sections, scenes, variants, and
      section-level move/copy. Evidence: marker/clip/automation coherence.
  - [ ] E2.7.a Implement stable section/scene/variant identity, nested/ordered
        structure, musical range/repetitions/color/label/cue and aliases.
        Evidence: structure tests.
  - [ ] E2.7.b Create/resize/split/merge/duplicate/reorder/move/copy sections
        with explicit ripple versus reference behavior and non-drag commands.
        Evidence: edit tests.
  - [ ] E2.7.c Move/copy affected clips/takes/comp/MIDI/score/lyrics/markers/
        tempo-meter-key/automation consistently or preview exclusions. Evidence:
        coherence tests.
  - [ ] E2.7.d Create/switch/compare arrangement variants using shared media and
        immutable lineage without cross-variant mutation. Evidence: variant
        tests.
  - [ ] E2.7.e Verify undo/redo/concurrency/export/render/session-scene aliases
        and exact marker/clip/automation timing. Evidence: arrangement suite.
- [ ] E2.8 Build/complete comping with take lanes, audition, swipe/segment
      selection, crossfades, and source trace. Evidence: comp provenance.
  - [ ] E2.8.a Create ordered take lanes from loop/punch/overdub/import with
        immutable files, performer/session/device/timing/dropout provenance.
        Evidence: lane tests.
  - [ ] E2.8.b Implement lane/take/range audition with level/pan/routing/
        latency context, solo-safe restoration, and keyboard controls. Evidence:
        audition tests.
  - [ ] E2.8.c Implement swipe and form/table-based segment selection with exact
        source/timeline ranges, overlaps, gaps, priority, and destination.
        Evidence: selection tests.
  - [ ] E2.8.d Generate/edit adjacent crossfades with bounded handles and
        click-free sample continuity; preserve original takes. Evidence:
        crossfade goldens.
  - [ ] E2.8.e Persist comp revision as ordered source-segment manifest and map
        anchors through take edits/moves or mark stale. Evidence: provenance
        tests.
  - [ ] E2.8.f Verify flatten/bounce/restore/undo/redo/concurrent edits and
        null/sample comparisons against source segments. Evidence: comp suite.
- [ ] E2.9 Build/complete automation for track, bus, plugin, tempo, and spatial
      parameters. Evidence: read/write/touch/latch and curve tests.
  - [ ] E2.9.a Discover/register automatable parameters with stable device/path/
        unit/range/default/step/bipolar/logarithmic/discrete semantics.
        Evidence: parameter registry.
  - [ ] E2.9.b Implement lane create/show/hide/arm and point add/move/delete/
        range/scale/copy with musical/sample time and non-drag forms/commands.
        Evidence: lane editing tests.
  - [ ] E2.9.c Implement step/linear/bezier/hold curves, tangents, smoothing,
        value clamping, sample/block interpolation, and deterministic playback.
        Evidence: curve goldens.
  - [ ] E2.9.d Implement read/off/write/touch/latch/trim modes with gesture
        capture, return/ramp, punch, loop, undo, and visible recording state.
        Evidence: mode tests.
  - [ ] E2.9.e Handle tempo and spatial/multidimensional parameters, clip versus
        track automation, section moves, device reorder/remove/missing/version
        migration. Evidence: lifecycle tests.
  - [ ] E2.9.f Verify realtime/offline render parity, latency/feedback safety,
        persistence/round-trip, and accessible semantic list. Evidence:
        automation suite.
- [ ] E2.10 Build/complete plugin/device graph with latency, bypass, preset,
      missing-plugin, sandbox, crash, and version behavior. Evidence: fault
      suite.
  - [ ] E2.10.a Implement typed device node/port/parameter/state graph with
        ordered serial/parallel/rack/split/merge/sidechain routing and cycle
        validation. Evidence: graph tests.
  - [ ] E2.10.b Measure/report plugin/device latency and tail, compute graph
        compensation including live/record paths, and update safely on change.
        Evidence: latency tests.
  - [ ] E2.10.c Implement hard/soft/bypass/suspend behavior with
        gain/click/tail/ latency semantics and automation. Evidence: bypass
        goldens.
  - [ ] E2.10.d Load/save/compare/migrate factory/user/project presets and full
        state snapshots with hashes/resources/version/license. Evidence: preset
        tests.
  - [ ] E2.10.e Preserve missing plugin placeholder/state/routing/automation and
        support locate/substitute/reinstall/version migrate with impact preview.
        Evidence: missing-device tests.
  - [ ] E2.10.f Run plugins in ratified sandbox with CPU/memory/time/file/
        network/device limits, crash detection/quarantine/restart/safe mode.
        Evidence: sandbox tests.
  - [ ] E2.10.g Inject scan/load/process/UI/state/crash/hang/malicious/version/
        licence failures and prove project/audio recovery. Evidence: fault
        suite.
- [ ] E2.11 Build/complete asset and sample browser with preview, tempo/key,
      rights, favorites, tags, search, dedupe, and project use. Evidence:
      permission-aware search.
  - [ ] E2.11.a Define asset/sample
        source/revision/blob/proxy/waveform/duration/
        format/channel/sample-rate/tempo/key/tuning/tags/rights/provenance
        projection. Evidence: browser contract.
  - [ ] E2.11.b Ingest/scan/analyze/deduplicate immutable local/project/library/
        provider assets with exact versus derived metadata and confidence.
        Evidence: ingest tests.
  - [ ] E2.11.c Implement permission-aware text/facet/semantic search, stable
        pagination, favorites/collections/tags, and no unreleased/protected
        count/snippet leaks. Evidence: search tests.
  - [ ] E2.11.d Implement tempo/key-aware low-latency preview with original/
        processed comparison, start/loop/level/output controls, and device
        recovery. Evidence: preview tests.
  - [ ] E2.11.e Add to project/clip/sampler with immutable source link, rights/
        territory/use prompt, transform/provenance, and duplicate handling.
        Evidence: use journey.
  - [ ] E2.11.f Handle removed/revoked/expired/offline/missing/provider-failed
        assets with honest state and replacement/locate workflow. Evidence:
        lifecycle tests.
- [ ] E2.12 Build/complete freeze, flatten, bounce-in-place, stem export, and
      restore. Evidence: source/revision lineage.
  - [ ] E2.12.a Define operation/range/source revision/signal tap/profile/tails/
        dependencies/destination/naming/rights/provenance and impact preview.
        Evidence: render plan.
  - [ ] E2.12.b Freeze track/device chain to immutable render while preserving
        editable graph/state/automation/media and latency/routing behavior.
        Evidence: freeze tests.
  - [ ] E2.12.c Flatten only after explicit warning/authorization into a new
        revision with restorable source snapshot and declared lost editability.
        Evidence: flatten tests.
  - [ ] E2.12.d Bounce-in-place clips/tracks/regions with exact timing/tails/
        routing/effects/automation/channel/sample rate and source links.
        Evidence: bounce goldens.
  - [ ] E2.12.e Export deterministic stems by track/bus/group/role/range with
        alignment, silence/tail policy, manifest, metadata, rights, and hashes.
        Evidence: stem tests.
  - [ ] E2.12.f Restore/unfreeze to exact source revision/state, reconcile later
        edits, and verify null/semantic comparison plus undo/redo. Evidence:
        lineage suite.
- [ ] E2.13 Build/complete undo/redo across all structured editing commands.
      Evidence: command inventory coverage.
  - [ ] E2.13.a Generate canonical command inventory with input/preconditions/
        effects/inverse/merge boundary/side effects and undo eligibility.
        Evidence: command ledger.
  - [ ] E2.13.b Implement atomic and transaction/grouped undo for timeline/
        mixer/device/score/lyrics/rights/release structured mutations. Evidence:
        command tests.
  - [ ] E2.13.c Preserve immutable media/renders/uploads/jobs/external delivery
        by undoing references or issuing compensating/new revisions rather than
        deleting history. Evidence: side-effect tests.
  - [ ] E2.13.d Define per-user/session/project undo scope under collaboration,
        remote operations, branch/merge, offline replay, and conflict. Evidence:
        concurrency tests.
  - [ ] E2.13.e Clear/fork/restore stacks correctly after undo→new edit,
        reload/crash/schema migration, failed command, and partial transaction.
        Evidence: lifecycle tests.
  - [ ] E2.13.f Test command→undo→redo semantic/audio equality, focus/selection
        restoration, keyboard announcements, and inventory coverage. Evidence:
        coverage gate.
- [ ] E2.14 Build/complete autosave, crash recovery, project snapshots, version
      history, compare, and restore-as-new-version. Evidence: crash journey.
  - [ ] E2.14.a Define local operation/checkpoint/server revision/media-upload/
        snapshot/schema/device/session/encryption/retention state. Evidence:
        recovery contract.
  - [ ] E2.14.b Implement debounced idempotent autosave with expected revision,
        durable-before-ack, dirty/saving/saved/error/offline/conflict
        indicators. Evidence: autosave tests.
  - [ ] E2.14.c Persist local journals/media safely before risky recording/edit/
        plugin/render actions and recover after tab/app/process/device crash.
        Evidence: crash tests.
  - [ ] E2.14.d Create named/automatic immutable project snapshots with complete
        dependency/media/plugin/right manifests and retention/cleanup. Evidence:
        snapshot tests.
  - [ ] E2.14.e Show version history and semantic/audio-aware compare across
        arrangement/session/mix/rights/release with missing dependency state.
        Evidence: history tests.
  - [ ] E2.14.f Restore as a new revision, revalidate
        plugins/media/rights/gates/ approvals, retain current and historical
        versions, and test crash journey. Evidence: recovery E2E.
- [ ] E2.15 Build/complete keyboard command system with remapping and collision
      audit. Evidence: pointer-free core edit journey.
  - [ ] E2.15.a Define semantic command registry with stable IDs/context/scope/
        availability/default bindings/platform variants and descriptions.
        Evidence: command schema.
  - [ ] E2.15.b Implement layered global/workspace/editor/device/plugin/modal
        resolution with text-input/assistive technology/browser/OS reserved-key
        handling. Evidence: resolver tests.
  - [ ] E2.15.c Build searchable keyboard preferences for
        add/remove/remap/reset/ import/export profiles with immediate conflict
        and unreachable-command diagnostics. Evidence: settings tests.
  - [ ] E2.15.d Support chords/sequences/modifiers/repeat/hold where safe and
        announce unavailable/context-changing/destructive commands. Evidence:
        interaction tests.
  - [ ] E2.15.e Audit exact/shadowed/prefix/context/platform/plugin collisions
        and fail required commands with no usable binding. Evidence: collision
        report.
  - [ ] E2.15.f Run pointer-free create/edit/record/mix/save/review/export/
        recovery journey with focus and screen-reader compatibility. Evidence:
        keyboard E2E.
- [ ] E2.16 Build/complete non-drag alternatives for clip, automation, routing,
      section, and marker operations. Evidence: WCAG 2.2 dragging tests.
  - [ ] E2.16.a Inventory every
        drag/drop/move/trim/resize/reorder/connect/curve/ range operation and
        define equivalent semantic result. Evidence: drag inventory.
  - [ ] E2.16.b Add numeric/text forms and keyboard step commands for clip/
        region/marker positions, ranges, offsets, fades, loops, and lanes.
        Evidence: timeline tests.
  - [ ] E2.16.c Add point/value/time/curve forms, ordered tables, nudge/scale/
        delete commands for automation. Evidence: automation tests.
  - [ ] E2.16.d Add source/destination/port/tap/channel/gain/sidechain forms and
        before/after/into/out commands for routing/device graph. Evidence:
        routing tests.
  - [ ] E2.16.e Add before/after/position/range/move-copy form commands for
        sections/scenes/markers with affected-item preview. Evidence: structure
        tests.
  - [ ] E2.16.f Keep validation/snap/impact/permissions/concurrency/undo/audit
        identical and run keyboard/screen-reader/touch journeys. Evidence: WCAG
        2.2 suite.
- [ ] E2.17 Build/complete semantic accessible grid/list views for timeline,
      mixer, device graph, and automation. Evidence: screen-reader journey.
  - [ ] E2.17.a Define synchronized content/operation/selection/focus/playhead/
        status parity contract between visual DAW surfaces and semantic views.
        Evidence: parity matrix.
  - [ ] E2.17.b Build timeline treegrid/table by track/lane/clip/region/take/
        marker with musical/sample positions, ranges, state, and edit commands.
        Evidence: timeline semantic tests.
  - [ ] E2.17.c Build mixer grid/table by track/channel for level/pan/mute/solo/
        arm/monitor/sends/meters/routing with units and bounded controls.
        Evidence: mixer semantic tests.
  - [ ] E2.17.d Build device/routing graph tree/table/matrix for nodes/ports/
        edges/parameters/latency/errors plus connect/reorder/configure commands.
        Evidence: graph semantic tests.
  - [ ] E2.17.e Build automation table by parameter/point/time/value/curve/mode
        with add/edit/nudge/delete/range operations. Evidence: automation
        semantic tests.
  - [ ] E2.17.f Preserve focus/selection/playback announcements under
        virtualization/live updates and run end-to-end screen-reader edit/mix/
        route/automate journey. Evidence: semantic E2E.
- [ ] E2.18 Define and meet interactive latency, audio glitch, large-project,
      memory, and render performance budgets. Evidence: profiler reports.
  - [ ] E2.18.a Define supported host/device/buffer/sample-rate/project/network/
        plugin scenarios and measurement harness/calibration. Evidence:
        performance profile.
  - [ ] E2.18.b Set input-to-monitor, control-to-audible, transport, edit, UI
        input/paint, waveform/scroll/zoom interaction latency budgets. Evidence:
        latency budgets.
  - [ ] E2.18.c Set xrun/dropout/click/missed-deadline/CPU audio-thread and
        realtime allocation/lock/GC budgets with safe instrumentation. Evidence:
        glitch budgets.
  - [ ] E2.18.d Set large track/clip/device/automation/score/media project load/
        save/search/render and sustained CPU/memory/GPU/disk/network budgets.
        Evidence: scale budgets.
  - [ ] E2.18.e Set realtime/offline/batch render speed/resource/output-
        determinism and worker concurrency/cost budgets. Evidence: render
        budgets.
  - [ ] E2.18.f Profile production-scale fixtures, capture thread/trace/query/
        memory/audio diagnostics, trend baselines, and fail regressions.
        Evidence: profiler reports.
- [ ] E2.19 Build/complete audio recording and waveform editing for device/
      channel selection, calibration, pre-roll, punch, loop recording, take
      lanes, comp, sample-accurate cut, spectral repair, time/pitch processing,
      normalization, denoise, phase alignment, and non-destructive source
      restore. Evidence: calibrated capture/edit fixtures.
  - [ ] E2.19.a Build device/input/channel/clock/sample-rate/buffer permission
        setup, signal level/phase/noise/latency calibration, and safe
        monitoring. Evidence: setup tests.
  - [ ] E2.19.b Implement count-in/pre-roll/manual-auto punch/loop/multitrack/
        overdub capture with sample-accurate files, dropout markers, and crash-
        safe durable takes. Evidence: recording tests.
  - [ ] E2.19.c Generate waveform/peak/RMS/spectral caches from immutable media
        with channel/time/zoom selection and accessible numeric/semantic views.
        Evidence: waveform tests.
  - [ ] E2.19.d Implement sample-accurate cut/copy/delete/silence/gain/fade/
        normalize/channel operations as nondestructive revisions. Evidence:
        waveform edit tests.
  - [ ] E2.19.e Implement spectral selection/repair/attenuation/heal with exact
        time-frequency masks, algorithm/version, preview, and preserved
        untouched samples when promised. Evidence: spectral goldens.
  - [ ] E2.19.f Implement time/pitch/formant/denoise/declick/dehum restoration
        with settings/quality/latency/tails, A/B, and provenance. Evidence:
        processing tests.
  - [ ] E2.19.g Implement multi-mic/take phase/time alignment with polarity/
        delay/correlation/confidence and manual override, not forced aesthetic
        choice. Evidence: alignment tests.
  - [ ] E2.19.h Restore exact source or prior edit revision and verify
        calibrated capture/edit/null/undo/device-failure fixtures. Evidence:
        end-to-end corpus.
- [ ] E2.20 Build/complete virtual instruments and samplers for oscillator/
      synthesis engines, modulation, envelopes, filters, sample maps,
      articulation, polyphony, tuning, preset/version management, automation,
      freeze, and missing-device recovery. Evidence: state recall and render
      goldens.
  - [ ] E2.20.a Implement ratified synthesis
        engines/oscillators/noise/wavetable/ granular or other sources with
        oversampling/alias/CPU/polyphony/voice semantics. Evidence: engine
        tests.
  - [ ] E2.20.b Implement envelopes/LFOs/modulation matrices/filters/amplifier/
        pan/unison/voice allocation/glide and per-voice/global state. Evidence:
        synthesis tests.
  - [ ] E2.20.c Implement sampler zones/key-velocity ranges/round robin/layers/
        loops/crossfades/root pitch/tuning/articulation/streaming and immutable
        sample rights. Evidence: sample-map tests.
  - [ ] E2.20.d Implement polyphony/voice stealing/sustain/MIDI/MPE/per-note
        expression/microtonal tuning and deterministic event handling. Evidence:
        performance goldens.
  - [ ] E2.20.e Implement versioned preset/state/resources/licence/automation/
        modulation mappings and compare/morph where supported. Evidence: recall
        tests.
  - [ ] E2.20.f Implement freeze/render with exact MIDI/audio/device state/
        tails/latency/provenance and reversible source. Evidence: freeze tests.
  - [ ] E2.20.g Preserve missing-device state/resources/automation/routing and
        support locate/substitute/migrate/safe mode with loss diagnostics.
        Evidence: recovery tests.
  - [ ] E2.20.h Verify save/reload/version/host/sample-rate/render audio goldens
        and CPU/memory budgets. Evidence: instrument suite.
- [ ] E2.21 Build/complete effect and signal-chain authoring for EQ, dynamics,
      saturation, delay, reverb/convolution, modulation, pitch, restoration,
      metering, parallel/mid-side/multiband routing, oversampling, latency
      compensation, A/B, preset, and bypass. Evidence: signal-flow and
      deterministic-state tests.
  - [ ] E2.21.a Implement ratified EQ/filter/dynamics/gate/limiter/de-esser/
        saturation/distortion families with units/ranges/channels/detectors/
        lookahead. Evidence: processor tests.
  - [ ] E2.21.b Implement delay/reverb/convolution/modulation/pitch/restoration
        families with sync/free timing, IR/resource rights, tails, quality, and
        latency. Evidence: time/effect tests.
  - [ ] E2.21.c Implement ordered
        serial/parallel/send-return/mid-side/multiband/ sidechain signal chains
        with split/crossover/mix/phase/feedback safety. Evidence: routing
        goldens.
  - [ ] E2.21.d Implement input/output/GR/loudness/spectrum/phase meters with
        calibration/units/hold/reset and no audio-thread blocking. Evidence:
        meter tests.
  - [ ] E2.21.e Implement oversampling/quality modes and complete plugin/device
        latency/tail compensation across live, record, bypass, freeze, and
        offline render. Evidence: compensation tests.
  - [ ] E2.21.f Implement level-matched A/B/compare, versioned presets/full
        state, automation/modulation, safe bypass, and undo. Evidence:
        interaction tests.
  - [ ] E2.21.g Run impulse/step/sine/noise/reference/null/signal-flow goldens
        across sample rates/channels/hosts and deterministic state reload.
        Evidence: effect suite.
- [ ] E2.22 Build/complete MIDI, MPE, clock, sync, control-surface, pedal,
      hardware instrument, and external effect setup with discovery, mapping,
      learn, calibration, latency, reconnect, conflict, safe feedback, and
      project portability. Evidence: supported-device matrix and virtual-device
      automation.
  - [ ] E2.22.a Discover/identify/authorize hot-plug MIDI 1/MIDI 2 UMP/MPE/
        control/sync/hardware ports with stable aliases and privacy-safe labels.
        Evidence: discovery tests.
  - [ ] E2.22.b Configure input/output/channel/group/MPE zones/filter/transform/
        routing and project/global/default profiles. Evidence: routing tests.
  - [ ] E2.22.c Build learn/mapping for
        notes/CC/NRPN/RPN/program/pitch/pressure/ per-note
        expression/transport/control-surface feedback with conflicts. Evidence:
        mapping tests.
  - [ ] E2.22.d Configure MIDI clock/MTC/MMC/Link or ratified sync authority,
        tempo/timecode start/stop/locate/drift/jitter and safe feedback loops.
        Evidence: sync tests.
  - [ ] E2.22.e Configure pedals/hardware instruments/external effects with
        audio/MIDI routing, patch recall, latency/calibration,
        reamp/send-return, and recording provenance. Evidence: hardware tests.
  - [ ] E2.22.f Handle disconnect/reconnect/renamed/duplicate ports, permission
        revoke, stuck notes, runaway feedback, clock loss, and safe reset.
        Evidence: fault tests.
  - [ ] E2.22.g Serialize portable logical mappings plus device-specific
        bindings/substitution/loss diagnostics and test virtual devices across
        supported hosts. Evidence: device matrix.
- [ ] E2.23 Build/complete tuning, notation, scale/mode, world-rhythm,
      microtonal, transposition, chord/voicing, orchestration/range, form, and
      expression editors without Western-only validation assumptions. Evidence:
      representative culturally sourced and non-12-TET goldens plus named
      musical-domain review.
  - [ ] E2.23.a Build tuning/temperament/reference frequency/scale degree/
        accidental/glyph/keyboard-layout editor and audition across score/MIDI/
        instruments. Evidence: tuning tests.
  - [ ] E2.23.b Build notation/spelling/clef/part/transposition views that keep
        written and sounding pitch plus arbitrary tunings distinct. Evidence:
        notation tests.
  - [ ] E2.23.c Build mode/raga/maqam or ratified culturally sourced system
        metadata, ascending/descending/context/ornament/phrase rules as optional
        declared assistance. Evidence: mode fixtures.
  - [ ] E2.23.d Build rhythmic cycle/additive/polyrhythm/groove/swing/free-time
        editors and metronome/grid/playback without Western meter validation
        floors. Evidence: rhythm goldens.
  - [ ] E2.23.e Build chord/voicing/range/orchestration/form/expression
        assistance with instrument/cultural assumptions, configurable rules, and
        creative exception. Evidence: analysis tests.
  - [ ] E2.23.f Import/export/playback/analyze representative non-12-TET/world-
        rhythm/culturally sourced fixtures and report exact loss/unsupported
        state. Evidence: round-trip suite.
  - [ ] E2.23.g Obtain named musical-domain/cultural review and prohibit
        universal aesthetic score or automatic correction. Evidence: review
        approval.
- [ ] E2.24 Build/complete a session/clip-launch workspace with clips by scene,
      per-clip and scene launch, quantization, legato, follow/next actions,
      probability, capture-to-arrangement, arrangement-to-session aliases,
      recording, performance lock, and accessible grid operation. Evidence:
      sample-accurate launch/capture and pointer-free journeys.
  - [ ] E2.24.a Define session grid track/scene/slot/clip/alias identity and
        shared source relationship with arrangement. Evidence: session model.
  - [ ] E2.24.b Implement clip/scene launch/stop/retrigger/queue with global/
        scene/clip quantization, legato phase, exclusive/multi launch, and exact
        clock authority. Evidence: launch tests.
  - [ ] E2.24.c Implement deterministic seeded follow/next/random/probability/
        repeat actions with bounds, stop conditions, and visible upcoming state.
        Evidence: action tests.
  - [ ] E2.24.d Record MIDI/audio/automation and capture live launches/
        parameters into arrangement with sample-accurate event/clip/tempo
        lineage. Evidence: capture tests.
  - [ ] E2.24.e Create arrangement-to-session aliases/copies with explicit
        update/independence rules and no accidental source mutation. Evidence:
        round-trip tests.
  - [ ] E2.24.f Implement performance lock, safe allowed controls, undo
        boundary, failure recovery, and transport/device/scene emergency stop.
        Evidence: performance tests.
  - [ ] E2.24.g Provide accessible treegrid/table and non-drag clip/scene move/
        launch/configure commands with focus and status announcements. Evidence:
        pointer-free E2E.
- [ ] E2.25 Build/complete unified modulation for LFO, envelope, MSEG,
      envelope/audio follower, step/random, macro/XY, MIDI/MPE, and automation
      sources routed to any eligible parameter with polarity, depth, smoothing,
      rate/sync, per-voice/global scope, visualization, and cycle/range safety.
      Evidence: source→target matrix coverage and deterministic renders.
  - [ ] E2.25.a Define typed modulation source/instance/state for every named
        generator/control, ownership, per-voice/global scope, phase/reset, and
        deterministic seed. Evidence: source contracts.
  - [ ] E2.25.b Implement LFO/envelope/MSEG/step/random generators with shapes/
        points/curves/levels/timing/loop/one-shot/retrigger/rate/free-sync.
        Evidence: generator tests.
  - [ ] E2.25.c Implement envelope/audio followers with input tap/channel/
        detector/attack/release/range and macro/XY aggregation. Evidence:
        follower/macro tests.
  - [ ] E2.25.d Implement MIDI/MPE/control/automation sources with channel/note/
        expression mapping, resolution, pickup, smoothing, and feedback safety.
        Evidence: control tests.
  - [ ] E2.25.e Discover eligible typed targets and connect with polarity/depth/
        offset/range/curve/smoothing/combine order and exact units. Evidence:
        routing tests.
  - [ ] E2.25.f Detect cycles/runaway/audio-rate aliasing/range overflow/
        unavailable targets and enforce CPU/polyphony limits. Evidence: safety
        tests.
  - [ ] E2.25.g Visualize current/base/modulated value and provide semantic
        source-target matrix, keyboard forms, automation handoff, undo, preset/
        state recall. Evidence: accessible UI tests.
  - [ ] E2.25.h Run complete source→target/profile/sample-rate/realtime-offline
        matrix with seeded deterministic render goldens. Evidence: coverage
        report.
- [ ] E2.26 Build/complete native VST3, CLAP, AU and other ratified plugin
      hosting plus browser WAM where supported, including scan, signature/
      license, architecture, sandbox, latency/tail, sidechain, MIDI/MPE,
      automation/modulation, preset/state, UI, crash/quarantine, missing plugin,
      version migration, and safe-mode recovery. Evidence: per-format/plugin/
      host compatibility and malicious-plugin tests.
  - [ ] E2.26.a Ratify plugin formats/versions/OS/architectures/browser host
        profiles, SDK/licence requirements, and explicit unsupported cells.
        Evidence: hosting matrix.
  - [ ] E2.26.b Implement isolated scan/discovery/metadata/capability/port/
        parameter/category/vendor/version/hash/signature/licence inventory with
        timeout/crash quarantine. Evidence: scan tests.
  - [ ] E2.26.c Host realtime/offline audio, MIDI/MPE/note expression,
        sidechain/ multibus/channel layouts, latency/tail/dynamic IO, and
        sample-rate/block changes. Evidence: processing tests.
  - [ ] E2.26.d Host parameter automation/modulation/gesture/state/preset/
        resources/program lists with stable IDs, migration, and deterministic
        recall where possible. Evidence: state tests.
  - [ ] E2.26.e Host plugin
        UI/editor/window/scaling/focus/keyboard/accessibility safely and provide
        generic parameter UI/semantic fallback. Evidence: UI tests.
  - [ ] E2.26.f Sandbox native/browser code with file/network/device/process/
        memory/CPU/time permissions, content security, feedback protection, and
        audit. Evidence: sandbox tests.
  - [ ] E2.26.g Preserve missing/quarantined/licence-expired/wrong-architecture/
        newer-version placeholder, state/routing/automation and substitution/
        reinstall/migrate diagnostics. Evidence: recovery tests.
  - [ ] E2.26.h Inject malicious/hanging/crashing/corrupt-state/huge-allocation/
        filesystem/network/UI/scan/plugin-update attacks and recover project/
        audio safely. Evidence: adversarial tests.
  - [ ] E2.26.i Run per-format/plugin/host/sample-rate/layout/online-offline/
        save-reload/render compatibility cells with current evidence. Evidence:
        compatibility gate.
- [ ] E2.27 Build/complete ARA2 or ratified deep-edit integration and per-clip
      warp markers for time/pitch, tempo mapping, formants, transient/grid
      alignment, comp/take context, plugin state, undo, and round-trip.
      Evidence: supported editor matrix and non-destructive audio goldens.
  - [ ] E2.27.a Ratify ARA2/deep-edit protocols/editors/versions/host profiles/
        features and define document/region/track/media ownership. Evidence:
        editor matrix.
  - [ ] E2.27.b Exchange immutable audio source, musical context/tempo/meter/
        tuning, clip/take/comp ranges, selection, and change notifications with
        version/authorization. Evidence: integration tests.
  - [ ] E2.27.c Persist deep-editor document/plugin state, analysis caches,
        resources, undo transactions, version migration, missing editor, and
        safe mode. Evidence: state tests.
  - [ ] E2.27.d Implement per-clip warp/transient anchors with
        source/destination time, lock/type/strength/confidence,
        monotonic/range/grid validation. Evidence: warp schema/tests.
  - [ ] E2.27.e Implement time/pitch/formant/transient/grid/tempo mapping edits
        and comp/take propagation as nondestructive operations with A/B.
        Evidence: edit goldens.
  - [ ] E2.27.f Coordinate host/plugin undo/redo/save/reload/offline render and
        concurrent clip/tempo/take changes without double application. Evidence:
        lifecycle tests.
  - [ ] E2.27.g Round-trip original→edit→project save→reopen→render/export and
        verify untouched regions/declared transforms/audio tolerances. Evidence:
        non-destructive goldens.
  - [ ] E2.27.h Exercise unsupported/missing/crash/corrupt/version-skew/
        unavailable-analysis states with preserved source and explicit loss.
        Evidence: fault matrix.
- [ ] E2.28 Build/complete open and professional interchange for DAWproject,
      Standard MIDI/MIDI 2.0 UMP, MusicXML/notation, BWF/iXML, AAF, OMF, FCPXML,
      MXF, ADM-BWF, stems, and every ratified format. Evidence: automation,
      fades, warp, devices/plugin state, note expression, sends, tempo/meter,
      markers, clips/scenes, metadata, and loss diagnostics are tested per
      format.
  - [ ] E2.28.a Ratify format/profile/version/import-export scope, libraries/
        validators, licensing, and supported host/target cells. Evidence:
        interchange registry.
  - [ ] E2.28.b Implement DAWproject import/export for project graph/tracks/
        channels/devices/clips/scenes/tempo/meter/automation/media/metadata with
        portable plugin state where defined. Evidence: DAWproject tests.
  - [ ] E2.28.c Implement Standard MIDI/MIDI 2 UMP and MusicXML/notation for
        events/expression/articulation/tuning/tempo/meter/key/lyrics/parts with
        explicit quantization/loss. Evidence: musical interchange tests.
  - [ ] E2.28.d Implement BWF/iXML/stems/ADM-BWF/MXF for audio/timecode/channel/
        spatial/scene/take/track/rights/provenance metadata. Evidence: audio
        interchange tests.
  - [ ] E2.28.e Implement AAF/OMF/FCPXML or ratified editorial formats for
        tracks/clips/ranges/handles/fades/gain/automation/markers/media/
        timecode with relink. Evidence: editorial interchange tests.
  - [ ] E2.28.f Define normalized intermediate mapping for devices/plugin state/
        sends/warp/note expression/scenes/spatial/metadata and exact
        unsupported/ approximated/dropped fields. Evidence: mapping contracts.
  - [ ] E2.28.g Generate preflight/loss profile, require human acceptance where
        material, preserve source, provenance, rights, IDs, and reversible
        import namespace. Evidence: diagnostics tests.
  - [ ] E2.28.h Run format→project→format and cross-host semantic/audio round-
        trips with missing media/plugin/version/corrupt/large fixtures and
        validators. Evidence: per-format gate.
- [ ] E2.29 Build/complete synchronization and control for Ableton Link or
      ratified peer sync, MTC, LTC/SMPTE, MIDI clock, word clock/jam sync, MMC,
      OSC, MCU/HUI/EUCON or ratified control surfaces, MIDI learn, and headless
      REST/control APIs. Evidence: clock-drift, feedback-loop, reconnect,
      authority, and unsafe-remote-command tests.
  - [ ] E2.29.a Ratify supported protocols/versions/transports/devices/hosts/
        frame rates and define capability/security/latency profiles. Evidence:
        sync/control matrix.
  - [ ] E2.29.b Implement peer tempo/phase/quantum sync with explicit leader/
        peer authority, join/leave, start-stop behavior, drift/jitter smoothing,
        and musical-time mapping. Evidence: peer-sync tests.
  - [ ] E2.29.c Implement MTC/LTC-SMPTE/MIDI clock/word-clock or jam-sync
        source/destination/frame-rate/drop-frame/offset/locate/chase/freewheel/
        holdover. Evidence: clock tests.
  - [ ] E2.29.d Implement MMC/OSC/control-surface transport/mixer/device/
        automation commands, display/meter feedback, touch/ownership, and
        profiles. Evidence: control tests.
  - [ ] E2.29.e Implement MIDI learn and mapping conflict/pickup/feedback/
        persistence/portable aliases; prevent echo/runaway/stuck controls.
        Evidence: learn tests.
  - [ ] E2.29.f Implement authenticated scoped rate-limited headless REST/
        realtime control with idempotency, expected state, allowlists, step-up
        for dangerous actions, and audit. Evidence: remote API tests.
  - [ ] E2.29.g Exercise drift/jitter/packet loss/disconnect/reconnect/leader
        conflict/frame-rate change/feedback loop/replay/unsafe command and
        emergency stop. Evidence: adversarial matrix.
- [ ] E2.30 Build/complete offline-first PWA/OPFS and desktop-local operation
      for projects, media, undo, renders, collaboration queue, provider-gated
      features, quota/eviction, encryption, reconnect, merge, and recovery.
      Evidence: offline create/edit/reopen/export plus conflict and storage-loss
      journeys.
  - [ ] E2.30.a Define local project/media/operation/undo/render/job/cache/
        collaboration state, schema/version, ownership, encryption, and
        supported offline capabilities per host. Evidence: offline contract.
  - [ ] E2.30.b Implement PWA service worker/OPFS and desktop-local durable
        storage with atomic writes, content hashes, journals, locks, crash-safe
        recording/import, and migrations. Evidence: local storage tests.
  - [ ] E2.30.c Implement offline create/open/edit/record/import/undo/render/
        export for eligible features and honest disabled/provider-required/
        licence-check states. Evidence: offline workflow tests.
  - [ ] E2.30.d Manage media/proxy/cache/render quota, reservation, pressure,
        user cleanup/export, browser eviction detection, and never evict sole
        acknowledged originals silently. Evidence: quota tests.
  - [ ] E2.30.e Encrypt local sensitive/unreleased/voice/rights data, partition
        by tenant/user/project, enforce device/session/logout/revocation/
        retention, and protect paths. Evidence: security tests.
  - [ ] E2.30.f Queue collaboration/uploads/jobs with stable IDs/dependencies/
        retries/cancel and reauthenticate/reauthorize/deduplicate/rebase on
        reconnect. Evidence: reconnect tests.
  - [ ] E2.30.g Resolve project/media/graph/rights conflicts with mine/theirs/
        base semantic merge, retained copies, and no last-write-wins data loss.
        Evidence: conflict tests.
  - [ ] E2.30.h Run offline create→record/edit→close/reopen→render/export plus
        reconnect/two-client/schema/quota/eviction/storage-corruption/recovery
        journeys. Evidence: offline E2E.
- [ ] E2.31 Build/complete customizable, multi-window, touch/pen, high-DPI,
      control-surface, and blind-producer workflows across arrange, session,
      piano roll, score, mixer, modulation, plugin, and review surfaces.
      Evidence: saved workspace, keyboard/treegrid, screen-reader, touch-target,
      zoom, and reconnect matrices.
  - [ ] E2.31.a Define versioned workspace layout with panels/windows/tabs/
        docks/sizes/focus/context/monitor/display/DPI, named presets, migration,
        reset, and safe bounds. Evidence: workspace schema.
  - [ ] E2.31.b Implement multi-window detach/attach/focus/selection/transport/
        project synchronization, restore after display loss, and no duplicate
        audio engine or mutation owner. Evidence: window tests.
  - [ ] E2.31.c Implement touch/pen gestures/targets/hover alternatives/
        precision controls/palm rejection and non-gesture forms across all named
        surfaces. Evidence: touch/pen tests.
  - [ ] E2.31.d Implement high-DPI/per-monitor scaling/zoom/reflow/canvas text/
        waveform/plug-in UI behavior without blurry or unreachable controls.
        Evidence: DPI tests.
  - [ ] E2.31.e Integrate control-surface focus/bank/track/device/parameter/
        transport feedback with visible ownership and keyboard parity. Evidence:
        hardware workflow tests.
  - [ ] E2.31.f Provide blind-producer semantic treegrid/table/forms, command
        search, structured status/meters, non-drag edits, and concise
        announcements across all named workspaces. Evidence: screen-reader E2E.
  - [ ] E2.31.g Test saved layout/profile/project reopen, reconnect/device/
        display change, themes/density, 200–400% zoom, RTL/long locale, and
        supported input modes. Evidence: workspace matrix.
- [ ] E2.32 Build/complete batch render/export/convert/analyze with range,
      version, target/profile, naming, metadata, rights, dependencies, capacity,
      cost, preview, queue, partial result, retry, cancel, and output
      verification. Evidence: mixed-eligibility batch journey and deterministic
      manifest.
  - [ ] E2.32.a Define batch/item immutable request with project/source version,
        range, operation kind, target/profile, priority, owner, and expected
        outputs. Evidence: batch schema.
  - [ ] E2.32.b Resolve naming/path/template/collision, metadata/locale/
        identifiers/artwork, rights/consent/territory, plugin/media/model/
        renderer dependencies per item. Evidence: preflight tests.
  - [ ] E2.32.c Estimate capacity/CPU-GPU/memory/storage/network/time/currency/
        provider cost and require authorization/budget before dispatch.
        Evidence: estimate tests.
  - [ ] E2.32.d Preview exact eligible/blocked/degraded/lossy item plan and
        allow scoped selection; never drop ineligible items silently. Evidence:
        preview tests.
  - [ ] E2.32.e Execute durable dependency-aware queue with leases/concurrency/
        resource limits/progress/logs/artifacts/retry/cancel/idempotency.
        Evidence: job tests.
  - [ ] E2.32.f Record per-item success/partial/failed/cancelled/unknown and
        preserve successful outputs while retrying only safe failed work.
        Evidence: partial result tests.
  - [ ] E2.32.g Verify output bytes/hash/format/audio measurements/metadata/
        rights/provenance/naming and semantic/source lineage. Evidence: output
        tests.
  - [ ] E2.32.h Emit deterministic batch/item manifest with inputs/versions/
        dependencies/settings/cost/results and run mixed-eligibility E2E.
        Evidence: batch journey.
- [ ] E2.33 Build/complete modular synthesis/control where ratified, including
      audio, CV/gate, trigger, MIDI/MPE, modulation and event ports; typed
      modules; patch cables; polyphony; feedback safety; oversampling; latency;
      preset/version; automation; macro; performance view; and accessible
      list/matrix parity. Evidence: graph validation, signal goldens, CPU
      budgets, and non-pointer operation.
  - [ ] E2.33.a Ratify modular host/profile/module SDK/trust/licence/runtime/
        realtime safety and supported port/data-rate/polyphony semantics.
        Evidence: modular profile.
  - [ ] E2.33.b Define typed module/node/version/state/parameter and audio/CV/
        gate/trigger/MIDI-MPE/modulation/event port compatibility. Evidence:
        graph contract.
  - [ ] E2.33.c Implement add/remove/duplicate/group/reorder modules and
        connect/ disconnect cables with type/range/channel/polyphony/latency
        validation. Evidence: graph editing tests.
  - [ ] E2.33.d Implement polyphonic voice/event allocation, oversampling/
        resampling/block boundaries/latency compensation and deterministic
        scheduling. Evidence: signal tests.
  - [ ] E2.33.e Detect algebraic/feedback cycles, DC/runaway/NaN/denormal/high-
        gain/CPU overload and provide bounded feedback/delay/limiter/emergency
        safeguards. Evidence: safety tests.
  - [ ] E2.33.f Implement versioned patch/preset/resources/automation/macros and
        a performance view exposing only approved controls with
        recall/migration. Evidence: state tests.
  - [ ] E2.33.g Provide synchronized node/port/cable list/table/matrix and
        source- target forms for complete keyboard/screen-reader/non-pointer
        parity. Evidence: accessible graph tests.
  - [ ] E2.33.h Run typed graph validation, impulse/oscillator/control/event
        goldens, save/reload/offline render, CPU/polyphony budgets, crash/
        malicious module recovery. Evidence: modular suite.

### E3 — Concept, score, lyrics, performance, and AI assist

- [ ] E3.1 Build musical-concept capture from text, hum, voice memo, MIDI,
      chord, reference, and project context. Evidence: each input keeps
      provenance and rights prompts.
  - [ ] E3.1.a Define concept/idea/revision/input-kind/source/owner/project/
        status/visibility and immutable provenance contract. Evidence: concept
        schema.
  - [ ] E3.1.b Capture text/chord/score snippets with locale/tuning/tempo/key/
        theory assumptions and stable semantic anchors. Evidence: structured
        input tests.
  - [ ] E3.1.c Capture hum/voice memo/audio through permission/calibration/
        immutable media/checksum/device/session/performer consent workflow.
        Evidence: audio capture tests.
  - [ ] E3.1.d Capture MIDI/MPE with device/timebase/timing/expression/tuning
        and raw versus interpreted events. Evidence: MIDI capture tests.
  - [ ] E3.1.e Attach reference/project context with exact asset/revision/range/
        role, rights/consent/use prompts, protected visibility, and no silent
        provider upload. Evidence: reference tests.
  - [ ] E3.1.f Support save/tag/link/compare/park/discard/convert to proposal
        with autosave/accessibility/offline handling. Evidence: capture journey.
- [ ] E3.2 Quarantine and analyze uploaded audio/MIDI/project files. Evidence:
      malicious/oversize/invalid corpus.
  - [ ] E3.2.a Upload untrusted files into isolated non-executable quarantine
        with size/quota/chunk/checksum/content-type and tenant/project scope.
        Evidence: upload tests.
  - [ ] E3.2.b Detect signature/type mismatch, malformed/polyglot/active/archive
        bomb/malware/project traversal and unsafe plugin/resource references.
        Evidence: malicious corpus.
  - [ ] E3.2.c Probe audio/MIDI/project structure,
        duration/channels/rate/events/ graph/dependencies/version without
        loading untrusted plugins or code. Evidence: safe parser tests.
  - [ ] E3.2.d Analyze waveform/tempo/key/tuning/transients/loudness/MIDI/
        metadata/rights hints with algorithm versions/confidence and no
        authoritative overwrite. Evidence: analysis tests.
  - [ ] E3.2.e Release/reject/manual-review with scan/analysis/policy versions,
        reasons, audit, rescan, and safe proxy; preserve original bytes.
        Evidence: quarantine workflow.
  - [ ] E3.2.f Test timeout/crash/oversize/encrypted/unsupported/corrupt/
        decompression/plugin-reference paths and prove nothing escapes early.
        Evidence: invalid corpus report.
- [ ] E3.3 Build arrangement and composition divergence behind governed
      `lyria`/provider seams. Evidence: not-configured, failure, budget, and
      exact-output tests.
  - [ ] E3.3.a Define provider-agnostic request with pinned concept/project/
        score-MIDI-audio context, desired parts/structure/style constraints,
        rights, seed/count, and budgets. Evidence: request schema.
  - [ ] E3.3.b Resolve configured eligible provider/model/version/data-use/
        capability/rights region and fail honestly when unavailable. Evidence:
        negotiation tests.
  - [ ] E3.3.c Generate materially distinct arrangement/composition candidates
        with editable score/MIDI/audio/stem/section outputs, rationale, and
        source/reference use. Evidence: divergence tests.
  - [ ] E3.3.d Persist exact output bytes/events/settings/prompt/negative
        prompt/ seed/model/provider/cost/latency/provenance and safety results.
        Evidence: output attribution.
  - [ ] E3.3.e Present unranked or transparently measured candidates for human
        audition/compare/edit/accept/reject with no automatic project mutation.
        Evidence: decision tests.
  - [ ] E3.3.f Test not-configured/timeout/rate/malformed/partial/cancel/budget/
        retry/injection/rights failure and deterministic replay limits.
        Evidence: provider fault suite.
- [ ] E3.4 Build lyric divergence and rewrite assist with locale, theme, meter,
      rhyme, explicit-content, and banned-topic constraints. Evidence: explicit
      accept and attribution.
  - [ ] E3.4.a Define operation request with pinned lyric range/revision,
        locale/script/theme/perspective/meaning, syllable/stress/meter/rhyme/
        timing, prohibited topics, and budget. Evidence: request schema.
  - [ ] E3.4.b Generate distinct whole-section or minimal anchored rewrite
        candidates with syllable/stress/rhyme/prosody/semantic comparison and
        uncertainty. Evidence: candidate tests.
  - [ ] E3.4.c Validate
        explicit/advisory/banned-topic/safety/identity/defamation/
        similarity/rights constraints per locale without universal culture
        assumptions. Evidence: policy tests.
  - [ ] E3.4.d Attribute provider/model/prompt/version/reference use/cost and
        display exact diff for per-line/per-change accept/edit/reject. Evidence:
        review tests.
  - [ ] E3.4.e Rebase or invalidate on lyric/score/timing change; accepted
        changes create human-attributed new lyric revision with AI disclosure.
        Evidence: lifecycle tests.
  - [ ] E3.4.f Evaluate meter/rhyme/meaning preservation, stereotypes/
        multilingual performance, protected references, injection, and forbidden
        invention. Evidence: assist evaluation.
- [ ] E3.5 Build score/orchestration assist with instrument ranges,
      transposition, voicing, playability, and style constraints. Evidence:
      proposal-only mutation.
  - [ ] E3.5.a Define request pinned to score/arrangement/tuning/tempo/
        instrument library/profile, desired scope, performer capability, style/
        cultural assumptions, rights, and budget. Evidence: request contract.
  - [ ] E3.5.b Generate anchored proposal diffs for assignment/doubling/
        transposition/range/voicing/articulation/dynamics/register/orchestration.
        Evidence: proposal tests.
  - [ ] E3.5.c Measure instrument range/transposition/technical playability/
        balance/collision and expose declared model/rule assumptions separately
        from creative judgment. Evidence: analysis tests.
  - [ ] E3.5.d Preserve arbitrary tuning/non-Western notation and never “fix”
        intentional techniques/style exceptions automatically. Evidence:
        inclusive fixtures.
  - [ ] E3.5.e Present score/MIDI/audio audition plus semantic diff for
        per-part/ passage accept/edit/reject with stale revision handling.
        Evidence: review tests.
  - [ ] E3.5.f Store provider/model/prompt/reference/source/seed/cost provenance
        and enforce proposal-only server boundary. Evidence: mutation tests.
- [ ] E3.6 Build variant compare for audio, MIDI, score, lyrics, arrangement,
      gates, cost, and provenance. Evidence: synchronized accessible compare.
  - [ ] E3.6.a Select two or more immutable variants and resolve compatible
        musical/sample time maps, levels, sources, target profile, and
        permissions. Evidence: compare contract.
  - [ ] E3.6.b Provide synchronized level-matched audio A/B/solo/loop/scrub/
        blind playback with loudness adjustment disclosed. Evidence: audio
        compare tests.
  - [ ] E3.6.c Provide MIDI/score/lyrics/arrangement semantic diff by events/
        parts/sections/lines/anchors, with visual and table/text parity.
        Evidence: structured compare tests.
  - [ ] E3.6.d Compare raw gate measurements/verdicts/freshness,
        render/provider/ storage cost with units/currency, and full
        rights/provenance lineage. Evidence: evidence compare.
  - [ ] E3.6.e Synchronize selection/playhead/range/focus/comments across panes
        and support keyboard/screen-reader/zoom/nonvisual decision journey.
        Evidence: accessible compare.
  - [ ] E3.6.f Record human select/reject/park/no-decision separately without
        mutating variants or using aesthetic score as automatic winner.
        Evidence: decision tests.
- [ ] E3.7 Build decision records for selected, rejected, parked, and superseded
      ideas/takes/mixes/masters. Evidence: stable links.
  - [ ] E3.7.a Define immutable decision identity/type/subject kind/revision/
        actor-role/time/project/context and considered alternatives. Evidence:
        decision schema.
  - [ ] E3.7.b Require rationale, comparison evidence,
        creative/technical/rights/ cost considerations, uncertainty,
        conflict/recusal, and dissent where applicable. Evidence: validation
        tests.
  - [ ] E3.7.c Define selected destination/version creation, rejected retention,
        parked revisit trigger/expiry, and superseded replacement lineage.
        Evidence: disposition tests.
  - [ ] E3.7.d Preserve stable links after rename/move/archive/restore/migration
        and redacted permissions for media/rights. Evidence: link tests.
  - [ ] E3.7.e Prevent model/agent/score from impersonating human selection and
        maintain immutable history under reversal/new decision. Evidence:
        authority tests.
- [ ] E3.8 Build lyric editor with syllable/stress/rhyme/prosody, timing,
      translation, and pronunciation views. Evidence: known-value meter tests.
  - [ ] E3.8.a Build structured section/line/token/syllable editor with stable
        IDs, Unicode/script/language, verses/repeats, undo, and revision
        anchors. Evidence: editor tests.
  - [ ] E3.8.b Show/edit syllabification, stress/weight, meter/feet or
        applicable rhythmic model, melisma/elision, and declared language
        assumptions. Evidence: meter tests.
  - [ ] E3.8.c Show/edit rhyme sound/family/scheme/internal/near relationships
        using locale/pronunciation sources with uncertainty. Evidence: rhyme
        tests.
  - [ ] E3.8.d Align lines/syllables to score/MIDI/audio musical/sample time
        with tap/record/import/numeric controls and orphan/conflict handling.
        Evidence: timing tests.
  - [ ] E3.8.e Edit translation/transliteration/adaptation in aligned semantic
        views with terminology/names/meaning/rhyme/meter parity. Evidence:
        locale tests.
  - [ ] E3.8.f Edit IPA/phoneme/pronunciation/alternate performer guidance and
        audition where authorized. Evidence: pronunciation tests.
  - [ ] E3.8.g Validate expert-reviewed known values across ratified languages/
        scripts/meters without universalizing one prosodic system. Evidence:
        corpus report.
- [ ] E3.9 Build chord/key/theory inspector with explicit assumptions. Evidence:
      score and MIDI fixtures.
  - [ ] E3.9.a Resolve selected score/MIDI/audio-derived events, tuning/scale/
        notation/tempo context, analysis scope, and applicable theory profile.
        Evidence: inspector contract.
  - [ ] E3.9.b Show observed pitch classes/frequencies/spellings, candidate key/
        mode/tonality with confidence and temporal changes. Evidence: key tests.
  - [ ] E3.9.c Show chord/voicing/bass/inversion/function/interval/nonchord
        tones and alternative interpretations with exact event evidence.
        Evidence: chord tests.
  - [ ] E3.9.d Declare tuning/reference pitch/cultural/theoretical assumptions,
        algorithm/corpus/version, missing/ambiguous/nonapplicable state.
        Evidence: provenance tests.
  - [ ] E3.9.e Allow human choose/edit/annotate/decline an interpretation; never
        rewrite score/MIDI or rank aesthetic quality automatically. Evidence:
        interaction tests.
  - [ ] E3.9.f Verify Western/non-Western/non-12-TET/polychord/ambiguous/free-
        tonal score and MIDI fixtures plus semantic accessibility. Evidence:
        known-value suite.
- [ ] E3.10 Build performance capture with device calibration, latency
      compensation, count-in, monitoring, punch, takes, and consent. Evidence:
      capture tests.
  - [ ] E3.10.a Select/authorize audio/MIDI/MPE devices/channels and calibrate
        levels/noise/phase/clock/round-trip latency with recorded evidence.
        Evidence: setup tests.
  - [ ] E3.10.b Create performer/session/part/arrangement/tuning/tempo/cue/
        monitoring plan and verify applicable consent before arm. Evidence:
        capture plan.
  - [ ] E3.10.c Implement count-in/pre-roll/click/cue/monitor mix/direct-versus-
        processed path with feedback protection and disclosed latency. Evidence:
        monitoring tests.
  - [ ] E3.10.d Implement manual/automatic punch/loop/overdub/multitake capture
        with sample-accurate alignment, dropout markers, and crash-safe local
        originals. Evidence: recording tests.
  - [ ] E3.10.e Apply measured latency compensation without altering original
        timing, expose confidence/manual correction, and handle clock drift.
        Evidence: alignment tests.
  - [ ] E3.10.f Record immutable device/performer/consent/session/file/take/
        operator provenance and revoke/restrict downstream use. Evidence:
        provenance tests.
- [ ] E3.11 Build transcription for audio-to-MIDI, score, chords, and lyrics
      behind honest confidence/proposal states. Evidence: calibrated corpus.
  - [ ] E3.11.a Define request with pinned audio range/channels/stems, task,
        tuning/tempo/language/instrument/voice assumptions, provider/model, and
        budget. Evidence: request contract.
  - [ ] E3.11.b Produce MIDI/note events with pitch/spelling/onset/duration/
        velocity/expression/instrument/voice/confidence and aligned locators.
        Evidence: MIDI output tests.
  - [ ] E3.11.c Produce score/parts/rhythm/meter/key/articulation and chord/
        harmony candidates with alternative interpretations and explicit
        quantization. Evidence: notation output tests.
  - [ ] E3.11.d Produce lyric/speaker/word/syllable/phoneme/time alignments with
        language/uncertainty and no invented text hidden as transcript.
        Evidence: lyric output tests.
  - [ ] E3.11.e Present proposals for per-event/region/part accept/edit/reject,
        retain source audio and full model/run provenance. Evidence: review
        tests.
  - [ ] E3.11.f Evaluate onset/offset/pitch/chord/notation/word/alignment error,
        calibration, polyphony/noise/locale/instrument slices, and abstention.
        Evidence: calibrated corpus report.
- [ ] E3.12 Build generated voice/performance only with valid consent and
      authorized host controls. Evidence: impersonation and revocation tests.
  - [ ] E3.12.a Define voice/performer identity/model/enrollment/reference/
        consent purpose/scope/territory/term/host/output/disclosure contract.
        Evidence: authorization schema.
  - [ ] E3.12.b Verify current identity/consent/rights/model/provider/host/
        project/role and step-up before preview, generation, export, or share.
        Evidence: preflight tests.
  - [ ] E3.12.c Watermark/fingerprint or apply ratified provenance/disclosure,
        retain exact run/reference/settings, and restrict protected voiceprints.
        Evidence: output controls.
  - [ ] E3.12.d Enforce host allowlists, rate/budget/target/content policy,
        dangerous impersonation/public-figure/fraud constraints, and human
        approval. Evidence: policy tests.
  - [ ] E3.12.e Propagate revocation/expiry/scope change to queued jobs, stored
        outputs, projects, mixes, masters, releases, downloads, and providers.
        Evidence: revocation journey.
  - [ ] E3.12.f Test spoofed consent/identity, cross-project/model reuse, voice
        extraction, prompt injection, unauthorized host/API, and revoked cache.
        Evidence: impersonation suite.
- [ ] E3.13 Build generated stem and edit lineage down to model, prompt,
      reference, seed, and transformation. Evidence: provenance inspector.
  - [ ] E3.13.a Record immutable input source/stem/clip/range/mask/condition/
        reference/rights hashes and their exact revisions. Evidence: input
        lineage.
  - [ ] E3.13.b Record provider/model/weights/prompt/negative prompt/seed/
        parameters/tool/code/hardware/version/time/cost and safety policy.
        Evidence: generation lineage.
  - [ ] E3.13.c Record output stem/file/channel/range/hash/confidence/
        measurements and transformation parent/child DAG. Evidence: output
        lineage.
  - [ ] E3.13.d Carry lineage through comp/edit/stretch/process/mix/master/
        bounce/export/release while preserving range/ingredient relationships.
        Evidence: propagation tests.
  - [ ] E3.13.e Build permission-filtered inspector with graph plus semantic
        tree/table, source-use/rights/consent/disclosure/tamper/missing state.
        Evidence: provenance UI.
  - [ ] E3.13.f Verify hashes/signatures/replay where possible and label
        opaque/nondeterministic/stripped/untrusted lineage explicitly. Evidence:
        integrity tests.
- [ ] E3.14 Build assist cost, progress, cancel, retry, and human-acceptance
      views. Evidence: provider fault journey.
  - [ ] E3.14.a Define run/job step/progress/unit/estimate/cost currency/
        provider/budget/attempt/terminal state contract. Evidence: operations
        schema.
  - [ ] E3.14.b Show queued/running/waiting/uploading/generating/downloading/
        analyzing progress, elapsed/remaining/unknown, current step, and
        resource use. Evidence: progress tests.
  - [ ] E3.14.c Show estimated versus authorized versus actual token/compute/
        storage/egress/currency cost with delayed accounting and budget alerts.
        Evidence: cost tests.
  - [ ] E3.14.d Implement requested/acknowledged/completed cancellation across
        upload/provider/worker/download/commit boundaries with partial artifact
        handling. Evidence: cancel tests.
  - [ ] E3.14.e Implement retry/resume/replay with pinned inputs/versions or
        explicit substitution, idempotency, safe dedupe, and attempt history.
        Evidence: retry tests.
  - [ ] E3.14.f Present outputs as unaccepted auditionable proposals with exact
        compare/provenance/rights and per-output accept/edit/reject. Evidence:
        acceptance tests.
  - [ ] E3.14.g Inject not-configured/rate/timeout/malformed/partial/budget/
        cancel/network/rights-revoke/provider-commit failures. Evidence:
        provider fault journey.
- [ ] E3.15 Build composition, lyric, arrangement, performance, mix, mastering,
      metadata, and rights agent plans with least-privilege tool grants.
      Evidence: project revision, assets, policy, budget, stop conditions, and
      approval points are locked before dispatch.
  - [ ] E3.15.a Define distinct plan templates/allowed outputs for every named
        agent domain and prohibit blending creative, rights, QC, approval, and
        distribution authority. Evidence: template registry.
  - [ ] E3.15.b Pin project/song/session/arrangement/mix/master/release/assets/
        rights/policy/profile revisions, objective, constraints, and acceptance
        criteria. Evidence: plan inputs.
  - [ ] E3.15.c Grant least-privilege
        read/analyze/audition/draft/propose/render/ job tools with
        object/field/range/host/provider/target scope and expiry. Evidence:
        capability manifest.
  - [ ] E3.15.d Lock token/compute/currency/time/step/tool/storage/egress
        budgets, provider/data-use/local-cloud, retry/fallback, and
        rights/consent scope. Evidence: budget policy.
  - [ ] E3.15.e Define human checkpoints, audition/approval/rights-QC-required/
        stop-the-line/cancel/escalation conditions before dispatch. Evidence:
        control contract.
  - [ ] E3.15.f Preview exact plan/grants/data movement/risks to authorized
        human, sign/hash approval, and reject stale or mutated dispatch.
        Evidence: dispatch tests.
- [ ] E3.16 Build Euterpe-scoped agent run, queue, plan, tool-call, budget,
      output, rights-use, and provenance views. Evidence: live trace and project
      isolation.
  - [ ] E3.16.a Define run/step/tool/queue/event/budget/input-rights/output/
        artifact/provenance state and retention contracts. Evidence: run schema.
  - [ ] E3.16.b Build queue/run list with
        project/owner/template/status/progress/
        priority/wait/failure/budget/cost and stable filters. Evidence:
        operations tests.
  - [ ] E3.16.c Build live timeline for immutable plan, model/tool calls,
        source/ reference use, decisions/retries/errors, artifacts, and human
        controls. Evidence: trace tests.
  - [ ] E3.16.d Show token/compute/time/currency/storage/egress limits versus
        actual/forecast and truthful delayed costs. Evidence: budget tests.
  - [ ] E3.16.e Show exact input/output media/revisions/ranges, rights/consent/
        model use, hashes, transformations, and accepted status. Evidence:
        provenance view.
  - [ ] E3.16.f Enforce tenant/project/role/field/media/rights redaction in UI/
        API/events/logs/export and no cross-project counts/timing. Evidence:
        isolation tests.
  - [ ] E3.16.g Test live reconnect/order/dedupe/stale/partial/offline/cancel/
        terminal reconciliation. Evidence: live trace E2E.
- [ ] E3.17 Intake agent output as attributed clips, takes, settings,
      suggestions, or release-metadata proposals. Evidence: agents cannot assert
      clearance/consent, sign splits, approve a master, or distribute.
  - [ ] E3.17.a Define allowlisted output envelopes for clip/take/stem/score-
        MIDI/lyric/arrangement/settings/mix-master suggestion/metadata proposal
        with run provenance. Evidence: output schemas.
  - [ ] E3.17.b Validate format/hash/duration/range/timebase/channels/events/
        parameters/anchors/schema/size/rights/safety and quarantine media.
        Evidence: intake validation.
  - [ ] E3.17.c Store in a proposal namespace with explicit unaccepted state,
        immutable source/run links, no release/QC/rights/consent/approval
        semantics. Evidence: persistence tests.
  - [ ] E3.17.d Present level-matched audition/semantic diff/settings/input-use/
        confidence/cost/rights for per-part accept/edit/reject. Evidence: review
        tests.
  - [ ] E3.17.e Apply accepted output as human-attributed new
        revision/reference, retaining AI provenance and rerunning
        technical/rights/approval gates. Evidence: acceptance tests.
  - [ ] E3.17.f Attempt clearance/consent assertion, split signing, master/QC
        approval, release/distribution/takedown, direct project mutation, or
        privilege expansion. Evidence: capability rejection tests.
- [ ] E3.18 Implement agent pause, redirect, audition, reject, cancel, budget
      kill, and replay. Evidence: partial audio and metadata remain explicitly
      unaccepted and linked to immutable runs.
  - [ ] E3.18.a Define legal control transitions by run/step/provider/job/
        artifact state, actor, idempotency, and commit/terminal boundary.
        Evidence: control state machine.
  - [ ] E3.18.b Pause at safe checkpoints, stop new tool calls, retain leases/
        partial artifacts, and resume only after plan/grant/rights validation.
        Evidence: pause tests.
  - [ ] E3.18.c Redirect through reviewed plan amendment/new plan with changed
        objective/constraints/grants/budgets while preserving original.
        Evidence: redirect tests.
  - [ ] E3.18.d Audition outputs through sandboxed level-safe routing with no
        project mutation; reject with retained reason and immutable artifact.
        Evidence: audition/reject tests.
  - [ ] E3.18.e Cancel queued/running/provider/render/download phases with
        requested-versus-completed state, compensation, and timeout. Evidence:
        cancel tests.
  - [ ] E3.18.f Enforce automatic/manual budget kill with bounded overshoot and
        replay from pinned inputs/versions or disclosed substitutions, deduping
        side effects. Evidence: budget/replay tests.
  - [ ] E3.18.g Verify partial media/metadata/settings stay unaccepted/
        unapproved/unreleased with complete run/source/rights provenance.
        Evidence: status tests.
- [ ] E3.19 Build stem separation with source/channel selection, stem taxonomy,
      bleed/artifact/confidence review, manual correction, recombination, rights
      inheritance, and per-stem lineage. Evidence: stem-sum and known-mixture
      evaluation.
  - [ ] E3.19.a Define separation request pinned to source revision/range/
        channels, target stem taxonomy/count, model/profile, rights, quality,
        budget, and output format. Evidence: request schema.
  - [ ] E3.19.b Support channel/region/mask/source selection with preview,
        minimum signal validation, immutable original, and protected rights/
        consent checks. Evidence: selection tests.
  - [ ] E3.19.c Run durable provider/local job and produce aligned immutable
        stems plus residual, exact model/settings/seed/hardware/input-output
        hashes, and cost. Evidence: job tests.
  - [ ] E3.19.d Measure per-stem target/interference/bleed/artifact/silence/
        phase/timing/confidence and mark insufficient/unsupported/failure
        separately. Evidence: analysis tests.
  - [ ] E3.19.e Provide synchronized level-matched solo/mute/residual/sum A/B
        and semantic measurement table for human taxonomy/quality review.
        Evidence: review UI.
  - [ ] E3.19.f Allow manual stem relabel/group/split/merge/mask correction/
        spectral edit and rerun as new version without rewriting outputs.
        Evidence: correction tests.
  - [ ] E3.19.g Recombine with gain/pan/phase/alignment handling and compare
        stem sum to source under declared decomposition/tolerance. Evidence:
        recombination tests.
  - [ ] E3.19.h Inherit/review source/sample/performer/voice/model rights and
        consent per stem; prevent separation from creating new clearance.
        Evidence: rights tests.
  - [ ] E3.19.i Carry per-stem lineage through edit/mix/master/export/release
        and run synthetic/real known-mixture plus stem-sum evaluation. Evidence:
        evaluation report.
- [ ] E3.20 Build audio inpainting, continuation, variation, conditional
      generation, and style transfer with exact masks/ranges, musical context,
      reference rights, continuity controls, audition, replace/comp, and
      provenance. Evidence: untouched samples remain bit-identical where the
      operation promises surgical editing.
  - [ ] E3.20.a Define distinct operation contracts for
        inpaint/continue/variant/ conditional generation/style transfer, each
        with allowed scope/output and no semantic aliasing. Evidence: operation
        registry.
  - [ ] E3.20.b Select exact source/mask/range/channel/stem/context handles and
        musical tempo/meter/key/tuning/form/neighboring audio conditions.
        Evidence: selection tests.
  - [ ] E3.20.c Define prompt/score/MIDI/reference/style/instrument/seed/
        strength/negative/preservation/transition/duration constraints and
        resolve rights/consent. Evidence: condition schema.
  - [ ] E3.20.d Generate multiple immutable candidates with exact
        provider/model/ parameters/input-output
        hashes/cost/confidence/provenance and safe failure state. Evidence:
        generation tests.
  - [ ] E3.20.e Measure boundary continuity, tempo/pitch/timbre/phase/noise/
        loudness, source leakage/copying, artifacts, semantic/style fit, and
        uncertainty. Evidence: quality tests.
  - [ ] E3.20.f Audition synchronized source/candidate/context with level match,
        waveform/spectral/semantic comparison, and per-candidate decision.
        Evidence: audition tests.
  - [ ] E3.20.g Apply by nondestructive replace/new take/comp layer/alternate
        arrangement, preserving source and supporting undo/restore. Evidence:
        edit integration tests.
  - [ ] E3.20.h For surgical operations, assemble output from exact source plus
        generated mask and byte/sample-compare every promised untouched region.
        Evidence: preservation goldens.
  - [ ] E3.20.i Carry reference/model/generated-region rights/disclosure/
        provenance through project/mix/master/release and test revocation.
        Evidence: lineage tests.
- [ ] E3.21 Build voice conversion, speech, singing, choir, harmony, dubbing,
      pronunciation, timing, vocal processing, and performance-direction
      workflows with subject/performer consent, identity scope, locale,
      revocation, disclosure, and misuse monitoring. Evidence: each mode has a
      distinct consent/policy/evaluation profile.
  - [ ] E3.21.a Define separate mode profiles/contracts for conversion, speech,
        solo singing, choir/harmony, dubbing, pronunciation/timing, processing,
        and direction with explicit nonapplicability. Evidence: mode registry.
  - [ ] E3.21.b Verify subject/performer/voice/model identity, enrollment/
        reference provenance, purpose/territory/term/host/target consent,
        rights, and current revocation before every operation. Evidence: consent
        gate.
  - [ ] E3.21.c Define text/lyrics/phonemes/locale/accent/pronunciation/timing/
        pitch/melody/harmony/voice assignment/expression/direction inputs with
        protected data handling. Evidence: input contracts.
  - [ ] E3.21.d Generate immutable takes/stems with exact provider/model/voice/
        prompt/seed/settings/input-output hashes/watermark/cost/provenance.
        Evidence: generation tests.
  - [ ] E3.21.e Evaluate intelligibility/pronunciation/alignment/pitch/rhythm/
        harmony/identity similarity-drift/artifact/naturalness separately with
        calibrated uncertainty. Evidence: mode evaluations.
  - [ ] E3.21.f Audition/compare/comp/edit/process with human acceptance and
        prevent identity similarity or quality score from asserting consent/
        authenticity. Evidence: review tests.
  - [ ] E3.21.g Attach mode/AI/voice disclosure and rights metadata to clips/
        mixes/masters/releases; restrict downloads/sharing/model reuse.
        Evidence: enforcement tests.
  - [ ] E3.21.h On revocation/expiry/scope change, cancel jobs, quarantine/
        restrict outputs, preview blast radius, replace/takedown, and notify
        providers/targets. Evidence: revocation E2E.
  - [ ] E3.21.i Monitor impersonation/fraud/unauthorized cloning/voiceprint
        extraction/provider misuse with privacy-safe alerts and human review.
        Evidence: abuse tests.
- [ ] E3.22 Build melody, harmony, rhythm/groove, arrangement, orchestration,
      accompaniment, improvisation, and full text-to-music workflows with
      editable intermediate score/MIDI/audio representations. Evidence: creators
      can inspect and accept/reject parts rather than receiving an opaque final
      waveform.
  - [ ] E3.22.a Define distinct generation/assist workflows, allowed inputs/
        outputs, scope, creative assumptions, rights, provider capability,
        budget, and human checkpoints. Evidence: workflow registry.
  - [ ] E3.22.b Build melody and harmony/voicing generation from text/chords/
        motifs/score/MIDI/audio context with tuning/range/form constraints and
        alternative interpretations. Evidence: melodic/harmonic tests.
  - [ ] E3.22.c Build rhythm/groove generation from tempo/cycle/pulse/style/
        performance references with editable timing/velocity/expression and
        culturally declared assumptions. Evidence: rhythm tests.
  - [ ] E3.22.d Build arrangement/orchestration/accompaniment generation with
        sections/roles/instruments/ranges/articulations/texture/density and
        performer/device capabilities. Evidence: production tests.
  - [ ] E3.22.e Build improvisation and full text-to-music as planned sequences
        of editable form/chords/melody/rhythm/parts/score/MIDI/stems/audio, not
        a single opaque artifact. Evidence: plan/intermediate tests.
  - [ ] E3.22.f Provide synchronized score/MIDI/audio audition and semantic diff
        for per-section/part/event/stem accept/edit/reject/replace/comp.
        Evidence: decision UI.
  - [ ] E3.22.g Record every intermediate’s
        model/prompt/reference/seed/settings/ parent/rights/cost/provenance and
        carry through accepted project revision. Evidence: lineage tests.
  - [ ] E3.22.h Evaluate structure/timing/playability/preservation/diversity/
        originality/leakage/locale-cultural slices without universal aesthetic
        score. Evidence: evaluation report.
- [ ] E3.23 Build AI mix and master assistance as versioned, auditionable
      settings or renders with target/reference assumptions, explanations,
      confidence, cost, undo, and human approval. Evidence: assist cannot
      overwrite source, approve QC, or distribute.
  - [ ] E3.23.a Define mix versus master assist requests pinned to source
        revision, selected tracks/stems/range, target/reference/profile, allowed
        processors, rights/privacy, and budget. Evidence: request schemas.
  - [ ] E3.23.b Produce versioned settings/automation/routing/device proposals
        or immutable renders with exact changed parameters and no source
        mutation. Evidence: proposal tests.
  - [ ] E3.23.c Explain target/reference assumptions, measured issues,
        recommended actions, alternatives, confidence/uncertainty, and
        model/provider versions. Evidence: explanation tests.
  - [ ] E3.23.d Provide level-matched blindable A/B, loudness-normalized
        comparison, raw measurement deltas, semantic settings diff, and source
        restore. Evidence: audition tests.
  - [ ] E3.23.e Show estimated/actual cost/progress/cancel/retry and preserve
        partial outputs as unaccepted. Evidence: operations tests.
  - [ ] E3.23.f Accept/edit/reject per setting/range/render into a new human-
        attributed mix/master candidate with undo and full AI provenance.
        Evidence: acceptance tests.
  - [ ] E3.23.g Re-run independent master/target/rights/QC gates and require
        qualified human approval; block assist from QC approval/distribution.
        Evidence: authority tests.
- [ ] E3.24 Build local/on-device and cloud AI capability negotiation with
      model/weight version, hardware/memory, privacy, latency, quality, energy,
      offline availability, fallback, and result equivalence policy. Evidence:
      unsupported hardware fails honestly, local data does not leave the device
      silently, and provider fallback never changes consent or rights scope.
  - [ ] E3.24.a Define operation capability requirements and local/browser/
        desktop/native/worker/cloud provider profiles with model/weights/
        runtime/version/licence. Evidence: capability contracts.
  - [ ] E3.24.b Detect CPU/GPU/NPU/architecture/memory/storage/energy/thermal/
        network/offline/runtime support and benchmark or validate minimums.
        Evidence: hardware tests.
  - [ ] E3.24.c Resolve data classification/rights/consent/provider-training/
        region/retention/encryption/model-use constraints before considering
        each execution location. Evidence: privacy policy tests.
  - [ ] E3.24.d Estimate latency/quality/resource/energy/cost/output formats and
        present eligible choices/limits to user or policy. Evidence: negotiation
        UI.
  - [ ] E3.24.e Dispatch locally without network when selected and verify
        firewall/telemetry/cache/model-download boundaries; fail honestly on
        unsupported hardware. Evidence: local execution tests.
  - [ ] E3.24.f Dispatch cloud only after explicit allowed scope with minimized
        encrypted data, provider/model pinning, audit, cancel, and deletion.
        Evidence: cloud execution tests.
  - [ ] E3.24.g Allow fallback only within preauthorized privacy/rights/consent/
        quality/cost constraints and require reapproval for expanded data
        movement. Evidence: fallback tests.
  - [ ] E3.24.h Define semantic/audio result equivalence/tolerances and disclose
        nondeterminism/model substitutions rather than silently presenting
        identical capability. Evidence: parity report.

### E4 — Theory, audio quality, mix, and master gates

- [ ] E4.1 Implement tempo/meter-map consistency and event alignment gates.
      Evidence: known MIDI/score fixtures.
  - [ ] E4.1.a Validate tempo/meter segment order/ranges/overlaps/gaps/ramps/
        pickup/time signatures and musical-sample conversion continuity.
        Evidence: map tests.
  - [ ] E4.1.b Check score/MIDI/clip/marker/cue/automation/event anchors against
        applicable map revision and detect drift/orphan/ambiguous mapping.
        Evidence: alignment tests.
  - [ ] E4.1.c Check bar/beat/subdivision boundaries, tuplets/polyrhythm/
        additive/free-time applicability and avoid common-time assumptions.
        Evidence: rhythmic fixtures.
  - [ ] E4.1.d Persist raw timing deltas/units/event IDs/map versions/evidence
        separately from tolerance/profile verdict. Evidence: result contract.
  - [ ] E4.1.e Run known MIDI/score/audio/import/export/tempo-edit/long-duration
        fixtures and stale-map invalidation. Evidence: known-value suite.
- [ ] E4.2 Implement key/scale, chord spelling, range, voice-leading, parallel,
      and dissonance diagnostics as configurable theory assistance. Evidence:
      known score fixtures.
  - [ ] E4.2.a Define versioned theory profile with tuning/cultural/style/genre/
        period/instrument assumptions, enabled diagnostics, thresholds, and
        nonapplicability. Evidence: profile schema.
  - [ ] E4.2.b Measure key/scale membership/spelling/alternative interpretation,
        chord/voicing/inversion/function and exact note evidence. Evidence:
        harmony tests.
  - [ ] E4.2.c Measure written/sounding range/transposition/playability/
        instrumentation against versioned performer/instrument capabilities.
        Evidence: range tests.
  - [ ] E4.2.d Measure voice-leading/spacing/crossing/parallels/dissonance/
        resolution only where declared rules apply and show creative
        alternatives. Evidence: counterpoint tests.
  - [ ] E4.2.e Keep raw findings separate from advisory verdict; allow human
        annotate/waive/disable and never auto-edit or assign aesthetic quality.
        Evidence: assistance tests.
  - [ ] E4.2.f Validate Western/non-Western/non-12-TET/intentional exception/
        ambiguous fixtures with named musical review. Evidence: score corpus.
- [ ] E4.3 Distinguish intentional creative exceptions from technical errors.
      Evidence: authorized waiver retains original measurement.
  - [ ] E4.3.a Classify diagnostics as objective technical integrity, target
        conformance, configurable theory/style assistance, or subjective
        preference. Evidence: diagnostic registry.
  - [ ] E4.3.b Define exception/waiver scope over exact subject/measurement/
        profile/version/range, reason/evidence/actor
        authority/conditions/expiry. Evidence: waiver schema.
  - [ ] E4.3.c Preserve immutable raw measurement and original verdict while
        storing a separate human exception decision. Evidence: storage tests.
  - [ ] E4.3.d Restrict nonwaivable safety/rights/consent/integrity/target
        constraints and require specialist approval for bounded exceptions.
        Evidence: policy tests.
  - [ ] E4.3.e Invalidate waiver on subject/measurement/profile/target/rights/
        approver/expiry change and preserve history. Evidence: lifecycle tests.
  - [ ] E4.3.f Show intentional/waived/unresolved state distinctly from pass and
        include it in review/release evidence. Evidence: UI/audit tests.
- [ ] E4.4 Implement lyric syllable, stress, meter, rhyme, pronunciation, and
      line-timing measurements. Evidence: language-specific fixture set.
  - [ ] E4.4.a Pin lyric/score/audio/timing/pronunciation revision and
        applicable locale/prosody/rhyme/meter profile with declared assumptions.
        Evidence: measurement inputs.
  - [ ] E4.4.b Measure token/syllable boundaries, elision/melisma,
        stress/weight/ meter alignment and confidence/source. Evidence: prosody
        tests.
  - [ ] E4.4.c Measure rhyme families/scheme/position/exact-near/internal with
        pronunciation variants and uncertainty. Evidence: rhyme tests.
  - [ ] E4.4.d Measure phoneme/pronunciation/name/locale variants and line/
        syllable onset-duration alignment to score/MIDI/audio. Evidence:
        pronunciation/timing tests.
  - [ ] E4.4.e Persist raw values/evidence/algorithm/corpus/profile versions
        separately from creative assist verdict and mark nonapplicable.
        Evidence: result contract.
  - [ ] E4.4.f Validate expert-reviewed fixtures per ratified language/script/
        meter, including dialect/translation/free meter. Evidence: locale
        corpus.
- [ ] E4.5 Implement recording checks for clipping, DC offset, silence, noise,
      dropouts, channel, sample rate, bit depth, and phase. Evidence: calibrated
      WAV corpus.
  - [ ] E4.5.a Verify file/container/codec/sample-rate/bit-depth/channel/layout/
        duration/checksum and decode completeness against session/profile.
        Evidence: format tests.
  - [ ] E4.5.b Measure sample/inter-sample clipping where applicable, peak
        locations, DC offset, silence/digital zero, low level, and
        discontinuity. Evidence: signal tests.
  - [ ] E4.5.c Measure noise floor/SNR/hum/buzz/broadband/impulse/dropout/glitch
        with selected calibration and signal sufficiency. Evidence: noise tests.
  - [ ] E4.5.d Measure polarity/phase/correlation/time offset across channels/
        microphones and detect channel swaps/duplicates/missing channels.
        Evidence: multichannel tests.
  - [ ] E4.5.e Persist raw measurements/units/ranges/analyzer/calibration/source
        version separately from capture-profile verdict/remediation. Evidence:
        provenance tests.
  - [ ] E4.5.f Run calibrated WAV corpus across rates/depths/layouts/noise/
        deliberate clipping/dropout/phase and analyzer failures. Evidence:
        conformance report.
- [ ] E4.6 Implement mix checks for clipping, headroom, mono compatibility,
      phase, spectral balance, dynamics, masking, and true peak. Evidence:
      reference fixtures.
  - [ ] E4.6.a Analyze exact mix revision/range/layout/render path and verify
        sufficient signal/calibration/analyzer/profile before measurement.
        Evidence: input tests.
  - [ ] E4.6.b Measure sample/true peak/clipping/headroom and integrated/short/
        momentary dynamics/loudness distributions without universal targets.
        Evidence: level tests.
  - [ ] E4.6.c Measure mono/downmix compatibility, polarity/correlation/phase/
        spatial energy and identify affected channels/times/frequencies.
        Evidence: compatibility tests.
  - [ ] E4.6.d Measure spectrum/tonal balance/masking/dynamic range/transients/
        crest with exact references/bands/assumptions and uncertainty. Evidence:
        balance tests.
  - [ ] E4.6.e Persist raw values separately from target/reference/profile
        verdict and treat aesthetic comparison as advisory. Evidence: result
        tests.
  - [ ] E4.6.f Run calibrated reference/edge/intentional-exception/silence/
        multichannel fixtures with known expected values. Evidence: mix corpus.
- [ ] E4.7 Implement integrated, short-term, momentary loudness and loudness
      range per the pinned BS.1770/R128 implementation. Evidence: official or
      calibrated test signals.
  - [ ] E4.7.a Pin BS.1770/R128/EBU Tech implementation/version/channel weights/
        K-weighting/gating/window/LRA/true-peak dependencies. Evidence: meter
        profile.
  - [ ] E4.7.b Implement channel-layout-aware K-weighting and absolute/relative
        gating for integrated loudness with exact block overlap/rounding.
        Evidence: integrated tests.
  - [ ] E4.7.c Implement short-term and momentary window/hop calculations and
        time-aligned result series. Evidence: window tests.
  - [ ] E4.7.d Implement loudness range percentile/gating/sufficient-duration
        rules and explicit insufficient-signal state. Evidence: LRA tests.
  - [ ] E4.7.e Persist LUFS/LU/true-peak raw outputs/ranges/units/layout/
        algorithm/calibration/source hashes separately from target verdict.
        Evidence: result schema.
  - [ ] E4.7.f Validate official or independently calibrated mono/stereo/
        multichannel/silence/short/program test signals within tolerances.
        Evidence: conformance report.
- [ ] E4.8 Implement target-specific loudness and peak profiles rather than one
      universal LUFS number. Evidence: broadcast, streaming, podcast, and
      platform fixtures.
  - [ ] E4.8.a Define versioned profile per broadcast/streaming/podcast/video/
        physical/spatial platform with jurisdiction/target/layout/content type.
        Evidence: profile registry.
  - [ ] E4.8.b Encode integrated/short/momentary/LRA/true-sample peak targets/
        ranges, measurement standard, normalization behavior, and exceptions.
        Evidence: profile contracts.
  - [ ] E4.8.c Resolve applicable profile from release target/territory/format/
        layout/version and show unknown/conflicting/unsupported explicitly.
        Evidence: resolution tests.
  - [ ] E4.8.d Compute verdict from retained raw measurements with exact delta/
        reason/remediation and no reinterpretation of creative mix quality.
        Evidence: verdict tests.
  - [ ] E4.8.e Recompute verdict after profile change without rerendering valid
        source measurements and invalidate approval/delivery as needed.
        Evidence: recomputation tests.
  - [ ] E4.8.f Validate broadcast/streaming/podcast/platform boundary fixtures
        and current distributor expectations. Evidence: target suite.
- [ ] E4.9 Implement master format, channel layout, codec, sample rate, bit
      depth, dither, gap, fade, metadata, and artwork checks. Evidence:
      per-target goldens.
  - [ ] E4.9.a Verify container/codec/profile/extension/MIME/decode/duration/
        checksum/file size and target support. Evidence: file checks.
  - [ ] E4.9.b Verify sample rate/bit depth/endian/channel count/layout/labels/
        ordering/downmix metadata and expected master spec. Evidence: audio
        format tests.
  - [ ] E4.9.c Detect/respect dither/noise shaping, unnecessary repeat dither,
        truncation, clipping, sample-rate conversion version, and silence.
        Evidence: signal processing tests.
  - [ ] E4.9.d Verify program start/end, pregap/postgap/intertrack
        gap/continuous album transitions/fade shape/tails and sequence
        alignment. Evidence: sequencing tests.
  - [ ] E4.9.e Verify identifiers/titles/versions/contributors/languages/
        advisories/rights/lyrics/credits/provenance embedded and package
        metadata. Evidence: metadata tests.
  - [ ] E4.9.f Verify artwork dimensions/color/profile/format/file size/text/
        rights/advisory/target safe areas and associations. Evidence: artwork
        tests.
  - [ ] E4.9.g Run per-target goldens and distinguish unsupported/missing/
        invalid/stale/analyzer failure from pass. Evidence: master gate suite.
- [ ] E4.10 Implement spatial/immersive profile checks where supported.
      Evidence: channel/object layout fixtures and explicit unsupported states.
  - [ ] E4.10.a Resolve exact session/layout/renderer/device/target profile and
        supported object/bed/channel/ambisonic/binaural capabilities. Evidence:
        applicability tests.
  - [ ] E4.10.b Validate channel labels/order/angles/elevation/LFE/bed mapping
        and object
        count/IDs/coordinates/trajectories/spread/priority/automation. Evidence:
        layout tests.
  - [ ] E4.10.c Validate ambisonic order/dimensionality/channel ordering/
        normalization/coordinate orientation and conversion. Evidence: HOA
        tests.
  - [ ] E4.10.d Validate binaural renderer/HRTF/head-tracking sensor/timing/
        fallback/device metadata and object-to-binaural behavior. Evidence:
        binaural tests.
  - [ ] E4.10.e Validate downmix/render loudness/peak/phase/localization/
        fold-down/metadata/interchange and record exact loss. Evidence: render
        tests.
  - [ ] E4.10.f Emit explicit unsupported/degraded/unavailable/unknown renderer
        or layout state and run goldens per supported cell. Evidence: spatial
        gate.
- [ ] E4.11 Implement reference-track comparison without treating aesthetic
      similarity as automatic quality. Evidence: raw measures and policy.
  - [ ] E4.11.a Define authorized reference/revision/range/rights/target/
        alignment/level-match and comparison purpose. Evidence: reference
        contract.
  - [ ] E4.11.b Align tempo/time/sections and level-match transparently while
        preserving original playback and reporting applied transforms. Evidence:
        alignment tests.
  - [ ] E4.11.c Measure loudness/peak/dynamics/spectrum/tonal balance/stereo-
        spatial/phase/tempo/structure differences with units/ranges/uncertainty.
        Evidence: measurement tests.
  - [ ] E4.11.d Show synchronized A/B and semantic comparison with exact
        reference/source/profiles/analyzer versions. Evidence: compare tests.
  - [ ] E4.11.e Apply optional advisory target bands separately; prohibit a
        single similarity/quality verdict or automatic mix/master changes.
        Evidence: policy tests.
  - [ ] E4.11.f Validate identical/level-shifted/genre-different/intentionally
        different/short/unauthorized reference fixtures. Evidence: known-value
        suite.
- [ ] E4.12 Implement render null/round-trip and stem-sum diagnostics where
      applicable. Evidence: known signal fixtures.
  - [ ] E4.12.a Define applicability/alignment/gain/polarity/delay/sample-rate/
        layout/tail/tolerance and expected nondeterminism for each comparison.
        Evidence: diagnostic profile.
  - [ ] E4.12.b Render source and comparison through pinned realtime/offline/
        export/import/plugin/format paths with exact manifests. Evidence:
        reproducible inputs.
  - [ ] E4.12.c Align signals and compute null residual peak/RMS/spectrum/
        correlation/time-varying error by channel/range. Evidence: null tests.
  - [ ] E4.12.d Sum stems with declared routing/gain/pan/phase/processing/tails
        and compare to mix bus or source. Evidence: stem-sum tests.
  - [ ] E4.12.e Distinguish exact, tolerance-pass, expected nondeterministic,
        mismatched, insufficient, and analyzer failure with evidence. Evidence:
        result states.
  - [ ] E4.12.f Validate impulse/sine/noise/known plugin/round-trip/stem-mix/
        alignment-error fixtures across sample rates/layouts. Evidence: signal
        corpus.
- [ ] E4.13 Persist raw measurements separately from target verdicts. Evidence:
      profile change recomputes without rerendering.
  - [ ] E4.13.a Define immutable measurement record with source/render/range/
        channel/layout/input hashes, values/units/series, analyzer/calibration/
        plugin versions, and uncertainty. Evidence: measurement schema.
  - [ ] E4.13.b Define separate verdict with measurement references, applicable
        profile/threshold/rules, outcome/reason/remediation/exception and time.
        Evidence: verdict schema.
  - [ ] E4.13.c Prevent verdict fields from mutating/denormalizing raw values
        and prevent missing/unsupported/error measurements from defaulting to
        pass. Evidence: storage invariants.
  - [ ] E4.13.d Recompute new verdicts from retained valid measurements after
        profile/threshold change without rendering/analyzing again. Evidence:
        recomputation tests.
  - [ ] E4.13.e Rerender/reanalyze only when source/render/analyzer/calibration
        dependencies invalidate and preserve prior lineage. Evidence: lifecycle
        tests.
  - [ ] E4.13.f Compare old/new profile results and explain changes without
        rewriting historical approval/release evidence. Evidence: profile change
        report.
- [ ] E4.14 Version algorithms, calibration signals, profiles, thresholds, and
      plugins. Evidence: every result is reproducible or labeled otherwise.
  - [ ] E4.14.a Define immutable IDs/digests for analyzer/DSP code/config/
        runtime, calibration files/hardware, target/theory profiles/thresholds,
        and plugin/state/resources. Evidence: version contracts.
  - [ ] E4.14.b Resolve/pin every dependency before measurement/render and
        prohibit mutable aliases as sole provenance. Evidence: dispatch tests.
  - [ ] E4.14.c Persist dependency DAG plus OS/CPU/architecture/audio library/
        sample rate/layout/random seed/input-output hashes. Evidence: provenance
        tests.
  - [ ] E4.14.d Define approved/effective/retired/revoked/blocked states,
        compatibility, migration, rollback, retention, and artifact
        availability. Evidence: lifecycle policy.
  - [ ] E4.14.e Detect missing/unknown/changed/unavailable versions and label
        result unverifiable/stale/nondeterministic instead of reproducible.
        Evidence: failure tests.
  - [ ] E4.14.f Reproduce representative measurements/renders from provenance,
        compare within declared tolerance, and disclose substitutions. Evidence:
        reproducibility gate.
- [ ] E4.15 Add unavailable, unsupported, insufficient-signal, and analyzer
      failure states. Evidence: none renders as pass.
  - [ ] E4.15.a Define distinct applicability/execution/measurement/verdict
        states, reason codes, safe messages, owners, retry/remediation, and
        telemetry. Evidence: state taxonomy.
  - [ ] E4.15.b Map not-configured/dependency/provider/device/plugin/profile/
        permission/authorization outage to unavailable. Evidence: availability
        tests.
  - [ ] E4.15.c Map host/format/layout/sample-rate/operation/profile capability
        absence to unsupported with supported alternatives/loss. Evidence:
        support tests.
  - [ ] E4.15.d Detect silence/too-short/too-few-events/wrong range/missing
        channels/low SNR or other insufficient signal with required minimum.
        Evidence: sufficiency tests.
  - [ ] E4.15.e Map timeout/crash/NaN/malformed output/version/calibration/
        resource errors to analyzer failure with retained diagnostics. Evidence:
        fault tests.
  - [ ] E4.15.f Assert API/UI/export/review/release/analytics never display any
        nonpass state as pass/current/healthy. Evidence: projection suite.
- [ ] E4.16 Build accessible quality views with values, units, thresholds,
      confidence, history, and remediation. Evidence: non-visual parity.
  - [ ] E4.16.a Define view model with measurement name/value/unit/range/
        channel/time/evidence/analyzer/profile/threshold/verdict/confidence/
        freshness. Evidence: quality contract.
  - [ ] E4.16.b Build prioritized findings/history/remediation presentation
        using text/table/structure, not color/graph alone, and avoid one
        aesthetic quality score. Evidence: UI review.
  - [ ] E4.16.c Provide semantic tables/descriptions for waveform/spectrum/
        loudness/phase/spatial/time-series charts synchronized to visual
        selection/playback. Evidence: parity tests.
  - [ ] E4.16.d Provide keyboard inspect/filter/sort/range/audition/compare/
        waive/remediate/rerun operations with focus and announcements. Evidence:
        keyboard tests.
  - [ ] E4.16.e Show unavailable/unsupported/insufficient/failure/stale/
        intentional exception distinctly with eligible action and no pass
        styling. Evidence: state tests.
  - [ ] E4.16.f Test screen reader/zoom/reflow/themes/high contrast/long locale/
        RTL/large result sets and compare against visual content/operations.
        Evidence: nonvisual E2E.
- [ ] E4.17 Implement pitch/tuning, rhythm/groove, harmony/counterpoint, melody,
      orchestration, form, genre/style, and performance diagnostics as
      separable, culturally configurable assistants with declared assumptions.
      Evidence: no universal aesthetic "quality" score and expert-reviewed
      known-value corpora.
  - [ ] E4.17.a Define independent diagnostic modules with versioned cultural/
        theoretical/style/instrument/performance profiles, applicability,
        assumptions, measures, and nonapplicability. Evidence: module registry.
  - [ ] E4.17.b Implement pitch/tuning/intonation/spelling/range diagnostics for
        arbitrary temperaments/reference systems with exact event/frequency
        evidence. Evidence: pitch corpus.
  - [ ] E4.17.c Implement rhythm/groove/pulse/cycle/subdivision/swing/timing/
        repetition diagnostics without universal grid or common meter. Evidence:
        rhythm corpus.
  - [ ] E4.17.d Implement harmony/counterpoint/chord/voicing/voice-leading/
        dissonance diagnostics only under explicit applicable systems. Evidence:
        harmony corpus.
  - [ ] E4.17.e Implement melody/contour/range/phrase/orchestration/
        instrumentation/form diagnostics with performer and cultural source
        context. Evidence: composition corpus.
  - [ ] E4.17.f Implement genre/style/performance timing/dynamics/articulation/
        technique comparison as descriptive assistance with uncertainty, not
        correctness. Evidence: style/performance corpus.
  - [ ] E4.17.g Persist raw findings/assumptions/evidence separately from human
        interpretation/exception and offer suggestions only. Evidence: policy
        tests.
  - [ ] E4.17.h Validate each module on expert-reviewed culturally diverse/
        ambiguous/intentional-exception fixtures and prohibit aggregate
        aesthetic score. Evidence: signed evaluation.
- [ ] E4.18 Implement stem-separation/inpainting/continuation/style-transfer
      fidelity and leakage checks. Evidence: known source/target masks, bleed,
      preservation, artifact, and uncertainty fixtures.
  - [ ] E4.18.a Define job-kind-specific applicability, source/target/mask/
        reference/condition inputs, expected preserved/generated regions, and
        calibrated fixture truth. Evidence: evaluation contract.
  - [ ] E4.18.b Measure separation target capture/interference/bleed/residual/
        artifact/silence/phase/timing and stem-sum error per stem. Evidence:
        separation tests.
  - [ ] E4.18.c Measure inpainting mask fidelity/boundary continuity and byte/
        sample preservation of untouched regions where guaranteed. Evidence:
        inpaint tests.
  - [ ] E4.18.d Measure continuation/variation musical boundary continuity,
        structure/timing/pitch/timbre/energy drift and repetition/collapse.
        Evidence: continuation tests.
  - [ ] E4.18.e Measure style-transfer condition adherence separately from
        content/identity/source copying, reference leakage, and rights risk.
        Evidence: transfer tests.
  - [ ] E4.18.f Detect prompt/reference/training memorization or source stem
        leakage using ratified similarity/fingerprint methods and uncertainty.
        Evidence: leakage tests.
  - [ ] E4.18.g Persist raw metrics/model/evaluator/corpus versions separately
        from human acceptance/rights verdict and handle insufficient signal.
        Evidence: result tests.
  - [ ] E4.18.h Run synthetic/recorded/edge/adversarial known masks/mixtures/
        references across models and report thresholds/calibration. Evidence:
        fidelity report.
- [ ] E4.19 Implement voice/singing/choir intelligibility, pronunciation,
      timing, pitch, identity similarity/drift, artifact, consent, and
      disclosure checks. Evidence: protected calibrated corpus and explicit
      non-applicability.
  - [ ] E4.19.a Define separate evaluation profiles by speech/singing/choir/
        conversion/dubbing/locale/voice type with protected corpus governance
        and applicability. Evidence: profile registry.
  - [ ] E4.19.b Measure word/phoneme intelligibility, pronunciation/name/locale
        accuracy, alignment/timing/duration and confidence. Evidence: language
        tests.
  - [ ] E4.19.c Measure pitch/intonation/rhythm/expressive contour/harmony/
        voice-blend or separation under task-appropriate musical context.
        Evidence: music tests.
  - [ ] E4.19.d Measure identity similarity and unintended identity drift/
        cross-voice leakage separately from authenticity/consent, with privacy
        controls. Evidence: identity tests.
  - [ ] E4.19.e Measure artifacts/noise/breath/sibilance/phase/discontinuity/
        robotic or collapse indicators and calibrated uncertainty. Evidence:
        artifact tests.
  - [ ] E4.19.f Verify current consent/rights/model-use/territory/target/host/
        disclosure/watermark/provenance; mark nonapplicable or block separately
        from audio metrics. Evidence: policy gate.
  - [ ] E4.19.g Protect voiceprints/corpus/identity outputs from unauthorized
        storage/search/log/export/provider access and enforce deletion/
        revocation. Evidence: privacy tests.
  - [ ] E4.19.h Report task/subgroup/locale/voice slices, calibration/
        thresholds/uncertainty with named voice/performance/rights review.
        Evidence: signed evaluation.
- [ ] E4.20 Implement Dolby Atmos or ratified object-audio, binaural, ambisonic,
      immersive-device, head-tracking, downmix, loudness, peak, metadata, and
      renderer compatibility gates. Evidence: each supported
      layout/renderer/target pair has its own conformance proof.
  - [ ] E4.20.a Pin supported
        object-audio/binaural/ambisonic/device/interchange/ renderer/target
        profiles, SDK/validator versions, layouts, and explicit unsupported
        cells. Evidence: conformance matrix.
  - [ ] E4.20.b Validate bed/object/channel IDs/layout/count/routing, position/
        trajectory/spread/priority/automation, coordinate frames, and ranges.
        Evidence: object-audio tests.
  - [ ] E4.20.c Validate ambisonic order/dimensionality/channel order/
        normalization/coordinate orientation plus encode/decode rotations.
        Evidence: HOA tests.
  - [ ] E4.20.d Validate binaural HRTF/renderer/head-tracking sensor rate/
        latency/frame alignment/dropout/fallback/device metadata. Evidence:
        binaural/device tests.
  - [ ] E4.20.e Render supported speakers/headphones/devices and measure
        loudness/true peak/phase/downmix/fold-down/localization/metadata loss
        per cell. Evidence: render tests.
  - [ ] E4.20.f Validate ADM-BWF/Atmos or ratified package structure/timing/
        IDs/metadata/checksums/target constraints with pinned validators.
        Evidence: interchange tests.
  - [ ] E4.20.g Run known layout/object/trajectory/head-motion/downmix/silence/
        boundary/malformed fixtures and compare render goldens/tolerances.
        Evidence: profile goldens.
  - [ ] E4.20.h Publish supported/conditional/unsupported
        layout×renderer×device× target cells with current proof, known loss,
        owner, and expiry; fail stale cells. Evidence: release gate.

### E5 — Rights, consent, originality, provenance, and splits

- [ ] E5.1 Build rights registry for works, recordings, samples, stems,
      performances, voices, models, artwork, and references. Evidence:
      kind-specific forms.
  - [ ] E5.1.a Implement common rights subject/version/party/jurisdiction/
        territory/use/term/evidence/state/search projection with stable IDs.
        Evidence: registry contract.
  - [ ] E5.1.b Build separate work/composition, sound recording/master, sample/
        interpolation, and generated stem forms with kind-specific validation.
        Evidence: music rights forms.
  - [ ] E5.1.c Build performer/neighboring/voice/likeness/session consent forms
        with protected identity and model-use scope. Evidence: identity rights
        forms.
  - [ ] E5.1.d Build model/provider/training/inference/output/reference and
        artwork/image/design rights forms. Evidence: AI/artwork forms.
  - [ ] E5.1.e Support create/review/approve/dispute/expire/revoke/supersede/
        hold with immutable history, attachments, and property authorization.
        Evidence: lifecycle tests.
  - [ ] E5.1.f Provide permission-aware search/relations/blast radius without
        exposing contracts, voiceprints, or unreleased media. Evidence: registry
        E2E.
- [ ] E5.2 Build sample intake with source, license, territory, term, use,
      attribution, restrictions, document, and checksum. Evidence: validation.
  - [ ] E5.2.a Intake original sample bytes into quarantine and record source/
        seller/library/URL/purchase/account/date/project/import actor. Evidence:
        intake provenance.
  - [ ] E5.2.b Preserve immutable bytes/checksum/fingerprint/media metadata/
        preview proxy and scan/archive/project safety state. Evidence: file
        tests.
  - [ ] E5.2.c Capture license/rightsholder/licensee/use/synchronization/
        derivative/model/distribution/monetization/attribution restrictions.
        Evidence: licence form.
  - [ ] E5.2.d Capture territory/term/effective/expiry/platform/release/project/
        transfer/seat limits and current entitlement evidence. Evidence: scope
        tests.
  - [ ] E5.2.e Store contract/receipt/document encrypted with checksum/version/
        issuer and redacted ordinary projection. Evidence: document tests.
  - [ ] E5.2.f Validate missing/conflicting/expired/nontransferable/no-AI/
        no-commercial/unknown terms and block unsupported project use. Evidence:
        validation suite.
- [ ] E5.3 Build sample and interpolation clearance workflow. Evidence:
      unresolved use blocks release.
  - [ ] E5.3.a Detect/register exact sample/interpolation use by source/range/
        clip/stem/mix/master/release with recognizability and transform
        provenance. Evidence: use registry.
  - [ ] E5.3.b Create clearance case with parties/rights types/territories/
        channels/term/attribution/fees/deadline/risk and source evidence.
        Evidence: case schema.
  - [ ] E5.3.c Implement research/contact/request/negotiate/counter/approve/
        reject/withdraw/expire/dispute transitions with secure communications.
        Evidence: workflow tests.
  - [ ] E5.3.d Store executed licence/consent/fees/conditions/signatures and map
        exact allowed uses to project/release. Evidence: clearance record.
  - [ ] E5.3.e Re-evaluate after edit/replacement/territory/target/date/
        ownership/licence change and invalidate stale clearance. Evidence:
        lifecycle tests.
  - [ ] E5.3.f Block mix/master/release/delivery as policy requires when any
        detected required use is unresolved, disputed, expired, or unsupported.
        Evidence: release gate tests.
- [ ] E5.4 Build performer and voice consent with purpose, scope, territory,
      term, model use, revocation, and evidence. Evidence: consent matrix.
  - [ ] E5.4.a Define performer/subject identity/public projection/
        representative/minor-capacity and protected contact/voiceprint
        boundaries. Evidence: identity contract.
  - [ ] E5.4.b Capture informed purpose/operations/content/voice model/
        training-inference/output use/hosts/providers/targets/audience and
        prohibited uses. Evidence: consent form.
  - [ ] E5.4.c Capture project/work/session/range/territory/channel/locale/term/
        compensation/credit/privacy/transfer/withdrawal conditions. Evidence:
        scope contract.
  - [ ] E5.4.d Record identity verification, signed intent/time/document hash/
        witness or representative authority, evidence, and version. Evidence:
        signature tests.
  - [ ] E5.4.e Implement active/limited/withdrawn/revoked/expired/disputed/
        superseded lifecycle and downstream blast radius/remedy. Evidence:
        lifecycle tests.
  - [ ] E5.4.f Resolve applicability per recording/generation/conversion/edit/
        model/release and test consent matrix including provider fallback.
        Evidence: matrix suite.
- [ ] E5.5 Build ownership split proposals, negotiation state, signatures,
      disputes, and locked release snapshot. Evidence: exact-total and authority
      tests.
  - [ ] E5.5.a Create versioned split proposal per work/recording/right/
        territory/window with exact rational shares, parties/roles, rationale,
        and source evidence. Evidence: proposal schema.
  - [ ] E5.5.b Implement invite/view/counter/comment/accept/reject/abstain/
        withdraw transitions with party-specific authorization and expiry.
        Evidence: negotiation tests.
  - [ ] E5.5.c Verify party/representative identity/capacity and collect signed
        exact document hash/intent/time with no agent impersonation. Evidence:
        signature tests.
  - [ ] E5.5.d Open/track dispute over party/share/right/territory/window/
        authority with evidence, mediator/legal decision, and no silent
        overwrite. Evidence: dispute tests.
  - [ ] E5.5.e Enforce exact totals/overlap/incompatibility/duplicate/minor/
        missing authority and preserve immutable version history. Evidence:
        property tests.
  - [ ] E5.5.f Lock approved release snapshot to exact split version/signatures/
        territories and invalidate candidate on material change. Evidence:
        release snapshot tests.
- [ ] E5.6 Build publishing/master ownership distinction. Evidence: contracts
      and UI cannot collapse the two.
  - [ ] E5.6.a Define separate composition/publishing and recording/master
        rights subjects/parties/shares/administration/licensing uses. Evidence:
        domain contracts.
  - [ ] E5.6.b Map song/work/composition versions to recordings/masters via
        explicit many-to-many relationships without ownership inheritance.
        Evidence: relationship tests.
  - [ ] E5.6.c Present separate forms/tables/status/search/approvals for each
        right with distinct labels/explanations and permissions. Evidence: UI
        tests.
  - [ ] E5.6.d Compute release/territory/target eligibility from both required
        rights independently and show exactly which is missing/disputed.
        Evidence: gate tests.
  - [ ] E5.6.e Test publishing-only/master-only/same-party/different-party/
        cover/remix/sample/license/admin fixtures and API/export mappings.
        Evidence: noncollapse suite.
- [ ] E5.7 Build melodic, lyrical, recording, and sample similarity screening
      with source corpus/version and uncertainty. Evidence: calibrated
      evaluation.
  - [ ] E5.7.a Define separate screening tasks/features/candidate corpora/
        licences/permissions/retention and protected/unreleased exclusions.
        Evidence: screening policy.
  - [ ] E5.7.b Screen melody/harmony/rhythm using symbolic/audio-derived
        segments with transposition/tempo/variation controls and aligned
        evidence. Evidence: melodic tests.
  - [ ] E5.7.c Screen lyrics using locale/script/phonetic/semantic/exact-near
        spans while distinguishing common phrases/quotes/licensed reuse.
        Evidence: lyric tests.
  - [ ] E5.7.d Screen recordings/audio fingerprints and samples/interpolations
        with exact ranges, transforms, confidence, and corpus/source identity.
        Evidence: audio tests.
  - [ ] E5.7.e Persist model/index/corpus/version/features/threshold/raw
        matches/ uncertainty separately from rights verdict. Evidence:
        provenance tests.
  - [ ] E5.7.f Evaluate precision/recall/calibration/subgroup/genre/locale/
        transform slices and protected corpus leakage. Evidence: calibrated
        report.
- [ ] E5.8 Treat similarity as review evidence under ratified policy, not an
      automatic infringement verdict. Evidence: UI copy and gate policy.
  - [ ] E5.8.a Define versioned policy mapping similarity kind/score/confidence/
        corpus/source/use/context to no-action/review/more-evidence/specialist
        outcomes. Evidence: policy contract.
  - [ ] E5.8.b Display aligned match excerpts/ranges/transforms/corpus rights/
        uncertainty/model version and plain-language limitations. Evidence: UI
        tests.
  - [ ] E5.8.c Distinguish common elements, public-domain/licensed/authorized
        reuse, independent creation, reference, sample, interpolation, and
        unknown context. Evidence: review fixtures.
  - [ ] E5.8.d Require named human rights/legal review for material findings and
        record rationale/evidence/decision/conditions/dissent. Evidence:
        decision tests.
  - [ ] E5.8.e Prohibit automatic infringement accusation/takedown/payment/
        project mutation or source/artist reputation score. Evidence: misuse
        tests.
  - [ ] E5.8.f Recompute/invalidate on subject/corpus/model/policy/rights
        changes and preserve prior measurement/decision separately. Evidence:
        lifecycle tests.
- [ ] E5.9 Build AI-generated and AI-assisted disclosure metadata. Evidence:
      source run IDs and accepted edits.
  - [ ] E5.9.a Define disclosure subject/scope/mode/generated-versus-assisted/
        operation/materiality/model/provider/version/time/locale/target schema.
        Evidence: metadata contract.
  - [ ] E5.9.b Link exact agent/generation/edit run IDs, input/reference/source
        ingredients, output ranges/stems, and accepted human edits/actor.
        Evidence: lineage tests.
  - [ ] E5.9.c Define internal reviewer, rights/provenance, distributor/DDEX,
        C2PA, credit, and listener-visible disclosure requirements by profile.
        Evidence: mapping matrix.
  - [ ] E5.9.d Generate/edit reviewed wording and machine metadata without
        exposing prompts, protected identity, contracts, or proprietary model
        data. Evidence: projection tests.
  - [ ] E5.9.e Invalidate disclosures on content/run/model/rights/target change
        and block release if current required metadata is missing. Evidence:
        gate tests.
  - [ ] E5.9.f Round-trip disclosure through project/master/package/DDEX/
        distributor/catalog/read-back and correction. Evidence: parity tests.
- [ ] E5.10 Build C2PA/external provenance for supported audio/artwork/package
      formats. Evidence: sign, validate, tamper, strip, recover.
  - [ ] E5.10.a Pin supported C2PA/external ledger profiles/formats/algorithms/
        assertions/signers/trust stores/key lifecycle and target capabilities.
        Evidence: provenance profile.
  - [ ] E5.10.b Build manifest/ledger entry from exact audio/artwork/package
        ingredients, recording/generation/edit/mix/master actions, rights, and
        release revisions. Evidence: manifest tests.
  - [ ] E5.10.c Sign in isolated authorized service/HSM or equivalent with
        idempotency/timestamp/certificate chain/audit and no key exposure.
        Evidence: signing tests.
  - [ ] E5.10.d Embed/associate per format/target, preserve through supported
        transforms, and link stripped/external provenance explicitly. Evidence:
        packaging tests.
  - [ ] E5.10.e Validate hashes/signatures/ingredient/action lineage/trust/
        revocation/expiry/content binding and display precise status. Evidence:
        validation tests.
  - [ ] E5.10.f Test valid/tampered/replaced/stripped/unknown/untrusted/expired/
        malformed/recovered cases plus catalog/distributor read-back. Evidence:
        provenance suite.
- [ ] E5.11 Build rights/consent/model/sample blast radius. Evidence: revoke
      preview reaches projects, clips, mixes, masters, releases, and catalog.
  - [ ] E5.11.a Ingest or draft exact rights/consent/model/sample change/
        revocation/expiry/dispute event with evidence/scope/time/verification.
        Evidence: event contract.
  - [ ] E5.11.b Traverse versioned rights subject→sample/reference/run→media/
        clip/take/stem→session/mix/master→release/delivery/catalog dependency
        graph. Evidence: impact query.
  - [ ] E5.11.c Include local/offline projects, frozen/bounced renders,
        collaborations/review links, provider copies, packages/distributors/
        territories and unknown consumers. Evidence: coverage tests.
  - [ ] E5.11.d Show exact affected range/use/right/consent/model/territory/
        target/current state/proposed remedy/owner/urgency with protected
        redaction. Evidence: preview UI.
  - [ ] E5.11.e Freeze read-only impact snapshot, reconcile to consumer/
        capability registry, and require separate authorized remedy plan.
        Evidence: no-mutation tests.
  - [ ] E5.11.f Test complete project→clip→mix→master→release→catalog paths,
        stale/unauthorized/missing/offline nodes, and graph changes before
        execution. Evidence: blast-radius suite.
- [ ] E5.12 Build quarantine, replacement, re-clearance, takedown, and
      correction workflows. Evidence: released-item journey.
  - [ ] E5.12.a Quarantine affected inputs/outputs/projects/jobs/exports/
        delivery candidates by exact version/range while preserving evidence and
        authorized investigation access. Evidence: quarantine tests.
  - [ ] E5.12.b Create remedy plan with
        replace/edit/remove/re-record/regenerate/
        relicense/reconsent/recredit/territory-limit/takedown actions and
        dependencies. Evidence: plan schema.
  - [ ] E5.12.c Implement replacement through new media/clip/mix/master/release
        revisions with provenance, semantic/audio diff, revalidation, and
        approvals. Evidence: replacement tests.
  - [ ] E5.12.d Re-clear or renew exact rights/consent/splits/model/sample uses
        with current parties/terms/signatures and no retroactive silent scope.
        Evidence: re-clearance tests.
  - [ ] E5.12.e Execute distributor/catalog/project/share takedown/restriction
        saga with per-target status, retry, read-back, history, and notices.
        Evidence: takedown tests.
  - [ ] E5.12.f Execute metadata/audio/provenance/credit correction/redelivery
        with prior/current versions and verified downstream state. Evidence:
        correction tests.
  - [ ] E5.12.g Run a released-item revocation→quarantine→replacement or
        takedown→re-clearance→redelivery journey with unresolved target closure
        blocking. Evidence: E2E.
- [ ] E5.13 Protect contracts, identity, voiceprints, and unreleased media in
      logs, analytics, notifications, exports, and support tools. Evidence:
      redaction suite.
  - [ ] E5.13.a Classify exact protected fields/artifacts and allowed purpose/
        roles/projections for each named observability/output/support surface.
        Evidence: redaction matrix.
  - [ ] E5.13.b Apply allowlisted structured logging/tracing/metrics with opaque
        IDs, payload scrubbing, sampling/retention/access, and no audio/text
        excerpts. Evidence: telemetry tests.
  - [ ] E5.13.c Apply cohort/privacy floors and prohibit identity/voiceprint/
        contract/unreleased metadata dimensions or model-training reuse in
        analytics. Evidence: analytics tests.
  - [ ] E5.13.d Build permission-filtered notification templates/deep links/
        previews with device/channel/lock-screen redaction and no existence
        leaks. Evidence: notification tests.
  - [ ] E5.13.e Build purpose-bound audited exports/support bundles with field/
        file redaction, encryption, watermark, expiry, and safe diagnostics.
        Evidence: export/support tests.
  - [ ] E5.13.f Inspect UI/network/logs/traces/crash reports/cache/search/queue/
        backups/notifications/exports/support across roles/tenants. Evidence:
        redaction suite.
- [ ] E5.14 Build authorized, scoped, time-bounded waivers. Evidence: expired
      waiver invalidates release.
  - [ ] E5.14.a Define waiverable versus nonwaivable rights/consent/provenance/
        technical/target rules and required authority by severity. Evidence:
        waiver policy.
  - [ ] E5.14.b Create waiver for exact subject/use/measurement/gate/profile/
        project/release/territory/target/version with reason/evidence. Evidence:
        waiver schema.
  - [ ] E5.14.c Require qualified current human approver, step-up/independence/
        quorum as applicable, compensating controls, conditions, and explicit
        expiry. Evidence: authority tests.
  - [ ] E5.14.d Store immutable signed record separately from underlying
        finding/ measurement/rights state and prevent agent/service
        impersonation. Evidence: integrity tests.
  - [ ] E5.14.e Invalidate on expiry/revocation/subject/profile/target/rights/
        consent/approver/condition change and notify release owners. Evidence:
        lifecycle tests.
  - [ ] E5.14.f Recompute release eligibility and block delivery/catalog
        continuation when waiver is missing/stale/expired; preserve history.
        Evidence: release gate tests.

### E6 — Collaboration, review, realtime, and approvals

- [ ] E6.1 Build project membership and role assignment. Evidence: least
      privilege and cross-project isolation.
  - [ ] E6.1.a Define membership identity/project/person/service/role/scope/
        status/inviter/approver/effective/expiry and inheritance. Evidence:
        membership schema.
  - [ ] E6.1.b Implement invite/request/accept/decline/assign/change/suspend/
        revoke/expire with identity verification and current project authority.
        Evidence: lifecycle tests.
  - [ ] E6.1.c Enforce least-privilege object/field/function/media/device/
        rights/review/release permissions and role incompatibilities. Evidence:
        authorization tests.
  - [ ] E6.1.d Apply role changes to active sessions/offline caches/review
        links/ collaboration streams/jobs/search/downloads promptly. Evidence:
        revocation tests.
  - [ ] E6.1.e Test overlapping IDs/names and direct object/function/filter/
        count/cache/websocket/media access across two projects/tenants.
        Evidence: isolation suite.
- [ ] E6.2 Build presence, selection, transport-leader, and edit-lock semantics.
      Evidence: disconnect and stale-lock tests.
  - [ ] E6.2.a Define ephemeral presence/session/device/user/window/project/
        workspace/last-seen/expiry and privacy-safe display. Evidence: presence
        contract.
  - [ ] E6.2.b Broadcast permission-filtered selection/cursor/playhead/range/
        track/clip/score/lyric context with rate limits and no media/rights
        leakage. Evidence: selection tests.
  - [ ] E6.2.c Define transport leadership request/grant/transfer/revoke/expiry/
        conflict and follower behavior for play/seek/record/loop/tempo.
        Evidence: leader state machine.
  - [ ] E6.2.d Define edit locks/reservations by data kind/range/object with
        optimistic operations where safe, owner/reason/lease/heartbeat/override.
        Evidence: lock contract.
  - [ ] E6.2.e Handle disconnect/sleep/crash/network partition/reconnect/role
        revoke with stale expiry, recovery, and no permanent lock or dual
        leader. Evidence: fault tests.
  - [ ] E6.2.f Announce ownership/lock changes accessibly and test two-client/
        multi-window/realtime ordering/deduplication. Evidence: collaboration
        E2E.
- [ ] E6.3 Build collaboration for score, lyrics, arrangement, clips, mix,
      rights, and release metadata. Evidence: conflict policy per data type.
  - [ ] E6.3.a Define operation schemas/identity/causality/checkpoints/
        authorization for each named structured data type. Evidence: protocol
        contracts.
  - [ ] E6.3.b Choose CRDT/OT/optimistic concurrency/locks per data type based
        on semantic merge safety and document decisions. Evidence: conflict
        matrix.
  - [ ] E6.3.c Implement concurrent score/lyric/arrangement/clip edits with
        stable anchors/order/ranges/moves and deterministic convergence.
        Evidence: creative tests.
  - [ ] E6.3.d Implement mix graph/automation/device/routing edits with cycle/
        parameter/version/transport safety and explicit semantic conflicts.
        Evidence: mix tests.
  - [ ] E6.3.e Implement rights/split/consent/release metadata changes through
        stricter expected-version/review/signature rules, never field-level
        blind merge. Evidence: governance tests.
  - [ ] E6.3.f Reauthorize/rebase/deduplicate offline operations, present mine/
        theirs/base conflicts, preserve copies, audit, and invalidate approvals.
        Evidence: two-client suite.
- [ ] E6.4 Build anchored comments on bars/beats, score events, lyric ranges,
      clips, time ranges, automation, devices, mixes, masters, and rights.
      Evidence: anchor survival.
  - [ ] E6.4.a Define comment/thread identity/subject kind/version/semantic
        anchor/author/visibility/state/mentions and immutable history. Evidence:
        comment schema.
  - [ ] E6.4.b Implement musical-time/score-event/lyric-range/clip-source/
        sample-time/automation-point/device-parameter anchors. Evidence: anchor
        registry.
  - [ ] E6.4.c Implement mix/master revision and protected rights/contract/
        split field anchors with role-filtered projections. Evidence: governed
        anchor tests.
  - [ ] E6.4.d Map anchors through
        insert/delete/move/stretch/tempo/comp/revision changes or mark
        ambiguous/orphaned with candidates, never silently retarget. Evidence:
        survival corpus.
  - [ ] E6.4.e Implement reply/mention/resolve/reopen/suggestion/notification/
        deep-link/playback-focus behavior with redaction. Evidence: workflow
        tests.
  - [ ] E6.4.f Test web/desktop/offline/concurrent edits/export/archive/legal-
        hold and accessible keyboard/screen-reader journeys. Evidence:
        collaboration E2E.
- [ ] E6.5 Build review versions and synchronized playback. Evidence: comment
      always refers to immutable audio/version.
  - [ ] E6.5.a Create immutable review manifest pinning project/arrangement/
        session/mix/master/media/score/lyrics/metadata/rights/profile versions.
        Evidence: review version schema.
  - [ ] E6.5.b Generate or associate checksum-addressed review audio/video/
        waveform/transcript/score proxies with exact range/layout/loudness
        profile. Evidence: proxy tests.
  - [ ] E6.5.c Synchronize play/pause/seek/loop/rate/playhead/time display and
        optional leader/follower across authorized reviewers with drift bounds.
        Evidence: playback tests.
  - [ ] E6.5.d Create comments/annotations only against immutable review version
        and exact time/musical/semantic anchor. Evidence: anchoring tests.
  - [ ] E6.5.e Show stale/superseded/missing/expired review versions distinctly
        and preserve previous playback/comments without retargeting. Evidence:
        lifecycle tests.
  - [ ] E6.5.f Test network jitter/reconnect/range requests/device change/
        unauthorized download and screen-reader media controls. Evidence: review
        E2E.
- [ ] E6.6 Build take, arrangement, mix, and master compare with level-matched
      playback where applicable. Evidence: comparison tests.
  - [ ] E6.6.a Select immutable compatible variants and resolve time/tempo/
        arrangement/range/layout/source/profile/permission alignment. Evidence:
        compare contract.
  - [ ] E6.6.b Implement synchronized A/B/solo/blind/loop/scrub with transparent
        loudness/peak level matching and clip-safe gain. Evidence: playback
        tests.
  - [ ] E6.6.c Show take/comp/source, arrangement section/clip, mix graph/
        automation, master settings/render/measurement semantic diffs. Evidence:
        structured diff tests.
  - [ ] E6.6.d Show provenance/rights/cost/gates/reviews/freshness and do not
        infer winner from loudness or similarity. Evidence: evidence compare.
  - [ ] E6.6.e Support comments and human select/reject/park/no-decision with
        stable links and no variant mutation. Evidence: decision tests.
  - [ ] E6.6.f Verify sample synchronization/level match/mono-spatial layouts,
        keyboard/screen-reader parity, and large media performance. Evidence:
        comparison suite.
- [ ] E6.7 Build review templates for creative, performance, engineering, mix,
      master, rights, accessibility/metadata, label, and distribution. Evidence:
      role/quorum matrix.
  - [ ] E6.7.a Define versioned template identity/applicability/priority and
        required checklist/measurement/gate/evidence/reviewer/decision fields.
        Evidence: template schema.
  - [ ] E6.7.b Define independent creative/performance/engineering/mix/master/
        rights/accessibility-metadata/label/distribution scopes. Evidence:
        template registry.
  - [ ] E6.7.c Resolve templates by project/release profile/risk/territory/
        target/host/version and compose code/policy-owned floors. Evidence:
        resolution tests.
  - [ ] E6.7.d Define reviewer role/credential/domain/independence/recusal/
        quorum/order/deadline/delegate and nondelegable human decisions.
        Evidence: role/quorum matrix.
  - [ ] E6.7.e Govern draft/review/approve/activate/supersede/deprecate/migrate/
        expire with immutable versions and impact preview. Evidence: governance
        tests.
  - [ ] E6.7.f Preview exact review plan and fail missing/conflicting/
        unversioned requirements. Evidence: review-plan gate.
- [ ] E6.8 Build approval locks and invalidation on dependent change. Evidence:
      source mix or rights change invalidates master/release signoff.
  - [ ] E6.8.a Build transitive dependency manifest/hash for each approval over
        exact project/media/score/lyrics/session/mix/master/rights/profile/
        release versions. Evidence: lock contract.
  - [ ] E6.8.b Acquire immutable approval lock at decision with actor/role/
        scope/conditions/signature/time and prohibit manifest mutation.
        Evidence: approval tests.
  - [ ] E6.8.c Subscribe/reconcile dependency changes and map materiality to
        stale/invalidate/review-required per approval type. Evidence:
        invalidation rules.
  - [ ] E6.8.d Atomically recheck dependencies before render/delivery/release/
        takedown execution to close race windows. Evidence: race tests.
  - [ ] E6.8.e Surface exact invalidating dependency/version/change/affected
        approval/remediation and notify responsible reviewers safely. Evidence:
        status tests.
  - [ ] E6.8.f Verify source mix/media/plugin/profile/rights/consent/splits/
        metadata/target change scenarios and immutable prior approvals.
        Evidence: dependency suite.
- [ ] E6.9 Build collaborator invites, guest review links, expiry, watermark,
      download control, and revocation. Evidence: link security tests.
  - [ ] E6.9.a Define invite/link identity/purpose/project/review version/
        recipient-role/allowed fields-actions/expiry/max uses/device/session and
        revocation. Evidence: access schema.
  - [ ] E6.9.b Issue high-entropy signed single/multi-use links with no secret
        in analytics/referrer/logs, optional identity proof/step-up, and safe
        landing. Evidence: link tests.
  - [ ] E6.9.c Apply permission-filtered media/score/lyrics/comment review view,
        dynamic visible/audible watermark, redacted rights/unreleased metadata,
        and no project mutation. Evidence: guest view.
  - [ ] E6.9.d Enforce stream/range/download/original/proxy limits, expiration/
        offline caching/print/copy controls, and truthful screenshot limits.
        Evidence: download tests.
  - [ ] E6.9.e Revoke immediately across active sessions/cache/CDN/signed media
        URLs and audit attempts/success without sensitive payload. Evidence:
        revocation tests.
  - [ ] E6.9.f Test guessing/replay/forwarding/cross-project/substitution/
        expired/revoked/referrer/log/watermark bypass and role change. Evidence:
        link security suite.
- [ ] E6.10 Build notifications with unreleased-content redaction and quiet
      hours. Evidence: routing fixtures.
  - [ ] E6.10.a Define event/template/channel/recipient/urgency/dedupe/expiry/
        action schema and per-data-class redaction rules. Evidence: notification
        contracts.
  - [ ] E6.10.b Resolve current project membership/role/rights access/device/
        channel/preferences/locale/time zone before rendering/delivery.
        Evidence: recipient tests.
  - [ ] E6.10.c Redact titles/artists/file names/audio/waveforms/lyrics/
        contracts/voice/model/release dates and use opaque authenticated deep
        links as required. Evidence: redaction tests.
  - [ ] E6.10.d Apply quiet hours/frequency/dedupe/digest with narrowly defined
        recording/live/safety/release emergency override authority. Evidence:
        routing tests.
  - [ ] E6.10.e Deliver idempotently with provider acknowledgment/retry/
        suppression/bounce and mark partial/failed honestly. Evidence: delivery
        tests.
  - [ ] E6.10.f Test lock-screen/email/push/in-app/webhook/log/analytics
        projections, role revoke, project archive, and two projects. Evidence:
        routing fixtures.
- [ ] E6.11 Build realtime session health and graceful degradation. Evidence:
      collaboration outage keeps local work safe.
  - [ ] E6.11.a Define realtime connection/auth/protocol/peer/leader/clock/
        operation queue/ack/lag/presence/quality state and telemetry. Evidence:
        health contract.
  - [ ] E6.11.b Measure RTT/jitter/loss/reconnect/operation backlog/ack age/
        clock drift/audio dropout and calibrate healthy/degraded/offline
        thresholds. Evidence: health tests.
  - [ ] E6.11.c Show persistent accessible connection/sync/leader/unsynced edit/
        recording safety state with actionable diagnosis and no false online.
        Evidence: UI tests.
  - [ ] E6.11.d Degrade presence/live cursors/synchronized playback/audio
        monitor first while preserving local edit/record/autosave/export where
        safe. Evidence: degradation tests.
  - [ ] E6.11.e Queue local operations/media durably and reauthenticate/
        reauthorize/rebase/dedupe/conflict-resolve on reconnect. Evidence:
        recovery tests.
  - [ ] E6.11.f Inject service outage/partition/high jitter/protocol skew/leader
        loss/role revoke/storage pressure and prove local work remains safe.
        Evidence: outage suite.
- [ ] E6.12 Build immutable review and approval audit. Evidence: automation
      cannot impersonate a named approver.
  - [ ] E6.12.a Define append-only review/decision/approval/waiver event with
        actor identity/role/scope/subject manifest/action/reason/evidence/time/
        signature. Evidence: audit schema.
  - [ ] E6.12.b Capture comments/checklists/measurements/changes/conditions/
        conflicts/recusals/quorum and exact versions through transactional
        outbox. Evidence: audit tests.
  - [ ] E6.12.c Chain/hash/sign events or manifests, preserve ordering/
        supersession/revocation, and prohibit update/delete of history.
        Evidence: tamper tests.
  - [ ] E6.12.d Build protected/redacted permissioned audit query/export with
        stable cursor/completeness/version and no contract/voice/media leak.
        Evidence: projection tests.
  - [ ] E6.12.e Attempt model/agent/service account/header/DB replay/identity/
        role/signature impersonation and prove rejection/no side effect.
        Evidence: non-impersonation tests.
  - [ ] E6.12.f Verify backup/restore/replay/version migration/completeness and
        independent tamper detection. Evidence: integrity report.
- [ ] E6.13 Build remote rehearsal/recording and live collaboration with
      clock/tempo authority, cueing, low-latency monitor versus
      quality-recording paths, dropout concealment, local capture, upload,
      alignment, consent, and reconnect. Evidence: network-impairment tests
      preserve the authoritative recordings.
  - [ ] E6.13.a Define remote session/participant/role/device/route/clock/
        transport/tempo/cue/recording/consent/security and capability profile.
        Evidence: session contract.
  - [ ] E6.13.b Enroll/calibrate participant audio/MIDI devices, input levels/
        channels/clock/round-trip latency, permissions, and performer consent.
        Evidence: setup journey.
  - [ ] E6.13.c Elect/transfer clock-tempo-transport authority and distribute
        count-in/click/cues/markers/talkback with drift/jitter bounds. Evidence:
        sync tests.
  - [ ] E6.13.d Separate low-latency monitor stream from local quality
        recording, label codec/dropout concealment, and never treat monitor
        capture as authoritative. Evidence: path tests.
  - [ ] E6.13.e Record crash-safe local originals with checksums/device/session/
        performer provenance, upload resumably, deduplicate, and retain consent
        scope. Evidence: capture tests.
  - [ ] E6.13.f Align takes using clocks/cues/correlation/manual correction with
        confidence, preserving raw timing and dropout/missing spans. Evidence:
        alignment tests.
  - [ ] E6.13.g Handle disconnect/reconnect/leader loss/network partition/
        device change/role revoke with local continuity and no duplicate or lost
        takes. Evidence: recovery tests.
  - [ ] E6.13.h Run latency/jitter/loss/reorder/bandwidth/outage/clock drift/
        process crash tests and verify authoritative local recordings. Evidence:
        impairment report.
- [ ] E6.14 Build stage/performance mode for set list, scene/patch changes,
      stems/click/cues, MIDI/OSC/lighting control where ratified, redundancy,
      rehearsal, operator lockout, emergency stop, recording, and post-show
      ingest. Evidence: safe device-control allowlists and offline/failover
      drills.
  - [ ] E6.14.a Define locked show/set/song/scene/cue/stem/click/patch/control/
        device/operator/revision manifest and performance capability profile.
        Evidence: show contract.
  - [ ] E6.14.b Build set-list/scene transition/rehearsal editor with preflight
        for media/plugins/presets/devices/routes/tempo/timecode/rights and
        offline package. Evidence: preflight tests.
  - [ ] E6.14.c Implement sample-accurate stem/click/cue playback and safe
        quantized/manual scene/patch changes with redundant local media/
        transport. Evidence: playback tests.
  - [ ] E6.14.d Implement allowlisted typed MIDI/OSC/lighting or ratified
        controls with target/range/rate/state/ack/feedback-loop constraints.
        Evidence: device control tests.
  - [ ] E6.14.e Lock operator UI/actions/permissions during show, require
        step-up for dangerous changes, and provide immediate audio/control
        emergency stop/manual fallback. Evidence: safety tests.
  - [ ] E6.14.f Implement primary/backup device/computer/audio/output/clock/
        power/network failover with health, rehearsal, switchover, and rollback.
        Evidence: redundancy drills.
  - [ ] E6.14.g Record program/stems/MIDI/control/operator/incident timeline
        locally with consent/provenance and ingest/reconcile post-show.
        Evidence: recording tests.
  - [ ] E6.14.h Run offline/device/plugin/media/clock/network/controller/crash/
        failover/emergency-stop drills and prove no unauthorized commands.
        Evidence: stage game-day.

### E7 — Persistence, APIs, rendering, and distribution

- [ ] E7.1 Decide authoritative project, media, rights, collaboration, and
      release stores. Evidence: ownership ADR.
  - [x] E7.1.a Inventory current local/OPFS/desktop/database/blob/index/cache/
        realtime/provider/distributor records and all writers/readers. Evidence:
        storage inventory.
        <!-- euterpe-storage-inventory.json: storage inventory of the Euterpe Tauri
                                                                                                                                                                                                                                                                        DAW. Of the 9 named storage layers, 5 are PRESENT — local (localStorage, 4
                                                                                                                                                                                                                                                                        writers / 4 readers), desktop (4 Tauri commands: save_project/load_project/
                                                                                                                                                                                                                                                                        export_stems/reveal_in_file_manager), blob (Tauri export_stems + Blob),
                                                                                                                                                                                                                                                                        realtime (yjs/CRDT collaboration state), distributor (delivery targets) — and
                                                                                                                                                                                                                                                                        4 are ABSENT: OPFS, database (IndexedDB/SQLite), index, cache. FINDING:
                                                                                                                                                                                                                                                                        Euterpe is DESKTOP-FILE-CENTRIC (persistence via Tauri filesystem commands) +
                                                                                                                                                                                                                                                                        localStorage for client state, with NO browser-persistent storage. 14
                                                                                                                                                                                                                                                                        project-store modules. Used PRECISE API-signature patterns (navigator.storage.
                                                                                                                                                                                                                                                                        getDirectory, indexedDB.open, caches.open, new Y.Doc) — loose patterns
                                                                                                                                                                                                                                                                        (\bidb\b/getDirectory/"indexing") gave false positives that wrongly marked
                                                                                                                                                                                                                                                                        OPFS/database/index present. generate-euterpe-storage-inventory.mjs + 6 tests,
                                                                                                                                                                                                                                                                        wired into inventory/verify + CI. E7.1.b (aggregate/transaction ownership) +
                                                                                                                                                                                                                                                                        later E7.1.* are design and stay unchecked. -->
  - [ ] E7.1.b Define authoritative aggregate/transaction ownership, immutable/
        mutable data, durability/latency class, and offline replica per domain.
        Evidence: ownership matrix.
  - [ ] E7.1.c Define media/blob versus project graph, rights/protected records,
        collaboration operations/checkpoints, and release/delivery boundaries.
        Evidence: data architecture.
  - [ ] E7.1.d Define derived waveform/search/read model/cache/distributor
        projections, freshness/rebuild/reconciliation, and forbidden reverse
        writes. Evidence: projection contracts.
  - [ ] E7.1.e Resolve duplicate ownership/migrations/outbox/saga/backups/
        recovery/region/encryption and accountable services. Evidence: approved
        ADR.
- [ ] E7.2 Add missing schemas and migrations without duplicating the existing
      DAW project model. Evidence: old-project migration rehearsal.
  - [ ] E7.2.a Derive missing durable entities/columns/indexes/constraints from
        canonical contracts and required queries while reusing existing project
        aggregate. Evidence: schema diff.
  - [ ] E7.2.b Define adapter/reference boundaries from normalized service data
        to embedded DAW project state; prohibit a parallel session graph.
        Evidence: model review.
  - [ ] E7.2.c Design expand/backfill/dual-read-write/switch/contract migrations
        with chunking/checkpoints/idempotency/throttling/rollback. Evidence:
        migration plan.
  - [ ] E7.2.d Preserve timing/media hashes/device state/automation/rights/
        provenance/history and validate references/state mappings. Evidence:
        migration tests.
  - [ ] E7.2.e Rehearse old/large/missing-plugin/offline/concurrent projects on
        production-equivalent stores and compare semantic/audio goldens.
        Evidence: rehearsal report.
  - [ ] E7.2.f Roll out with metrics/reconciliation/stop conditions and remove
        legacy paths only after browser/desktop/realtime/render compatibility.
        Evidence: migration gate.
- [ ] E7.3 Store large media as immutable checksum-addressed blobs with
      resumable upload and derived proxies. Evidence: byte rehash and recovery.
  - [ ] E7.3.a Define blob identity/hash algorithm/tenant/project/data class/
        size/type/original filename/source/rights/retention/encryption/region.
        Evidence: blob schema.
  - [ ] E7.3.b Implement multipart/chunk resumable upload with content range/
        per-chunk and final hash/size/type/quota/idempotency/session expiry.
        Evidence: upload tests.
  - [ ] E7.3.c Quarantine/scan/probe originals before available use and commit
        metadata/reference only after durable complete blob. Evidence:
        completion tests.
  - [ ] E7.3.d Generate waveform/proxy/transcode/analysis derivatives with
        immutable parent/hash/tool/profile/version lineage and independent
        failure. Evidence: proxy tests.
  - [ ] E7.3.e Serve authorized range/stream/download via short-lived scoped
        URLs or service path with no cross-project/cache/path leakage. Evidence:
        access tests.
  - [ ] E7.3.f Inject interrupted/duplicate/out-of-order/corrupt upload, process
        crash, blob/database mismatch, restore, and rehash all bytes. Evidence:
        recovery suite.
- [ ] E7.4 Implement project/session/revision APIs with optimistic concurrency.
      Evidence: contract suite.
  - [ ] E7.4.a Define versioned create/read/query/patch/command/transition/
        snapshot/diff contracts, errors, idempotency, pagination, and limits.
        Evidence: API schemas.
  - [ ] E7.4.b Enforce tenant/project/role/object/field/function/host
        capability/ maturity authorization and redacted projections. Evidence:
        auth tests.
  - [ ] E7.4.c Require expected version/ETag or operation causality for
        mutation, return structured current/conflict, and prevent silent
        overwrite. Evidence: concurrency tests.
  - [ ] E7.4.d Persist immutable revisions/operations/audit/outbox atomically
        with durable-before-ack and stable media references. Evidence:
        durability tests.
  - [ ] E7.4.e Support batch/command transactions and validate timing/routing/
        state invariants server-side rather than trusting client. Evidence:
        invariant tests.
  - [ ] E7.4.f Test retries/timeout-after-commit/stale/offline replay/mass
        assignment/cross-project/unknown/oversized/version skew. Evidence:
        contract suite.
- [ ] E7.5 Implement media ingest, analysis, proxy, render, bounce, mix, and
      master jobs. Evidence: durable job fault tests.
  - [ ] E7.5.a Define distinct versioned job request/run/attempt/step/result/
        artifact contracts pinned to project/media/range/profile/dependencies/
        rights. Evidence: job schemas.
  - [ ] E7.5.b Implement durable queue/lease/heartbeat/timeout/retry/backoff/
        cancellation/idempotency/priority/concurrency/resource/budget controls.
        Evidence: worker tests.
  - [ ] E7.5.c Implement quarantine ingest/probe/analyze/proxy jobs with
        immutable source/derivative provenance and safe parser isolation.
        Evidence: ingest pipeline.
  - [ ] E7.5.d Implement render/bounce/mix/master jobs with exact graph/plugin/
        state/sample-rate/layout/range/tails/profile and deterministic manifest.
        Evidence: render pipeline.
  - [ ] E7.5.e Persist progress/logs/measurements/artifacts/cost/terminal state
        before acknowledgment and verify hashes/format/audio output. Evidence:
        result tests.
  - [ ] E7.5.f Inject
        database/blob/queue/worker/plugin/provider/device/resource/
        cancellation/timeout-after-commit faults and recover/replay safely.
        Evidence: fault suite.
- [ ] E7.6 Implement rights, consent, splits, and provenance APIs with
      property-level authorization. Evidence: protected-field tests.
  - [ ] E7.6.a Define versioned resource/command/query/review/sign/revoke/
        blast-radius contracts and canonical kind/state/value validation.
        Evidence: API schemas.
  - [ ] E7.6.b Separate public credits/safe rights projection from protected
        identity/contact/contracts/voiceprints/signatures/payment fields.
        Evidence: projection contracts.
  - [ ] E7.6.c Enforce tenant/project/role/purpose/object/field/function/
        territory/state/compartment authorization on read/write/filter/sort/
        export. Evidence: property auth tests.
  - [ ] E7.6.d Implement expected-version/idempotency/immutable revisions/
        signatures/audit/outbox and prevent mass assignment or direct approval
        state. Evidence: mutation tests.
  - [ ] E7.6.e Emit redacted versioned invalidation/revocation events to
        project/ job/review/release/catalog consumers. Evidence: integration
        tests.
  - [ ] E7.6.f Test BOLA/existence/count/snippet/filter/error/cache/log/batch/
        revoked grant/two-project access. Evidence: protected-field suite.
- [ ] E7.7 Implement permission-aware project, asset, sample, and catalog
      search. Evidence: unreleased metadata does not leak.
  - [ ] E7.7.a Define document/field classification, tenant/project/catalog/
        role/right/release/territory/compartment ACL, freshness, locale, and
        deletion schema. Evidence: search model.
  - [ ] E7.7.b Index only allowlisted projections from authoritative events with
        content/version hashes and protected contract/voice/unreleased fields
        excluded. Evidence: indexing tests.
  - [ ] E7.7.c Apply authorization before retrieval/aggregation/ranking/
        highlighting/vector search and stable cursor pagination. Evidence: query
        tests.
  - [ ] E7.7.d Prevent autocomplete/suggestion/snippet/count/facet/timing/cache/
        analytics/log leaks of unreleased or cross-project items. Evidence:
        leakage tests.
  - [ ] E7.7.e Process role/release/territory/rights/revocation/takedown/delete
        promptly and reconcile index to source of truth. Evidence: update tests.
  - [ ] E7.7.f Run two-tenant/project/multi-role/existence-oracle/BOLA/
        injection/stale-index/large-catalog adversarial suite. Evidence: search
        report.
- [ ] E7.8 Implement realtime collaboration protocol versioning and recovery.
      Evidence: version-skew tests.
  - [ ] E7.8.a Define handshake/protocol/schema/capability/host/client versions,
        auth/session/project/actor IDs, resume cursor, and support window.
        Evidence: protocol contract.
  - [ ] E7.8.b Version operation/event/error/presence/selection/leader/lock/
        checkpoint/ack envelopes with unknown-field/type handling. Evidence:
        message schemas.
  - [ ] E7.8.c Negotiate compatible subset or fail/read-only/export explicitly;
        never silently reinterpret musical timing or rights operations.
        Evidence: negotiation tests.
  - [ ] E7.8.d Persist/resume operations with ordering/causality/deduplication/
        replay/checkpoints/compaction and current authorization. Evidence:
        recovery tests.
  - [ ] E7.8.e Rebase/migrate queued old-client/offline operations or surface
        semantic conflict/copied project without data loss. Evidence: migration
        tests.
  - [ ] E7.8.f Test old/new web/desktop/service combinations, rolling deploy,
        reconnect after history compaction, unknown messages, and role revoke.
        Evidence: version-skew suite.
- [ ] E7.9 Implement deterministic release bundle assembly. Evidence: audio,
      artwork, metadata, rights, provenance, and delivery hash goldens.
  - [ ] E7.9.a Create immutable release candidate manifest pinning sequence/
        recordings/masters/renditions/artwork/lyrics/credits/metadata/rights/
        splits/consent/provenance/profiles/targets. Evidence: candidate schema.
  - [ ] E7.9.b Resolve target/territory/locale-specific files/metadata/
        identifiers/advisories/rights/disclosures and explicit loss/ unsupported
        state. Evidence: resolution tests.
  - [ ] E7.9.c Render/copy/package only checksum-verified immutable artifacts
        with canonical names/paths/order/permissions and no hidden mutable
        source. Evidence: assembly tests.
  - [ ] E7.9.d Generate canonical manifest and per-file/package hashes/
        signatures/C2PA or ledger references plus tool/config/build versions.
        Evidence: hash goldens.
  - [ ] E7.9.e Verify target format/loudness/metadata/artwork/rights/DDEX/
        provenance and compare package to locked candidate before approval.
        Evidence: bundle gate.
  - [ ] E7.9.f Rebuild from same inputs across workers and compare bytes/hashes
        or explicitly documented nondeterministic fields. Evidence: determinism
        tests.
- [ ] E7.10 Implement DDEX ERN generation/validation for the ratified profile.
      Evidence: new release, update, takedown, and territory fixtures.
  - [ ] E7.10.a Pin DDEX ERN/profile/code-list/namespace/schema/validator/
        partner-extension versions and internal mapping owner. Evidence: profile
        manifest.
  - [ ] E7.10.b Map parties/contributors/works/recordings/releases/resources/
        identifiers/titles/genres/languages/rights/territories/deals/technical
        details. Evidence: mapping tests.
  - [ ] E7.10.c Generate deterministic new-release message with stable message/
        party/resource/release IDs and target-specific resource files. Evidence:
        new release golden.
  - [ ] E7.10.d Generate update/redelivery/metadata correction/audio
        replacement/ territory addition-removal/takedown lifecycle messages with
        prior relationships. Evidence: lifecycle goldens.
  - [ ] E7.10.e Validate schema/profile/cardinality/codes/dates/territories/
        identifiers/deals/resources/checksums/rights and safe extensions.
        Evidence: validator tests.
  - [ ] E7.10.f Round-trip through sandbox/contract distributor, reconcile
        acknowledgments/content acceptance, and expose unsupported loss.
        Evidence: conformance report.
- [ ] E7.11 Implement distributor adapters with plan, deliver, acknowledge,
      reconcile, retry, and compensate. Evidence: partial failure tests.
  - [ ] E7.11.a Define per-distributor/version/account/territory/target
        capabilities, credentials, DDEX/API/SFTP transport, limits, schedules,
        and unsupported operations. Evidence: adapter profiles.
  - [ ] E7.11.b Build read-only delivery plan from locked bundle with messages/
        files/metadata/actions/dependencies/idempotency/expected current state/
        costs/loss. Evidence: plan tests.
  - [ ] E7.11.c Deliver in dependency order with resumable transfer, retries/
        backoff/rate, checksums, signed requests, acknowledgments, and durable
        per-action status. Evidence: delivery tests.
  - [ ] E7.11.d Parse transport/message/resource/content acknowledgments and
        errors without treating receipt as release acceptance. Evidence:
        acknowledgment tests.
  - [ ] E7.11.e Query/read back catalog/territory/metadata/resource/availability
        state and semantic-compare to plan. Evidence: reconciliation tests.
  - [ ] E7.11.f Compensate only safe reversible actions, retain partial release
        and operator remediation otherwise, with idempotent retry/replay.
        Evidence: partial failure suite.
- [ ] E7.12 Implement read-back/acknowledgement verification per distributor.
      Evidence: accepted transport without accepted content is not success.
  - [ ] E7.12.a Define distributor-specific acknowledgment layers/codes/
        correlation IDs/status transitions/timing and independent read-back
        capability. Evidence: verification contracts.
  - [ ] E7.12.b Correlate transport/file/message/resource/release/deal/territory
        acknowledgments to exact delivery action and immutable bundle. Evidence:
        correlation tests.
  - [ ] E7.12.c Read back identifiers/sequence/titles/contributors/audio/
        artwork/metadata/rights/territory/date/advisory/provenance/catalog
        state. Evidence: content tests.
  - [ ] E7.12.d Semantic-compare with target-specific transforms/tolerances and
        classify accepted/processing/partial/rejected/mismatch/unknown.
        Evidence: comparator tests.
  - [ ] E7.12.e Keep release/target unresolved until required content/territory
        state is accepted and verified; expose owner/retry/escalation. Evidence:
        closure tests.
  - [ ] E7.12.f Test transport-accepted/content-rejected, delayed processing,
        contradictory callbacks, stale read-back, rate/auth outage, and manual
        reconciliation. Evidence: verification suite.
- [ ] E7.13 Implement takedown, redelivery, metadata correction, audio
      replacement, and territory change sagas. Evidence: idempotent recovery.
  - [ ] E7.13.a Define distinct remedy requests/authority/reason/scope/release/
        target/territory/effective time/desired state and immutable plans.
        Evidence: saga schemas.
  - [ ] E7.13.b Generate target-specific dependency actions/messages/files/
        notices/idempotency keys/preconditions/expected versions and impact.
        Evidence: plan tests.
  - [ ] E7.13.c Execute takedown and territory add/remove/restrict with per-
        target status/history/read-back and no deletion of internal evidence.
        Evidence: availability tests.
  - [ ] E7.13.d Execute redelivery/metadata correction/audio replacement with
        new bundle/version/provenance/QC/rights and prior relationship.
        Evidence: update tests.
  - [ ] E7.13.e Handle duplicate/out-of-order/delayed acknowledgments, timeout
        after commit, partial targets, retries/backoff/cancel/compensation.
        Evidence: fault tests.
  - [ ] E7.13.f Replay from durable checkpoints and reconcile distributor/
        catalog/search/read models without duplicate delivery or stale
        resurrection. Evidence: recovery tests.
- [ ] E7.14 Implement two-tenant/project isolation across APIs, blobs, search,
      collaboration, review links, rendering, and distribution. Evidence:
      integration suite.
  - [ ] E7.14.a Create two tenants/projects with overlapping IDs/names/media/
        rights/roles/plugins/jobs/releases/distributor accounts. Evidence:
        isolation fixture.
  - [ ] E7.14.b Test API
        object/property/function/filter/sort/batch/cache/offline replay and
        service-account authorization. Evidence: API isolation.
  - [ ] E7.14.c Test blob upload/range/proxy/waveform/download/signed URL/key/
        path/metadata/backup substitution. Evidence: media isolation.
  - [ ] E7.14.d Test search suggestions/snippets/counts/facets/vectors/timing
        and collaboration websocket/operations/presence/locks/review links.
        Evidence: discovery/realtime isolation.
  - [ ] E7.14.e Test render/provider/plugin/job queue/payload/artifact/log/
        metrics/dead-letter/callback project context. Evidence: worker
        isolation.
  - [ ] E7.14.f Test distributor accounts/credentials/bundles/messages/
        acknowledgments/read-back/takedown and catalog projections. Evidence:
        delivery isolation.
  - [ ] E7.14.g Attempt guessed IDs/stale grants/admin/emergency/cache
        poisoning/ deep links/cross-project relationships. Evidence: integration
        suite.
- [ ] E7.15 Generate API and realtime consumer contracts. Evidence: Studio,
      service, desktop, and distributor drift gates.
  - [ ] E7.15.a Generate canonical OpenAPI plus realtime
        message/state/capability schemas from authoritative route/domain
        contracts with stable IDs. Evidence: contract artifacts.
  - [ ] E7.15.b Produce Studio/service/desktop/render-worker/provider/
        distributor audience projections without protected internal fields.
        Evidence: consumer schemas.
  - [ ] E7.15.c Add requests/responses/errors/events/commands/fixtures and
        generated clients/types pinned to exact spec/version digest. Evidence:
        fixtures.
  - [ ] E7.15.d Run schema lint/breaking-change/server conformance/unknown
        route- message/unimplemented operation tests. Evidence: provider gate.
  - [ ] E7.15.e Run web/desktop/service/worker/distributor compile and semantic
        contract tests across supported version skew. Evidence: consumer gates.
  - [ ] E7.15.f Fail CI on drift, protected-field exposure, orphan operation,
        stale generated code, unsafe incompatibility, or unversioned protocol.
        Evidence: drift gates.

### E8 — Release calendar, catalog, analytics, and operations

- [ ] E8.1 Build release calendar for recording, mix, master, rights, artwork,
      marketing, delivery, embargo, and street date. Evidence: dependency and
      time-zone tests.
  - [ ] E8.1.a Define typed calendar item/reference for each named milestone/
        window with owner/status/start/end/deadline/time zone/recurrence and
        exact subject revision. Evidence: calendar schema.
  - [ ] E8.1.b Build permissioned day/week/month/agenda views with stable
        project/artist/label/owner/type/status/territory/target filters and
        unreleased redaction. Evidence: view tests.
  - [ ] E8.1.c Create/edit/reschedule/assign through owned workflows with
        dependency/rights/embargo/capacity/target/impact preview, concurrency,
        and audit. Evidence: action tests.
  - [ ] E8.1.d Compute critical path/readiness/conflicts/slack/missed window/
        escalation without converting the calendar into release authority.
        Evidence: dependency tests.
  - [ ] E8.1.e Handle UTC/local/label/distributor/territory time zones, DST,
        ambiguous/nonexistent times, street-date rules, and embargo events.
        Evidence: temporal tests.
  - [ ] E8.1.f Provide agenda/table/forms/non-drag alternatives and run record→
        mix→master→rights→delivery→street-date journey. Evidence: accessible
        E2E.
- [ ] E8.2 Build studio, engineer, performer, review, render, and delivery
      capacity reservations. Evidence: committed slots cannot overbook.
  - [ ] E8.2.a Define resource/skill/credential/device/room/worker/distributor
        capacity, calendar/time zone/availability/privacy, and units. Evidence:
        capacity schema.
  - [ ] E8.2.b Define tentative/held/committed/released/expired reservation with
        project/session/review/job/delivery/window/units/priority/dependencies.
        Evidence: reservation contract.
  - [ ] E8.2.c Check studio/engineer/performer/equipment/consent/travel/review/
        render resource and distributor window constraints. Evidence:
        eligibility tests.
  - [ ] E8.2.d Commit atomically under concurrent booking, enforce exclusive/
        pooled capacity and prevent double booking or worker oversubscription.
        Evidence: concurrency tests.
  - [ ] E8.2.e Reschedule/reassign/cancel/overbook-exception with impact,
        authority, notifications, cost, and audit. Evidence: operations tests.
  - [ ] E8.2.f Test cross-time-zone/recurring/partial capacity/outage/role
        revoke/deadline conflict and release of stale holds. Evidence: capacity
        suite.
- [ ] E8.3 Build album/EP/single/playlist sequencing and versioned curation.
      Evidence: deterministic catalog bundle.
  - [ ] E8.3.a Define product/sequence/version/type/disc-side/position/
        transition/gap/hidden-bonus/locale/territory/window/state schema.
        Evidence: curation contract.
  - [ ] E8.3.b Select exact recording/master/version/ISRC/assets/rights/
        metadata and add/remove/reorder/group using drag and equivalent
        commands. Evidence: editor tests.
  - [ ] E8.3.c Validate duplicate/identifier/version/territory/rights/advisory/
        loudness-gap-continuity/format/target and playlist-specific rules.
        Evidence: eligibility tests.
  - [ ] E8.3.d Preview synchronized sequence/transitions/crossfades/gaps/
        metadata/artwork/credits and accessible ordered list. Evidence: preview
        tests.
  - [ ] E8.3.e Review/approve immutable version with semantic/audio diff and
        invalidate on track/master/rights/metadata/profile change. Evidence:
        review tests.
  - [ ] E8.3.f Assemble repeated builds from same inputs and compare order/
        metadata/files/hashes or declared nondeterminism. Evidence:
        deterministic bundle.
- [ ] E8.4 Build catalog search, relation, version, territory, rights, status,
      takedown, and replacement views. Evidence: permission-aware queries.
  - [ ] E8.4.a Define permission-filtered catalog read model with
        work/recording/
        release/version/party/identifier/territory/rights/status/remedy/
        replacement/freshness fields. Evidence: catalog contract.
  - [ ] E8.4.b Implement text/identifier/facet/semantic search with stable
        pagination/sorts/suggestions and unreleased/protected/territory
        authorization before retrieval. Evidence: search tests.
  - [ ] E8.4.c Show work↔recording↔version↔release↔product/artist/label/
        contributor/sample/provenance relationships with temporal validity.
        Evidence: relation views.
  - [ ] E8.4.d Show per-territory/right-type/party/window eligibility, disputes/
        expiry/waivers and redacted evidence without collapsing work/master.
        Evidence: rights views.
  - [ ] E8.4.e Show per-distributor/target status/ack/read-back/delivery
        history/ errors plus takedown/correction/replacement lineage. Evidence:
        status views.
  - [ ] E8.4.f Test unauthorized titles/counts/snippets/facets/relations, stale
        index, correction/takedown propagation, large catalog, and semantic
        accessibility. Evidence: query suite.
- [ ] E8.5 Build project/release dashboards around action, gate, schedule,
      delivery, and incident status. Evidence: no decorative card mosaic.
  - [ ] E8.5.a Define primary triage questions and live queues for assigned
        work/ blockers/gate failures/upcoming dependencies/delivery exceptions/
        rights incidents with owners/times/severity. Evidence: dashboard
        contract.
  - [ ] E8.5.b Compose restrained task-led hierarchy with urgent actionable work
        first, project/release context and secondary metrics second, no hero or
        repeated card mosaic. Evidence: design review.
  - [ ] E8.5.c Implement stable filters/sorts/grouping/URLs/freshness/live
        updates and permissioned unreleased/rights/cost projections. Evidence:
        query tests.
  - [ ] E8.5.d Implement authorized open/claim/assign/remediate/retry/hold/
        cancel/escalate/review actions with state/version/impact/reason/
        confirmation. Evidence: action tests.
  - [ ] E8.5.e Render loading/empty/stale/offline/degraded/partial/unauthorized/
        fixture/error honestly with accessible recovery. Evidence: state tests.
  - [ ] E8.5.f Verify wide/compact/single-column, keyboard/screen reader, scale/
        live updates, and no sensitive count/title leaks. Evidence: frontend
        conformance E2E.
- [ ] E8.6 Build metadata/localization workflow for title, contributors, lyrics,
      advisories, artwork, credits, and territory. Evidence: locale and
      distributor parity.
  - [ ] E8.6.a Define locale variant pinned to release/work/recording/master/
        lyrics/credits/artwork/rights/target/profile revisions. Evidence: locale
        manifest.
  - [ ] E8.6.b Edit title/subtitle/version/phonetic/sort/contributor
        names/roles/ order/credits and identifier display with language/script
        rules. Evidence: metadata editor tests.
  - [ ] E8.6.c Translate/transliterate/adapt lyrics with segment/timing/
        pronunciation/prosody/meaning/explicit advisory parity and human
        provenance. Evidence: lyric locale tests.
  - [ ] E8.6.d Localize artwork/text/advisory/credits/descriptions with rights/
        territory/target safe areas and accessibility. Evidence: asset tests.
  - [ ] E8.6.e Review terminology/names/claims-like factual metadata/rights/
        advisories/locale completeness and exact target field transformations.
        Evidence: locale review.
  - [ ] E8.6.f Preview/generate/read back DDEX/distributor/catalog locale cells,
        report unsupported/drop/normalization, and block parity gaps. Evidence:
        distributor parity suite.
- [ ] E8.7 Instrument audio engine, save, collaboration, render, gate, review,
      delivery, rights, and takedown traces/metrics. Evidence: bounded schema.
  - [ ] E8.7.a Define versioned allowlisted event/metric/span schemas, units/
        dimensions/sampling/retention/access/owner and prohibited audio/lyrics/
        rights/identity fields. Evidence: telemetry catalog.
  - [ ] E8.7.b Instrument audio engine lifecycle/device/buffer/sample rate/xrun/
        glitch/latency/CPU safely outside realtime critical work. Evidence:
        engine telemetry.
  - [ ] E8.7.c Instrument save/autosave/media upload/durability/offline/recovery
        and collaboration connection/lag/queue/conflict/convergence. Evidence:
        editing telemetry.
  - [ ] E8.7.d Instrument render/bounce/mix/master jobs and gates/reviews/
        approvals with versions/progress/resources/cost/outcome/reasons.
        Evidence: pipeline telemetry.
  - [ ] E8.7.e Instrument rights/consent/splits/expiry/revocation workload and
        delivery/ack/read-back/takedown/correction per-target state using opaque
        IDs. Evidence: release telemetry.
  - [ ] E8.7.f Propagate trace context through web/desktop/native/service/queue/
        worker/provider/distributor without protected payload. Evidence: trace
        tests.
  - [ ] E8.7.g Validate known-value metrics/cardinality/overhead/redaction/
        deletion/two-tenant access and obtain privacy/audio review. Evidence:
        bounded schema gate.
- [ ] E8.8 Define SLOs for save durability, audio glitches, collaboration,
      render, master QC, delivery, and catalog correction. Evidence:
      error-budget policy.
  - [ ] E8.8.a Define acknowledged-save survival/recovery-loss and media upload/
        project revision durability objectives. Evidence: durability SLO.
  - [ ] E8.8.b Define audio xrun/glitch/dropout/input-monitor/transport
        objectives by supported host/device/buffer/project scenario. Evidence:
        audio SLO.
  - [ ] E8.8.c Define collaboration availability/operation latency/lag/
        convergence/reconnect/conflict and local-safety objectives. Evidence:
        realtime SLO.
  - [ ] E8.8.d Define render queue/start/completion/failure/resource and master
        QC evaluator availability/freshness/reproducibility objectives.
        Evidence: production SLO.
  - [ ] E8.8.e Define delivery/ack/content read-back and catalog correction/
        takedown propagation/closure objectives per target. Evidence:
        distribution SLO.
  - [ ] E8.8.f Define windows/denominators/exclusions/slices/data sources/
        uncertainty/alerts and quality guardrails. Evidence: measurement policy.
  - [ ] E8.8.g Define error budgets, stop-the-line, rollout/fallback/disable/
        remediation/owner and prohibit throughput from overriding audio/
        rights/release correctness. Evidence: error-budget policy.
- [ ] E8.9 Build cost attribution for storage, egress, providers, rendering,
      mastering, collaboration, and distribution. Evidence: reconciliation.
  - [ ] E8.9.a Define cost event/source/invoice/usage
        unit/currency/rate/version/ time/tenant/project/job/provider/target and
        allocation policy. Evidence: cost contract.
  - [ ] E8.9.b Meter original/proxy/render/archive storage and upload/download/
        stream/distributor/provider egress with dedupe/shared allocation.
        Evidence: infrastructure costs.
  - [ ] E8.9.c Attribute provider/model/analyzer/mastering/render-worker CPU-
        GPU/time/licence/token/currency cost to immutable jobs/attempts/outputs.
        Evidence: job costs.
  - [ ] E8.9.d Attribute collaboration/realtime/relay/recording and distributor/
        delivery/message/territory/transaction costs. Evidence: service costs.
  - [ ] E8.9.e Show estimated/authorized/accrued/final/late/adjusted/refunded
        costs with units/currency/FX/date and role-filtered project/release
        rollups. Evidence: cost views.
  - [ ] E8.9.f Reconcile usage to provider invoices/accounts/credits with
        tolerance, unknown/unallocated/duplicate/late charges and auditable
        corrections. Evidence: reconciliation report.
- [ ] E8.10 Build backup/restore for projects, media, rights, audit, and release
      records. Evidence: consistent restore drill.
  - [ ] E8.10.a Define protected data/services, cadence/retention/encryption/
        immutability/region/key access/recovery order/RPO/RTO and owners.
        Evidence: backup policy.
  - [ ] E8.10.b Back up database/project operations/snapshots, immutable media/
        proxies/manifests, rights/contracts/signatures, audit, jobs/releases/
        delivery state and configuration references. Evidence: backup tests.
  - [ ] E8.10.c Restore selected point/full/incremental sets into isolated
        environment with tenant/project/key/identity mapping and no production
        side effects. Evidence: restore trace.
  - [ ] E8.10.d Reconcile project graph/media hashes/plugin state/rights/
        signatures/audit/search/read models/jobs/catalog/distributor state.
        Evidence: integrity report.
  - [ ] E8.10.e Test corrupt/missing/expired key/partial backup/version skew/
        legal hold/large media and fallback or explicit unrecoverable state.
        Evidence: failure tests.
  - [ ] E8.10.f Measure RPO/RTO, validate playable/renderable/releasable project
        and no duplicate external delivery, then obtain operations/security
        approval. Evidence: restore drill.
- [ ] E8.11 Publish audio device, plugin, render, rights, delivery, takedown,
      incident, and recovery runbooks. Evidence: game-day.
  - [ ] E8.11.a Publish audio device/permission/rate/buffer/glitch/latency/
        dropout/clock and recording recovery runbooks. Evidence: audio runbooks.
  - [ ] E8.11.b Publish plugin scan/licence/missing/crash/quarantine/version/
        sandbox/safe-mode and project recovery runbooks. Evidence: plugin
        runbooks.
  - [ ] E8.11.c Publish render/job/cost/capacity/output mismatch/determinism/
        worker/provider failure runbooks. Evidence: render runbooks.
  - [ ] E8.11.d Publish rights/consent/split/sample/provenance/voice revocation/
        leak/dispute/waiver runbooks with named legal escalation. Evidence:
        rights runbooks.
  - [ ] E8.11.e Publish distributor delivery/ack/read-back/correction/
        redelivery/takedown/territory and partial failure runbooks. Evidence:
        delivery runbooks.
  - [ ] E8.11.f Publish security/privacy/availability/integrity/stage/
        collaboration incident and backup/restore/replay runbooks. Evidence:
        incident/recovery runbooks.
  - [ ] E8.11.g Run timed cross-role game-days, verify access/contacts, revise
        unsafe/missing steps, and reapprove. Evidence: game-day report.
- [ ] E8.12 Build support-safe diagnostics bundle with protected-data redaction.
      Evidence: privacy test.
  - [ ] E8.12.a Define purpose/requester/authorization/project/host/time range/
        data classes/bundle schema/retention and user preview/consent policy.
        Evidence: diagnostics contract.
  - [ ] E8.12.b Collect app/build/OS/browser/native/audio/MIDI/device/plugin/
        capability/config state with paths/serials/accounts/licenses minimized.
        Evidence: environment diagnostics.
  - [ ] E8.12.c Collect bounded structured logs/traces/crash/audio-engine/job/
        network/storage/quota/realtime/distributor status without audio/lyrics/
        contracts/voiceprints/tokens. Evidence: telemetry bundle.
  - [ ] E8.12.d Include project/media/plugin/rights/release only as opaque IDs,
        counts/hashes/status and allowlisted problem-specific summaries.
        Evidence: redaction tests.
  - [ ] E8.12.e Scan generated bundle for secrets/PII/paths/media/text/contract
        leakage, encrypt/sign/hash, deliver via expiring audited channel, and
        delete on expiry. Evidence: delivery tests.
  - [ ] E8.12.f Test representative roles/tenants/projects/voice/rights/
        unreleased/stage/offline failures and manual privacy review. Evidence:
        privacy report.
- [ ] E8.13 Build album/release/program steering overlays for creative,
      technical, rights, budget, territory, delivery, and cadence constraints.
      Evidence: tighten-only property tests and impact preview.
  - [ ] E8.13.a Define versioned overlay identity/scope/priority/effective
        window/ owner/reviewer and each allowed
        creative/technical/rights/budget/ territory/delivery/cadence property.
        Evidence: overlay schema.
  - [ ] E8.13.b Define code/regulatory/contract/global policy floors and a
        stricter-versus-weaker partial order per property. Evidence: lattice
        specification.
  - [ ] E8.13.c Resolve applicable album/release/program/artist/label/territory/
        target overlays deterministically with conflicts explicit. Evidence:
        resolution tests.
  - [ ] E8.13.d Preview affected projects/tracks/renders/gates/reviews/rights/
        budgets/schedules/deliveries and newly blocked/required actions.
        Evidence: impact preview.
  - [ ] E8.13.e Govern draft/review/approve/activate/supersede/rollback/migrate/
        expire with separation, immutable versions, and no direct floor edits.
        Evidence: governance tests.
  - [ ] E8.13.f Property-test every composition is equal or tighter than floors
        and cannot relax consent/rights/human approval/safety/target
        constraints. Evidence: tighten-only suite.
- [ ] E8.14 Build UI-governed genre, mood, instrument, role, version, rights,
      format, territory, and catalog taxonomies over code-owned floors.
      Evidence: versioned review, alias, deprecation, localization, and
      distributor-mapping journey.
  - [ ] E8.14.a Define stable concept/code/set/version/label/definition/parent/
        relation/alias/status/locale/external mapping and code-floor schema.
        Evidence: taxonomy contracts.
  - [ ] E8.14.b Build authorized draft editor for
        add/edit/reparent/relate/alias/ merge/split/localize with
        cycle/duplicate/code/meaning validation. Evidence: editor tests.
  - [ ] E8.14.c Map rights/format/territory to code-owned contractual/technical
        floors and map catalog/genre/mood/instrument/role/version to distributor
        vocabularies with relation/provenance. Evidence: mapping tests.
  - [ ] E8.14.d Preview impact across projects/assets/search/analytics/rights/
        policies/DDEX/distributors/catalog before activation. Evidence: impact
        analysis.
  - [ ] E8.14.e Govern review/approve/activate/supersede/deprecate with aliases/
        replacements/localization/immutable versions and no floor weakening.
        Evidence: governance tests.
  - [ ] E8.14.f Migrate references/indexes/read models/packages/distributor
        mappings recoverably, preserve historical meaning, and reject orphan/
        unknown values. Evidence: migration journey.
- [ ] E8.15 Build podcast and audiovisual project profiles for episodes,
      rundown/script, guests, multitrack recording, edit, music/SFX, video,
      chapters, transcript/captions, artwork, feed/video renditions, rights,
      review, schedule, and correction. Evidence: project-to-distribution
      package journey.
  - [ ] E8.15.a Define podcast/audiovisual show/season/episode/version/rundown/
        script/segment/guest/source/claim/media/release profile contracts.
        Evidence: profile schemas.
  - [ ] E8.15.b Build rundown/script authoring with host/guest roles, consent,
        source/fact-check links, timing, cues, disclosures, and episode
        continuity. Evidence: preproduction tests.
  - [ ] E8.15.c Capture/import multitrack audio/video with immutable media,
        devices/session/guest consent, proxies/sync/timecode, and provenance.
        Evidence: ingest tests.
  - [ ] E8.15.d Edit dialogue/music/SFX/video/graphics nondestructively with
        rights, loudness, aspect/safe-area, source/timecoded anchors, and
        accessible controls. Evidence: edit tests.
  - [ ] E8.15.e Create transcript/speakers/chapters/captions/translations/audio
        descriptions/artwork/metadata with locale and semantic parity. Evidence:
        accessibility/localization tests.
  - [ ] E8.15.f Run editorial/fact-check/audio-video/rights/accessibility/label/
        target review on exact episode version and invalidate on change.
        Evidence: review tests.
  - [ ] E8.15.g Schedule/generate/validate feed/audio/video/article/social
        renditions/packages and publish/read back through target adapters.
        Evidence: distribution tests.
  - [ ] E8.15.h Propagate correction/retraction/replacement through episode/
        transcript/captions/clips/feed/video/embed/archive with timecoded
        notices. Evidence: correction E2E.
  - [ ] E8.15.i Run complete project→record/edit/review→package→distribution
        journey including partial target failure. Evidence: profile journey.
- [ ] E8.16 Build artist/label marketing operations for campaign brief, assets,
      approved copy, audience/territory, schedule, links, rights, budget,
      channel delivery, analytics, and takedown without granting marketing
      actors mix/master or rights authority. Evidence: separation-of-duties
      tests.
  - [ ] E8.16.a Define separate marketing campaign/brief/asset/copy/audience/
        schedule/link/channel/budget/delivery/version/state and role boundary.
        Evidence: campaign schema.
  - [ ] E8.16.b Create brief pinned to approved
        release/artist/label/territories/
        dates/objectives/disclosures/constraints with no access to protected
        session/contracts. Evidence: brief tests.
  - [ ] E8.16.c Select rights-cleared approved artwork/audio/video/snippets/
        lyrics/credits and author target copy as proposals with exact release
        identity. Evidence: content tests.
  - [ ] E8.16.d Resolve consented audience/territory/locale/channel/quiet/
        frequency and build stable campaign links/UTM or equivalent with
        canonical correction state. Evidence: targeting/link tests.
  - [ ] E8.16.e Budget/estimate/approve/schedule/preview per-channel jobs and
        execute through target adapters with acknowledgment/retry/partial state.
        Evidence: delivery tests.
  - [ ] E8.16.f Instrument privacy-safe delivery/engagement/conversion/cost and
        prohibit metrics from changing creative/rights/release approval.
        Evidence: analytics tests.
  - [ ] E8.16.g Propagate release delay/correction/takedown/territory/right
        change to queued/live campaigns, links/assets, and channels with
        read-back. Evidence: remedy tests.
  - [ ] E8.16.h Attempt marketing-role mix/master/media source/rights/split/
        consent/release/takedown mutations and prove rejection. Evidence:
        separation-of-duties suite.
- [ ] E8.17 Build provider, model, sample-pack, plugin/preset, instrument, and
      workflow administration for compatibility, license, security scan,
      evaluation, entitlement, install/update/deprecate/quarantine, dependency
      impact, and cost. Evidence: untrusted code/content cannot enter an active
      project silently.
  - [ ] E8.17.a Define governed catalog item/version/kind/vendor/source/hash/
        compatibility/licence/security/evaluation/entitlement/cost/status/
        dependency contract. Evidence: admin schema.
  - [ ] E8.17.b Onboard provider/model with API/data-use/region/retention/
        training/operation capabilities, credentials, quotas/cost, and
        evaluation approval. Evidence: provider tests.
  - [ ] E8.17.c Onboard sample packs/presets/instruments with immutable content,
        source/rights/licence/territory/use, metadata, scan, and preview.
        Evidence: content tests.
  - [ ] E8.17.d Onboard plugins/code/workflows with
        signature/build/architecture/
        format/dependencies/permissions/sandbox/security scan and malicious
        tests. Evidence: code tests.
  - [ ] E8.17.e Run versioned
        functional/audio/quality/performance/compatibility/ safety/equivalence
        evaluations per supported host/profile. Evidence: evaluation matrix.
  - [ ] E8.17.f Govern draft/review/approve/entitle/install/activate/update/
        deprecate/quarantine/revoke/rollback with separation and immutable
        audit. Evidence: lifecycle tests.
  - [ ] E8.17.g Preview project/preset/render/provider/workflow dependency
        impact and migration/substitution/loss before update/quarantine/removal.
        Evidence: impact tests.
  - [ ] E8.17.h Enforce approved version/entitlement/licence at project open/
        scan/load/job/render and block silent untrusted code/content activation.
        Evidence: enforcement tests.
  - [ ] E8.17.i Reconcile usage/licence/seats/provider/plugin/storage/egress
        costs and expose current supported/blocked alternatives. Evidence: cost
        report.
- [ ] E8.18 Expose conservatory/ear-training, discovery, accompaniment,
      virtuoso/performance, guitar, sacred, and philosophy capabilities only
      through a ratified production use case or a governed deep link to their
      separate product surfaces. Evidence: each library has a concrete
      disposition; none is implied covered by the DAW.
  - [ ] E8.18.a Inventory each named library/package/route/component/API/data/
        audience/current consumer and claimed production capability. Evidence:
        source ledger.
  - [ ] E8.18.b Split into atomic capabilities and assess genuine professional
        production use, consumer/learning/discovery purpose, ownership,
        security, and dependency fit. Evidence: disposition analysis.
  - [ ] E8.18.c Ratify retained in-DAW use cases with exact workflow/context/
        role/inputs/outputs/rights/quality/accessibility and owned mutation.
        Evidence: production use cases.
  - [ ] E8.18.d Assign separate product host/owner/audience for nonproduction
        capabilities and define compatible identity/entitlement/support.
        Evidence: host decisions.
  - [ ] E8.18.e Implement governed deep link/handoff with opaque context,
        reauthorization/redaction/expiry/return path and no shared unsafe state.
        Evidence: handoff tests.
  - [ ] E8.18.f Mark externalized/deprecated/removed/unsupported explicitly in
        navigation/docs/capability profile instead of implying DAW coverage.
        Evidence: product UX tests.
  - [ ] E8.18.g Reconcile every library/capability to one reviewed disposition
        and fail orphan/duplicate/unreachable/unauthorized rows. Evidence:
        completeness gate.
- [ ] E8.19 Build a DJ/performance profile where ratified for decks, library/
      crates, analysis/beat grids, cue/loop, sync, stems, sampler, mixer,
      effects, transitions, recording, broadcast/stream, set history, hardware,
      offline media, rights/reporting, and emergency recovery. Evidence:
      supported controller/format/stream matrix and set-recording provenance;
      otherwise disposition the DJ workstation as a separate product.
  - [ ] E8.19.a Decide integrated profile versus separate DJ product from
        audience/host/audio/device/offline/library/rights/live-safety/ownership
        analysis and ratify boundary. Evidence: product ADR.
  - [ ] E8.19.b If retained, define deck/library/crate/track/analysis/grid/cue/
        loop/set/history/controller/broadcast/recording state and capability
        contracts. Evidence: DJ schemas.
  - [ ] E8.19.c Ingest/search/prepare offline-capable media with waveform/tempo/
        key/grid/gain/stems/cues/loops, rights/territory/source, and manual
        corrections. Evidence: library tests.
  - [ ] E8.19.d Implement multi-deck playback/sync/pitch/key lock/cue/loop/stem/
        sampler/mixer/effects/transitions with sample-accurate clock, latency,
        and safe gain. Evidence: performance tests.
  - [ ] E8.19.e Integrate ratified controllers/MIDI/HID/audio interfaces with
        mappings/feedback/reconnect/redundancy and accessible semantic/ keyboard
        alternatives. Evidence: controller tests.
  - [ ] E8.19.f Implement record/broadcast/stream target setup, levels/metadata/
        rights/reporting/ack/failover and immutable set/track/action provenance.
        Evidence: capture/stream tests.
  - [ ] E8.19.g Implement performance lock, local offline media,
        device/computer/ audio/clock/network failover, emergency stop, and
        post-set recovery. Evidence: stage drills.
  - [ ] E8.19.h Validate supported controller×format×host×stream cells, audio/
        timing/performance budgets, rights reports, and set-recording lineage.
        Evidence: profile matrix.
  - [ ] E8.19.i If externalized, record separate host/handoff/disposition and
        remove implied DJ support from DAW profiles/routes/tests. Evidence:
        exclusion gate.

### E9 — Euterpe verification and launch gates

- [ ] E9.1 Run old/current project migration and revision integrity suites.
      Evidence: corpus report.
  - [ ] E9.1.a Assemble supported historical/current browser/desktop project/
        session/media/plugin/preset/rights/release records with provenance and
        expected semantics. Evidence: corpus manifest.
  - [ ] E9.1.b Run stepwise and supported direct migrations with interruption/
        retry/rollback/export/downgrade refusal. Evidence: migration matrix.
  - [ ] E9.1.c Verify timing/tempo/meter/score/MIDI/clips/comp/routing/devices/
        automation/media hashes and state recall. Evidence: DAW integrity.
  - [ ] E9.1.d Verify rights/consent/splits/provenance/review/approval/release/
        catalog identity and protected boundaries. Evidence: governance
        integrity.
  - [ ] E9.1.e Test missing plugins/media/licences/unknown fields/corrupt/large/
        offline/version-skew cases with explicit loss. Evidence: edge suite.
  - [ ] E9.1.f Compare semantic diffs/audio render goldens/revision hashes and
        publish supported compatibility results. Evidence: corpus report.
- [ ] E9.2 Run deterministic DAW command, timing, routing, automation, comp,
      render, and undo/redo suites. Evidence: command coverage index.
  - [ ] E9.2.a Generate complete structured command inventory and map each
        command to preconditions/effect/inverse/persistence/accessibility/tests.
        Evidence: coverage index.
  - [ ] E9.2.b Run sample-accurate tempo/meter/transport/loop/punch/clip/MIDI/
        score/session-launch timing goldens. Evidence: timing results.
  - [ ] E9.2.c Run track/bus/send/sidechain/device/plugin/modular/control
        routing and latency/tail/bypass/failure signal-flow goldens. Evidence:
        routing results.
  - [ ] E9.2.d Run automation/modulation/curve/mode/target/state/realtime-
        offline deterministic renders. Evidence: control results.
  - [ ] E9.2.e Run take/comp/warp/ARA/edit/fade/bounce/freeze/stem/null/source-
        preservation goldens. Evidence: edit/render results.
  - [ ] E9.2.f Run command→undo→redo→save/reopen semantic/audio equality,
        collaboration/offline boundaries, and failed transaction recovery.
        Evidence: undo results.
  - [ ] E9.2.g Fail any retained command without current deterministic or
        explicitly nondeterministic coverage and provenance. Evidence: command
        gate.
- [ ] E9.3 Run known-value score, theory, lyric, recording, mix, loudness, peak,
      format, rights, split, and delivery tests. Evidence: gate registry
      coverage.
  - [ ] E9.3.a Freeze expert-reviewed positive/boundary/negative/exception/
        unsupported/failure fixtures with expected raw measurements/verdicts.
        Evidence: known-value corpus.
  - [ ] E9.3.b Verify score/tuning/theory/rhythm/groove/lyric prosody/rhyme/
        pronunciation/time alignment across ratified cultural profiles.
        Evidence: musical gate results.
  - [ ] E9.3.c Verify recording signal/format/noise/dropout/phase and mix peak/
        headroom/mono/spectrum/dynamics/masking measurements. Evidence: audio
        gate results.
  - [ ] E9.3.d Verify BS.1770 loudness/LRA/true peak, target profiles, master/
        spatial format, null/stem-sum, and failure states. Evidence: QC results.
  - [ ] E9.3.e Verify rights kinds/licence/consent/similarity/splits/exact
        totals/ waivers/provenance/disclosure/release eligibility. Evidence:
        rights results.
  - [ ] E9.3.f Verify DDEX/distributor target eligibility/format/metadata/
        territory/ack/read-back/correction/takedown gates. Evidence: delivery
        results.
  - [ ] E9.3.g Map every retained gate to fixtures/measurement and verdict
        assertions/remediation/owner/current execution; fail gaps. Evidence:
        gate registry coverage.
- [ ] E9.4 Run calibrated transcription, similarity, AI assist, and generated
      voice evaluations. Evidence: threshold, subgroup, uncertainty report.
  - [ ] E9.4.a Pin held-out corpora/splits/labels across task, instrument/voice/
        genre/style/tuning/locale/subgroup/risk/adversarial conditions.
        Evidence: evaluation manifest.
  - [ ] E9.4.b Pin model/provider/prompt/config/code/index versions and run with
        input/output/right/consent/cost/latency provenance. Evidence: run
        records.
  - [ ] E9.4.c Evaluate audio-to-MIDI/score/chord/lyric transcription accuracy,
        alignment, calibration, missed important events, and abstention.
        Evidence: transcription report.
  - [ ] E9.4.d Evaluate melodic/lyrical/recording/sample similarity precision/
        recall/calibration/transforms/common/licensed cases and corpus leakage.
        Evidence: similarity report.
  - [ ] E9.4.e Evaluate assist meaning/source
        preservation/diversity/playability/ artifacts/leakage/rights and
        per-change acceptance behavior. Evidence: assist report.
  - [ ] E9.4.f Evaluate voice intelligibility/pronunciation/timing/pitch/
        identity drift/artifacts/consent/disclosure/misuse by mode. Evidence:
        voice report.
  - [ ] E9.4.g Report confidence intervals/subgroup deltas/severity errors/
        thresholds/regressions and obtain named music/voice/rights/safety
        approval. Evidence: signed evaluation.
- [ ] E9.5 Run official/calibrated BS.1770/R128 meter test signals. Evidence:
      conformance report.
  - [ ] E9.5.a Pin standard/algorithm/profile/implementation/calibration/
        tolerance/channel-layout versions and authoritative test signal source.
        Evidence: test manifest.
  - [ ] E9.5.b Verify signal bytes/sample rate/bit depth/channels/duration/hash
        and independent expected integrated/short/momentary/LRA/true-peak
        values. Evidence: fixture validation.
  - [ ] E9.5.c Run mono/stereo/multichannel K-weighting/gating/integrated tests
        and compare within published tolerances. Evidence: integrated results.
  - [ ] E9.5.d Run momentary/short-term time series/window/hop and
        LRA/sufficient duration tests. Evidence: temporal results.
  - [ ] E9.5.e Run
        true-peak/oversampling/silence/short/low-level/layout/invalid/
        analyzer-failure fixtures. Evidence: edge results.
  - [ ] E9.5.f Compare supported host/worker implementations and publish
        current- branch conformance artifacts with all versions. Evidence:
        report.
- [ ] E9.6 Run DDEX ERN golden/conformance validation. Evidence: pinned profile
      reports.
  - [ ] E9.6.a Pin ERN/profile/schema/code-list/namespace/validator/partner-
        extension versions and deterministic generator build. Evidence:
        conformance manifest.
  - [ ] E9.6.b Validate new single/album/EP/multidisc/multiterritory release
        resources/releases/deals/parties/identifiers/technical details.
        Evidence: new-release goldens.
  - [ ] E9.6.c Validate metadata update/redelivery/audio replacement/territory
        add-remove/takedown lifecycle and prior relationships. Evidence:
        lifecycle goldens.
  - [ ] E9.6.d Test missing/invalid/duplicate IDs, share/rights/territory/date/
        code/cardinality/resource/checksum/unsupported extension cases.
        Evidence: negative corpus.
  - [ ] E9.6.e Round-trip through sandbox/contract distributor and distinguish
        transport/message/resource/content/catalog acceptance. Evidence:
        interoperability tests.
  - [ ] E9.6.f Publish current-branch validation/loss/read-back results per
        supported distributor profile and fail stale/unproven cells. Evidence:
        profile reports.
- [ ] E9.7 Run real-database, blob, search, queue, collaboration, render, and
      distributor integration tests. Evidence: durable round-trip.
  - [ ] E9.7.a Provision production-equivalent supported service versions with
        two tenants/projects, large media, plugins, rights, collaborators,
        workers, and sandbox targets. Evidence: environment manifest.
  - [ ] E9.7.b Exercise project
        edit/save→blob/proxy/analyze→search→collaboration
        →render/QC→bundle→distributor→catalog/read-back with trace linkage.
        Evidence: integration trace.
  - [ ] E9.7.c Verify rights/consent/splits/provenance/property redaction and no
        cross-project data in indexes/queues/logs/artifacts/targets. Evidence:
        isolation tests.
  - [ ] E9.7.d Inject DB/blob/search/queue/realtime/worker/plugin/provider/
        distributor timeout/outage/duplicate/order/auth/version faults.
        Evidence: fault tests.
  - [ ] E9.7.e Reconcile authoritative records/media hashes/indexes/operations/
        jobs/bundles/acknowledgments/catalog after create/update/takedown/
        restore. Evidence: reconciliation report.
  - [ ] E9.7.f Restart/replay/backup/restore and prove acknowledged saves/media/
        decisions/deliveries survive without duplicate external effects.
        Evidence: durable round-trip.
- [ ] E9.8 Run full-journey Playwright: concept to arrangement and saved
      revision. Evidence: live audio engine where browser automation permits.
  - [ ] E9.8.a Create a real project and capture text/hum-or-fixture audio/MIDI/
        chord/reference concepts with provenance/rights prompts. Evidence:
        concept trace.
  - [ ] E9.8.b Generate or author multiple arrangement/score/MIDI/lyric
        candidates, compare synchronously, and record human decision. Evidence:
        creative trace.
  - [ ] E9.8.c Edit sections/tracks/clips/MIDI/score/lyrics/devices/automation
        through pointer and keyboard paths, including undo/redo. Evidence:
        editing trace.
  - [ ] E9.8.d Autosave to live store, close/reopen, inspect history/semantic
        diff, restore as new revision, and verify media/plugin state. Evidence:
        persistence trace.
  - [ ] E9.8.e Exercise offline/degraded/provider-not-configured/role/stale/
        conflict state and no simulated success. Evidence: resilience tests.
  - [ ] E9.8.f Use live browser audio engine/virtual devices where automation
        supports it; otherwise pin deterministic audio fixtures and record exact
        limitation. Evidence: Playwright report.
- [ ] E9.9 Run full-journey Playwright: recording/import to mix/master QC.
      Evidence: deterministic fixture media.
  - [ ] E9.9.a Configure virtual/fixture device and calibrate, record or import
        scanned deterministic multichannel media with take/session provenance.
        Evidence: capture trace.
  - [ ] E9.9.b Perform nondestructive waveform/clip/take/comp/fade/stretch/
        repair/alignment edits and prove source restore. Evidence: edit trace.
  - [ ] E9.9.c Build track/bus/send/sidechain/device/plugin/automation mix,
        render it, and verify deterministic signal-flow/null outputs. Evidence:
        mix trace.
  - [ ] E9.9.d Create master candidate with target/reference/profile, process/
        render, and run loudness/peak/format/spatial/QC measurements. Evidence:
        master trace.
  - [ ] E9.9.e Review accessible values/units/thresholds/history/remediation,
        fix a failed gate, rerun, and obtain exact-revision human approval.
        Evidence: QC trace.
  - [ ] E9.9.f Test missing/crashing plugin, device loss, analyzer failure,
        cancel/retry, stale source mix, and no nonpass displayed as pass.
        Evidence: Playwright report.
- [ ] E9.10 Run full-journey Playwright: rights/splits to delivery and catalog
      verification. Evidence: sandbox distributor or contract adapter.
  - [ ] E9.10.a Register work/master/sample/performer/voice/model/artwork rights
        and consents with protected projections. Evidence: rights trace.
  - [ ] E9.10.b Negotiate/sign exact ownership splits, resolve sample clearance,
        and verify publishing versus master totals independently. Evidence:
        split/clearance trace.
  - [ ] E9.10.c Build release sequence/metadata/locales/artwork/territories/
        target profiles and lock exact rights/master/provenance candidate.
        Evidence: release trace.
  - [ ] E9.10.d Assemble deterministic bundle, validate DDEX/format/QC/
        disclosure/rights, approve, and deliver to sandbox/contract distributor.
        Evidence: delivery trace.
  - [ ] E9.10.e Correlate acknowledgments and read back catalog audio/metadata/
        identifiers/territories/status; reject transport-only success. Evidence:
        catalog proof.
  - [ ] E9.10.f Test partial target failure/retry, role/field authorization,
        protected data nonleakage, and accessible status. Evidence: Playwright
        report.
- [ ] E9.11 Run full-journey Playwright: rights revocation to takedown.
      Evidence: affected catalog targets reconcile.
  - [ ] E9.11.a Seed a delivered release using rights/consent/sample/model
        across project/clips/mix/master/bundle/distributor/catalog. Evidence:
        baseline.
  - [ ] E9.11.b Revoke/expire/dispute an exact right or consent and preview the
        complete read-only blast radius with protected redaction. Evidence:
        impact trace.
  - [ ] E9.11.c Approve quarantine/replacement/re-clearance/takedown plan and
        invalidate active reviews/release/jobs/links. Evidence: remedy plan.
  - [ ] E9.11.d Execute per-target takedown/restriction/notice saga with
        duplicate/out-of-order/failure injection and truthful progress.
        Evidence: saga trace.
  - [ ] E9.11.e Read back distributor/catalog/search/share states, keep one
        failed target unresolved, repair/retry, and close only after
        reconciliation. Evidence: target proof.
  - [ ] E9.11.f Verify immutable prior artifacts/audit/provenance, no protected
        leakage, and optional replacement/redelivery lineage. Evidence:
        Playwright report.
- [ ] E9.12 Run WCAG 2.2 AA, keyboard, announcement, non-drag, and semantic DAW
      parity suites. Evidence: themes/viewports/locales.
  - [ ] E9.12.a Define critical arrange/session/piano roll/score/mixer/device/
        modulation/plugin/review/rights/release route/action matrix across
        hosts/viewports/themes/locales. Evidence: coverage plan.
  - [ ] E9.12.b Run automated WCAG/name-role-value/landmark/form/error/contrast/
        focus/target/reflow/motion assertions around code-native DAW surfaces.
        Evidence: automation report.
  - [ ] E9.12.c Run pointer-free create/edit/record/route/automate/comp/mix/
        review/render/release with remapped commands and no traps. Evidence:
        keyboard report.
  - [ ] E9.12.d Run non-drag position/range/reorder/connect/curve/scene/device
        operations with identical validation/undo/audit. Evidence: dragging
        report.
  - [ ] E9.12.e Compare visual and semantic timeline/mixer/device/modulation/
        automation/graph/quality/compare views for content and operations.
        Evidence: parity report.
  - [ ] E9.12.f Assert transport/record/save/conflict/device/job/gate/review/
        delivery/takedown announcements without audio/content leakage. Evidence:
        announcement report.
  - [ ] E9.12.g Run supported screen-reader/zoom/high-contrast/RTL/long locale/
        touch/desktop manual review and close blockers. Evidence: signed matrix.
- [ ] E9.13 Run audio glitch, input latency, large project, memory, waveform,
      mixer, render, autosave, collaboration, and upload performance tests.
      Evidence: budgets pass.
  - [ ] E9.13.a Build production-scale fixtures/matrices for hosts/devices/
        buffers/rates/tracks/clips/media/plugins/automation/score/collaborators/
        rights/jobs. Evidence: load manifest.
  - [ ] E9.13.b Measure input-to-monitor/control-to-audible/transport timing,
        audio-thread CPU/xruns/glitches/clicks under increasing load. Evidence:
        realtime profile.
  - [ ] E9.13.c Measure project open/save/autosave/recovery, waveform/spectrum/
        scroll/zoom, mixer/device UI input/paint and sustained memory. Evidence:
        editor profile.
  - [ ] E9.13.d Measure realtime/offline/batch render speed/resources/
        determinism, upload/proxy/resume/egress and worker concurrency/cost.
        Evidence: pipeline profile.
  - [ ] E9.13.e Measure collaboration operation latency/lag/convergence/
        reconnect/conflict with multiple clients and impaired networks.
        Evidence: realtime profile.
  - [ ] E9.13.f Capture audio/UI
        threads/CPU/GPU/memory/disk/network/query/queue/ plugin traces, compare
        to budgets, trend, and fail regressions. Evidence: performance report.
- [ ] E9.14 Run malicious media/project/plugin/preset, prompt injection, voice
      impersonation, rights fraud, leak, auth, tenant, and distribution
      adversarial tests. Evidence: zero blockers.
  - [ ] E9.14.a Test malformed/polyglot/bomb/active
        audio-project-preset/archive/ metadata/media and unsafe
        parsers/paths/resources. Evidence: content security report.
  - [ ] E9.14.b Test malicious/hanging/crashing plugin/module/UI/state/update/
        licence/filesystem/network/sandbox escape and safe-mode recovery.
        Evidence: plugin security report.
  - [ ] E9.14.c Inject prompts/instructions through audio metadata/lyrics/
        presets/projects/references/provider outputs and agent handoffs.
        Evidence: injection report.
  - [ ] E9.14.d Attempt voice/performer impersonation, cloned
        consent/signatures, voiceprint extraction, revoked output/model reuse,
        and fraud. Evidence: identity report.
  - [ ] E9.14.e Attempt fabricated/licence/rights/splits/sample/provenance/
        similarity/waiver/disclosure manipulation and takedown suppression.
        Evidence: rights report.
  - [ ] E9.14.f Probe unreleased media/contracts/identity/voice through UI/API/
        search/log/cache/notifications/review links/exports/support/provider/
        distributor. Evidence: leakage report.
  - [ ] E9.14.g Test auth/session/role/BOLA/property/function/tenant/project and
        mass/replayed distribution/ack/callback/credential attacks. Evidence:
        authorization/distribution report.
  - [ ] E9.14.h Require zero blockers, assign regression/owner/mitigation and
        expiring approved lower-risk exceptions. Evidence: adversarial gate.
- [ ] E9.15 Run crash recovery, offline sync, backup/restore, and distribution
      replay drills. Evidence: RPO/RTO.
  - [ ] E9.15.a Crash tab/app/audio worklet/native host/plugin/worker during
        recording/edit/save/upload/render and recover acknowledged project/
        local originals/operations. Evidence: crash drills.
  - [ ] E9.15.b Run offline create/edit/record/render/export, two-client
        changes, reconnect/rebase/conflict/role revoke/quota/eviction/storage
        loss. Evidence: offline drills.
  - [ ] E9.15.c Restore project/media/plugin state/rights/contracts/audit/
        release/jobs from selected backups into isolated environment. Evidence:
        restore trace.
  - [ ] E9.15.d Reconcile hashes/revisions/routing/timing/rights/signatures/
        indexes/catalog/distributor state and prove playable/renderable project.
        Evidence: integrity report.
  - [ ] E9.15.e Replay delivery/takedown/correction saga from checkpoints under
        duplicate/out-of-order/partial prior actions and read back targets.
        Evidence: replay tests.
  - [ ] E9.15.f Measure achieved RPO/RTO, record unrecoverable external limits,
        revise runbooks, and obtain operations/security approval. Evidence:
        signed drill report.
- [ ] E9.16 Produce engineering-readiness evidence. Evidence: requirements map
      to current proof.
  - [ ] E9.16.a Define required architecture/contracts/migrations/audio/
        compatibility/security/privacy/rights/accessibility/quality/standards/
        operations evidence by host/profile. Evidence: readiness matrix.
  - [ ] E9.16.b Map every retained atomic capability/checklist parent/child to
        implementation/tests/goldens/evaluations/reports/runbooks/approvals.
        Evidence: evidence index.
  - [ ] E9.16.c Verify evidence provenance matches candidate commit/build/
        schemas/audio engine/plugins/models/providers/profiles/distributors/
        devices. Evidence: provenance check.
  - [ ] E9.16.d Reconcile route/package/capability/command/gate/device/plugin/
        format/target/consumer inventories with no orphan/unknown rows.
        Evidence: completeness report.
  - [ ] E9.16.e Classify missing/stale/failed/waived evidence, block required
        cells, and record exception scope/owner/compensation/expiry. Evidence:
        readiness gate.
  - [ ] E9.16.f Publish per-host/profile go/no-go, supported/conditional/
        unsupported cells, residual risks, support/rollback. Evidence: readiness
        packet.
- [ ] E9.17 Obtain named music-production, mastering, rights/legal,
      accessibility, privacy/security, engineering, and product approvals.
      Evidence: human signoff.
  - [ ] E9.17.a Define each approver’s scope/criteria/required evidence/
        qualification/independence/delegate/expiry and nondelegable decisions.
        Evidence: approval policy.
  - [ ] E9.17.b Route immutable creative/production/audio-engine/interchange/
        specialized workflow evidence to named music-production reviewers.
        Evidence: production decision.
  - [ ] E9.17.c Route loudness/master/spatial/target conformance and delivery
        evidence to qualified mastering/release reviewers. Evidence: mastering
        decision.
  - [ ] E9.17.d Route rights/consent/splits/sample/similarity/voice/model/
        provenance/distributor evidence to rights/legal specialists. Evidence:
        legal decision.
  - [ ] E9.17.e Route WCAG/blind-producer and privacy/security/threat/
        adversarial/source-redaction evidence to named specialists. Evidence:
        assurance decisions.
  - [ ] E9.17.f Route architecture/quality/operations/recovery/support and
        product scope/host/rollout/residual risks. Evidence: engineering/product
        decisions.
  - [ ] E9.17.g Record signed approve/reject/request-change/conditional
        decisions and invalidate on
        candidate/evidence/profile/model/plugin/target change. Evidence: human
        signoff.
- [ ] E9.18 Obtain launch approval for each supported host and distribution
      profile. Evidence: human governance record.
  - [ ] E9.18.a Build exact host×capability×device/plugin/offline/provider and
        distribution×territory/format/layout/profile candidate matrix. Evidence:
        launch scope.
  - [ ] E9.18.b Assemble per-cell current compatibility/performance/audio/
        security/accessibility/rights/QC/DDEX/read-back/recovery proof.
        Evidence: launch packets.
  - [ ] E9.18.c Verify build/signing/config/feature flags/migrations/models/
        plugins/profiles/credentials/runbooks/monitoring/on-call/rollback.
        Evidence: operational checklist.
  - [ ] E9.18.d Run final live-backed smoke per supported host and
        representative distributor profile, including correction/takedown
        read-back. Evidence: launch smokes.
  - [ ] E9.18.e Record named go/no-go/conditional decision per cell with scope/
        limits/known loss/rollout/conditions/owner/expiry/rollback trigger.
        Evidence: governance record.
  - [ ] E9.18.f Fail launch on unproven/stale/unknown supported cell and
        invalidate approval on candidate/profile/target change. Evidence: launch
        gate.
- [ ] E9.19 Run distinct full journeys for instrument/effect authoring,
      hardware/MIDI/MPE, culturally diverse tuning/notation, stem separation,
      inpainting/style transfer, voice conversion/singing/choir, AI composition,
      local/cloud AI negotiation, modular patching, immersive/spatial mix,
      remote recording, stage/DJ mode, podcast/video, and marketing handoff.
      Evidence: no generic DAW smoke test substitutes for specialized proof.
  - [ ] E9.19.a Run instrument/sampler/synth and effect/signal-chain authoring
        through state/preset/automation/modulation/freeze/render/missing-device
        recovery. Evidence: device journey.
  - [ ] E9.19.b Run hardware audio/MIDI/MIDI 2/MPE/pedal/control/external effect
        discovery→map/calibrate/record/recall→disconnect/reconnect. Evidence:
        hardware journey.
  - [ ] E9.19.c Run culturally diverse/non-12-TET tuning/notation/world-rhythm/
        theory/playback/interchange with expert assumptions and loss proof.
        Evidence: inclusive music journey.
  - [ ] E9.19.d Run stem separation through source rights/job/stem review/manual
        correction/recombination/stem-sum/lineage. Evidence: separation journey.
  - [ ] E9.19.e Run inpainting/continuation/style transfer through exact mask/
        condition/reference rights/candidates/audition/comp and untouched-region
        preservation. Evidence: generative edit journey.
  - [ ] E9.19.f Run voice conversion, speech/singing/choir/harmony through
        consent/identity/locale/job/evaluation/disclosure/revocation. Evidence:
        voice journey.
  - [ ] E9.19.g Run
        melody/harmony/rhythm/arrangement/orchestration/text-to-music plan
        through editable score/MIDI/stem/audio intermediates and part-level
        decisions. Evidence: composition journey.
  - [ ] E9.19.h Run local/on-device and cloud execution negotiation through
        hardware/privacy/rights/cost/quality choice, offline behavior, fallback,
        and result-equivalence proof. Evidence: AI location journey.
  - [ ] E9.19.i Run modular patch create/connect/modulate/automate/performance/
        save/render with feedback/CPU/crash safety and semantic list parity.
        Evidence: modular journey.
  - [ ] E9.19.j Run object/bed/ambisonic spatial mix through layout/automation/
        renderer/head tracking/downmix/QC/interchange/target delivery. Evidence:
        immersive journey.
  - [ ] E9.19.k Run remote rehearsal/record through device consent/clock/cues/
        monitor/local originals/upload/alignment/reconnect under network
        impairment. Evidence: remote session journey.
  - [ ] E9.19.l Run stage mode and, if retained, DJ mode through preflight/
        offline package/control allowlist/performance lock/record/failover/
        emergency stop/post-ingest. Evidence: live performance journeys.
  - [ ] E9.19.m Run podcast/audiovisual project through rundown/guest consent/
        multitrack edit/captions/rights/review/feed-video package/correction.
        Evidence: podcast/video journey.
  - [ ] E9.19.n Run marketing handoff through approved release/assets/copy/
        audience/budget/channels/analytics/takedown while denying production/
        rights mutations. Evidence: marketing journey.
  - [ ] E9.19.o Verify each journey uses owned live services/host profile,
        deterministic or calibrated media, roles/rights, failure recovery,
        trace/audit, and current proof. Evidence: specialized coverage ledger.
- [ ] E9.20 Run pairwise conformance across supported browser/native host,
      audio/MIDI device, plugin/instrument, sample rate/bit depth, channel/
      spatial layout, renderer, provider/model, project profile, interchange
      format, and delivery target. Evidence: unsupported cells are explicit and
      supported cells have current evidence.
  - [ ] E9.20.a Build canonical dimension/value/constraint model from ratified
        hosts/OS/architectures/devices/plugins/formats/rates/layouts/renderers/
        models/profiles/targets. Evidence: compatibility model.
  - [ ] E9.20.b Generate deterministic pairwise cells plus required higher-order
        audio-risk combinations and label invalid/unsupported cells with
        rationale. Evidence: coverage set.
  - [ ] E9.20.c Define per-cell semantic/audio/timing/state/rights/provenance/
        accessibility/performance/loss tolerances and required fixture/hardware.
        Evidence: conformance contract.
  - [ ] E9.20.d Execute supported cells on exact versioned hosts/devices/
        virtual hardware/plugins/models/renderers/formats/targets and retain
        manifests/goldens. Evidence: conformance results.
  - [ ] E9.20.e Exercise version skew/capability absence/degradation/fallback/
        warnings/refusal/recovery without silent quality/state/rights loss.
        Evidence: boundary tests.
  - [ ] E9.20.f Publish current supported/conditional/unsupported matrix with
        evidence age/known loss/prerequisites/owner/expiry and fail stale or
        unproven supported cells. Evidence: release gate.
- [ ] E9.21 Run full journeys for clip-launch performance/capture, source→target
      modulation, native/browser plugin scan/load/crash/recovery, ARA/warp/comp,
      deep interchange round-trips, external sync/control surfaces,
      offline-first storage/sync, customized accessible workspaces, and batch
      operations. Evidence: each structural DAW capability has current
      browser/native proof and fault coverage.
  - [ ] E9.21.a Run clip/scene launch/quantize/legato/follow/probability/
        performance lock/record/capture-to-arrangement with sample-accurate and
        pointer-free proof. Evidence: session journey.
  - [ ] E9.21.b Run every modulation source family through typed target routing/
        depth/polarity/range/smoothing/cycle safety/visual-semantic control and
        deterministic render. Evidence: modulation journey.
  - [ ] E9.21.c Run native VST3/CLAP/AU or ratified and browser WAM scan/
        signature/licence/load/state/UI/process/crash/quarantine/recover per
        supported host. Evidence: plugin journey.
  - [ ] E9.21.d Run ARA/deep edit and warp/transient/tempo/formant/comp context
        through save/undo/reopen/render/missing-editor and source-preservation.
        Evidence: deep-edit journey.
  - [ ] E9.21.e Run DAWproject/MIDI2/MusicXML/BWF-iXML/AAF-OMF-FCPXML/ADM-BWF or
        ratified import/export cross-host with complete loss profiles. Evidence:
        interchange journeys.
  - [ ] E9.21.f Run Link/MTC/LTC/MIDI clock/MMC/OSC/control surface/headless API
        authority/drift/feedback/reconnect/unsafe command. Evidence:
        sync/control journey.
  - [ ] E9.21.g Run offline-first create/record/edit/undo/render/export/reopen/
        quota/eviction/reconnect/merge/storage-loss across PWA and desktop.
        Evidence: offline journey.
  - [ ] E9.21.h Run saved multi-window/touch/high-DPI/control-surface/blind-
        producer workspace across arrange/session/score/mixer/modulation/plugin/
        review, including reconnect. Evidence: workspace journey.
  - [ ] E9.21.i Run mixed-eligibility batch render/export/convert/analyze
        through preflight/cost/capacity/queue/partial/retry/cancel/output
        verification/ deterministic manifest. Evidence: batch journey.
  - [ ] E9.21.j Reconcile each capability to current browser/native proof,
        device/profile/format cells, fault injection, accessibility, and owned
        artifacts; fail generic substitution. Evidence: structural DAW gate.
- [ ] E9.22 EVE-SEAM-EXIT: require "required Eve reads and writes admitted with
      source and behavior evidence, or an exact task-0.8 explicit non-goal
      proves zero operations; a named defer preserves ownership but never passes
      completion" before this phase exits.
      Evidence: source-derived totality record and passing verifier.
  - [ ] E9.22.a Re-run the route, bridge, state-signal, and complete domain-code
        envelope census; review every changed candidate instead of carrying a
        stale phase disposition forward. Evidence: current ratchets.
  - [ ] E9.22.b Bind the exact ratified task-0.8 workflow and requirement set;
        classify it as a required seam or an exact source-backed non-goal.
        Evidence: charter-bound disposition set.
  - [ ] E9.22.c For a required seam, admit both read and write operations with
        byte-pinned source and behavior evidence. A named defer retains its owner
        and unblock condition but fails completion. Evidence: operation proofs.
  - [ ] E9.22.d Permit a zero-operation boundary only for an exact ratified
        task-0.8 explicit non-goal; absence or unavailability is not a non-goal.
        Evidence: source-bound non-goal proof.
  - [ ] E9.22.e Run the totality verifier, every retained gate-case CLI, and the
        injected durable-candidate control; retain red and green receipts.
        Evidence: release gate logs.

---

## Phase A — Aja motion-capture operations console

Aja is a processing and embodied-instruction subsystem. Its console must manage
capture intake, consent, pipeline execution, motion QA, retargeting, annotation,
asset curation, and export while preserving the canonical Aja adapter used by
Metis and other V1 consumers.

### A0 — Host, boundary, pipeline, and risk decisions

- [ ] A0.1 Decide whether the canonical console lives in a new Aja control room
      or an Oshun Studio operator route. Evidence: host/ownership ADR.
  - [ ] A0.1.a Inventory current Studio routes/Aja services/operator audiences,
        identity/project context, deployment, security, and preview ownership.
        Evidence: host inventory.
  - [ ] A0.1.b Compare dedicated control room versus Studio route for queue/
        worker/live capture/biometric isolation/3D performance/support needs.
        Evidence: option analysis.
  - [ ] A0.1.c Decide canonical host, owning team, routes/deep links/shell/data/
        mutation boundaries and prohibited duplicate console behavior. Evidence:
        target architecture.
  - [ ] A0.1.d Define migration/redirect/session/handoff/rollout/rollback/
        monitoring/support consequences. Evidence: delivery plan.
  - [ ] A0.1.e Obtain product/mocap/architecture/privacy/security/accessibility
        ratification. Evidence: approved ADR.
- [ ] A0.2 Inventory all static Studio routes, Aja services, CLI commands,
      libraries, contracts, and existing preview/annotation components.
      Evidence: route-capability matrix.
      <!-- partial: route (a) + service (b) + code/CLI (c) inventories done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/aja-inventory.json. d-e (test/flag/deployment coverage,
                                                                                                                                                                                                                                                                      route-capability reconciliation) are design/human and stay unchecked. -->
  - [x] A0.2.a Enumerate routes/navigation/commands/pages/deep links and current
        operator/consumer journeys. Evidence: route inventory.
        <!-- evidence: .studioRoutes — 98 /studio/aja/* Studio pages (98 distinct
                                                                                                                                                                                                                                                                        segments, flat structure), path/segment/dynamic/test. Aja has no dedicated
                                                                                                                                                                                                                                                                        web app; the console is a Studio-route surface. generate-aja-inventory.mjs. -->
  - [x] A0.2.b Enumerate services/endpoints/workers/queues/models/stores/blobs/
        caches/search/providers and accountable owners. Evidence: service
        inventory.
        <!-- evidence: .services — the 3 motion services classified: svc-motion-ai
                                                                                                                                                                                                                                                                        (http-service, 9 /api/v1/* + /health routes), svc-motion-pipeline
                                                                                                                                                                                                                                                                        (http-service, 25 routes), svc-reference-video (library/no-http). owners are
                                                                                                                                                                                                                                                                        A0.2.b's accountable-owner assignment (semantic). -->
  - [x] A0.2.c Enumerate CLI commands/libraries/contracts/adapters/formats/
        preview/annotation/render components and consumers. Evidence: code
        inventory.
        <!-- evidence: .cli — 7 CLI commands (compare, formats, metrics, ping,
                                                                                                                                                                                                                                                                        skeletons, stats, version) across the command modules. -->
  - [ ] A0.2.d Map tests/docs/flags/deployments to atomic capabilities and
        classify live/fixture/local/service/incomplete. Evidence: coverage
        ledger.
        <!-- runtime/design: live/fixture classification needs the services run. -->
  - [ ] A0.2.e Reconcile route→capability→owner/host/data class/profile and flag
        duplicate/orphan/dead/unsafe paths. Evidence: route-capability matrix.
- [ ] A0.3 Map all `libs/aja/*` packages to pipeline stages, console
      capabilities, consumer adapters, or out-of-scope. Evidence: complete
      ledger.
  - [x] A0.3.a Inventory every package/export/consumer/runtime/test/documented
        purpose and dependency direction. Evidence: package inventory.
        <!-- evidence: .domains.aja.packages — 41 libs/aja/* packages with exports/consumer/dependency/test/disposition/direction. evidence/v1-workbenches/domain-package-inventories.json (254 pkgs; per-package exports/consumers/deps/disposition/tests + capability catalog with exact source locations). -->
  - [ ] A0.3.b Map retained exports to typed pipeline stages, console functions,
        canonical contracts/adapters, hosts, and owners. Evidence: capability
        mapping.
  - [ ] A0.3.c Classify duplicate/legacy/demo/consumer-specific/dead code with
        keep/merge/migrate/deprecate/remove/out-of-scope disposition. Evidence:
        disposition ledger.
  - [ ] A0.3.d Verify each package/export has one reviewed disposition and no
        private or circular boundary violation. Evidence: completeness gate.
- [ ] A0.4 Verify the canonical `@oshun/embodiment-aja` contract boundary.
      Evidence: console work does not introduce private consumer imports.
  - [x] A0.4.a Inventory package public exports/schema versions/consumers and
        all direct/transitive private import paths. Evidence: boundary
        inventory.
        <!-- evidence: aja-embodiment-boundary.json — @oshun/embodiment-aja declares
                                                                                                                                                                                                                                                                        3 public export subpaths (. / adapter / canonical-adapter, 76 public
                                                                                                                                                                                                                                                                        symbols); 3 consumer files, each importing only declared public subpaths.
                                                                                                                                                                                                                                                                        Private/deep-import violations = 0 (boundaryClean=true) — the A0.4 invariant
                                                                                                                                                                                                                                                                        ("console work does not introduce private consumer imports") holds today.
                                                                                                                                                                                                                                                                        generate-aja-embodiment-boundary.mjs + 6 tests. -->
  - [ ] A0.4.b Define canonical capability/demonstration/overlay/handoff/health/
        error contracts and ownership/version policy. Evidence: public contract.
  - [ ] A0.4.c Refactor or disposition private consumer imports using public
        adapters without leaking console/internal stage/storage models.
        Evidence: dependency changes.
  - [ ] A0.4.d Add architecture/import/lint/build/contract tests that fail
        private subpath, reverse dependency, duplicate contract, or drift.
        Evidence: boundary gate.
  - [ ] A0.4.e Run Metis and other consumer conformance with healthy/degraded/
        unsupported states. Evidence: consumer report.
- [ ] A0.5 Define operator, capture technician, animator, rigger, QA reviewer,
      consent officer, privacy reviewer, annotator, producer, publisher, and
      administrator roles. Evidence: permission matrix.
  - [ ] A0.5.a Define least-privilege duties/object/field/function rights for
        every named role across capture→package→recall. Evidence: role
        definitions.
  - [ ] A0.5.b Define tenant/project/session/subject/consent
        compartment/profile/ worker/export scope, delegation, guest, and
        temporary grants. Evidence: scope policy.
  - [ ] A0.5.c Define ingest/live-device/configure/run/pause/kill/annotate/
        cleanup/map/review/approve/export/recall/admin rights by state.
        Evidence: permission matrix.
  - [ ] A0.5.d Define separation/qualification/quorum/recusal for consent/
        privacy/QA/vertical specialist/publish and nondelegable human actions.
        Evidence: SoD policy.
  - [ ] A0.5.e Verify UI, API, queue, blob, preview, export, and direct command
        enforcement for positive/negative/revoked/cross-project cases. Evidence:
        authorization suite.
- [ ] A0.6 Ratify the canonical pipeline DAG from ingest through package.
      Evidence: stages, optional branches, retry boundaries, and artifacts are
      data.
  - [x] A0.6.a Inventory current stage implementations/inputs/outputs/models/
        configs/resources/caches/failures and profile variations. Evidence:
        stage ledger.
        <!-- aja-stage-ledger.json: derived from the TYPED motion-pipeline-sdk
                                                                                                                                                                                                                                                                        contracts (libs/aja/motion-pipeline-sdk/src/types.ts), not imposed
                                                                                                                                                                                                                                                                        taxonomy. 11 canonical PipelineStages (ingestion → validation →
                                                                                                                                                                                                                                                                        preprocessing → pose-estimation → skeleton-fitting → domain-analysis →
                                                                                                                                                                                                                                                                        quality-assessment → retargeting → format-conversion → postprocessing →
                                                                                                                                                                                                                                                                        delivery), each mapped to named-reference modules (validation 4 /
                                                                                                                                                                                                                                                                        pose-estimation 1 / retargeting 6 have dedicated libs; the other 8 run
                                                                                                                                                                                                                                                                        inline via the SDK client — reported honestly as orchestrated, NOT
                                                                                                                                                                                                                                                                        unimplemented). inputs = 7 InputConstraints fields, outputs = 6 OutputSpec
                                                                                                                                                                                                                                                                        fields, configs = StageConfig (params/timeout/deps/parallel), failures =
                                                                                                                                                                                                                                                                        StageRetryConfig (retry/backoff) + failed/skipped statuses + failFast,
                                                                                                                                                                                                                                                                        resources = 5 JobPriorities + per-stage/global timeouts + parallel flag,
                                                                                                                                                                                                                                                                        caches = 6 pipeline-cache modules (incl. pose-estimation-cache,
                                                                                                                                                                                                                                                                        retargeting-cache), profile variations = 5 domains × 4 quality presets =
                                                                                                                                                                                                                                                                        20 combinations + 8 QualityFeatures toggles. models are honestly noted as
                                                                                                                                                                                                                                                                        stage-specific StageConfig.params, not a top-level typed field.
                                                                                                                                                                                                                                                                        generate-aja-stage-ledger.mjs + 7 tests, wired into inventory/verify + CI.
                                                                                                                                                                                                                                                                        The canonical DAG (A0.6.b), branch/skip data (A0.6.c), and later A0.6.*
                                                                                                                                                                                                                                                                        DEFINE new structure and stay unchecked. -->
  - [ ] A0.6.b Define required ingest/preprocess/estimate/reconstruct/cleanup/
        retarget/validate/package stages and typed artifact edges. Evidence:
        canonical DAG.
  - [ ] A0.6.c Define modality/profile optional branches, conditions/fan-out-in,
        required floors, skip rules, and unsupported behavior as data. Evidence:
        branch matrix.
  - [ ] A0.6.d Define transaction/retry/checkpoint/cache/cancel/rerun
        boundaries, non-destructive lineage, and downstream invalidation.
        Evidence: execution contract.
  - [ ] A0.6.e Version/review/publish/deprecate templates and validate cycles/
        ports/resources/capabilities/consent/export floors. Evidence: ratified
        DAG.
- [ ] A0.7 Define supported input, skeleton, rig, animation, motion, video,
      calibration, and export formats. Evidence: compatibility matrix.
  - [x] A0.7.a Inventory format/profile/version/container/codec/schema/vendor/
        validator/library/licence and actual import/export consumers. Evidence:
        format registry.
        <!-- evidence: aja-format-registry.json — @aja/motion-formats 7 formats
                                                                                                                                                                                                                                                                        (fbx/gltf/usd/bvh/alembic/mocap-import/clip-database) over 10 extensions,
                                                                                                                                                                                                                                                                        each with import-capable + export-capable symbols, validators, version
                                                                                                                                                                                                                                                                        constants (e.g. FBX_VERSION_MAP), binary signatures (FBX_BINARY_MAGIC); 5
                                                                                                                                                                                                                                                                        round-trip formats; 58 downstream consumers across 30 projects.
                                                                                                                                                                                                                                                                        licence/vendor are the semantic half. generate-aja-format-registry.mjs + 5 tests. -->
  - [ ] A0.7.b Define video/depth/image/calibration/camera/timecode inputs with
        resolution/rate/color/orientation/clock/metadata limits. Evidence: input
        matrix.
  - [ ] A0.7.c Define skeleton/rig/mesh/blend-shape/coordinate/unit/bind-pose/
        naming/topology/skin/mapping support. Evidence: rig matrix.
  - [ ] A0.7.d Define motion/animation/curve/event/contact/root/face/hand/
        compression/interchange/export target support. Evidence: motion matrix.
  - [ ] A0.7.e Define conversion/round-trip/loss diagnostics and supported/
        conditional/unsupported format pairs with fixtures/version/owner/expiry.
        Evidence: compatibility matrix.
- [ ] A0.8 Define subject consent, biometric/privacy, data residency, retention,
      moderation, watermark, and revocation policies. Evidence: legal/privacy
      review.
  - [ ] A0.8.a Define biometric/personal/sensitive data classes across raw
        video/
        depth/audio/face/body/hand/gaze/voice/mesh/motion/embeddings/metadata.
        Evidence: data classification.
  - [ ] A0.8.b Define informed consent purpose/intended and excluded use/
        processing/modalities/models/recipients/territories/term/withdrawal/
        evidence. Evidence: consent policy.
  - [ ] A0.8.c Define collection/minimization/redaction/pseudonymization/
        encryption/key/region/provider/device/access/export/analytics controls.
        Evidence: privacy policy.
  - [ ] A0.8.d Define retention/deletion/legal hold/backups/derived asset/model/
        consumer impact and subject request/revocation response. Evidence:
        lifecycle policy.
  - [ ] A0.8.e Define content/safety moderation,
        watermark/provenance/disclosure, prohibited outputs/claims, and human
        review by vertical profile. Evidence: safety policy.
  - [ ] A0.8.f Obtain current legal/privacy/consent/biometric/security review
        and link policies to enforcement/tests/runbooks. Evidence: signed
        review.
- [ ] A0.9 Threat-model non-consensual capture, biometric leakage, malicious
      video/assets, model attacks, unsafe anatomical output, worker compromise,
      cost abuse, export tamper, and adapter spoofing. Evidence: mitigation/test
      map.
  - [ ] A0.9.a Map actors/assets/trust/data/control boundaries across devices/
        mobile/desktop/upload/API/queue/workers/models/storage/preview/export/
        consumers. Evidence: threat diagrams.
  - [ ] A0.9.b Model hidden/mismatched/revoked subject consent, covert capture,
        identity swaps, reidentification, secondary use, and prohibited profile
        claims. Evidence: consent threats.
  - [ ] A0.9.c Model biometric/raw media leakage through search/log/cache/
        preview/annotation/analytics/support/providers/exports/adapters and
        cross-tenant access. Evidence: privacy threats.
  - [ ] A0.9.d Model malicious video/depth/rig/archive/project/parser payloads,
        model poisoning/evasion/prompt attacks, unsafe generated motion, and
        export tamper. Evidence: content/model threats.
  - [ ] A0.9.e Model worker/GPU/lease/cache/checkpoint/result spoofing,
        arbitrary code/model/version, resource/cost abuse, queue starvation, and
        artifact substitution. Evidence: compute threats.
  - [ ] A0.9.f Model adapter/deep-link/webhook/SDK impersonation, replay,
        capability/health spoofing, and consumer misuse. Evidence: integration
        threats.
  - [ ] A0.9.g Assign controls/tests/monitoring/owners/residual risks/review
        cadence to every threat. Evidence: mitigation map.
- [ ] A0.10 Define real-time versus batch latency/quality profiles. Evidence:
      explicit SLO and gate differences.
  - [ ] A0.10.a Define live preview/feedback/record/handoff and batch
        processing/QA/package scenarios, users, devices, inputs, and outputs.
        Evidence: use profiles.
  - [ ] A0.10.b Set end-to-end/frame/preview/feedback/queue/stage/export
        latency, jitter/dropout/resource/cost budgets per profile. Evidence:
        SLOs.
  - [ ] A0.10.c Define allowed live model/resolution/modality/calibration/
        smoothing/quality compromises and clearly label provisional output.
        Evidence: live capability matrix.
  - [ ] A0.10.d Define batch model/resolution/iterations/ground-truth/gate/
        review/reproducibility floors and no live-preview promotion. Evidence:
        batch matrix.
  - [ ] A0.10.e Define profile-specific measurements/thresholds/failure/
        fallback/reprocess/approval/export eligibility. Evidence: gate
        differences.
  - [ ] A0.10.f Test same inputs across profiles, compare timing/quality/cost/
        lineage, and show unsupported/degraded honestly. Evidence: profile
        report.
- [ ] A0.11 Define ground-truth, calibration, and evaluation-corpus governance.
      Evidence: provenance and access controls.
  - [ ] A0.11.a Define corpus/dataset/version/purpose/modalities/subjects/
        motions/rigs/capture setups/labels/splits/owners and intended profiles.
        Evidence: corpus schema.
  - [ ] A0.11.b Verify consent/licence/lawful basis/territory/retention/model-
        evaluation use/withdrawal and protected biometric access. Evidence:
        rights register.
  - [ ] A0.11.c Define ground-truth acquisition/calibration systems/protocols/
        synchronization/coordinate mapping/uncertainty/quality and expert
        qualifications. Evidence: truth protocol.
  - [ ] A0.11.d Define annotation instructions/tools/inter-rater/adjudication/
        versioning/provenance and correction history. Evidence: labeling policy.
  - [ ] A0.11.e Define subject-safe train/dev/test/eval splits, leakage/
        duplicate/contamination/poisoning controls and subgroup minimums.
        Evidence: split policy.
  - [ ] A0.11.f Enforce encryption/compartment/access/export/audit/deletion/
        revocation/provider controls and approve releases. Evidence: governance
        review.
- [ ] A0.12 Define operational success measures for throughput, queue age, stage
      failure, quality, manual correction, cache, cost, consent, and export
      correctness. Evidence: metric spec.
  - [ ] A0.12.a Define item/frame/duration/profile throughput and queue
        wait/age/ starvation/SLA by priority without hiding rejected work.
        Evidence: queue metrics.
  - [ ] A0.12.b Define per-stage start/runtime/retry/failure/cancel/checkpoint/
        recovery/capability mismatch and artifact completeness. Evidence: stage
        metrics.
  - [ ] A0.12.c Define calibrated raw quality/verdict/uncertainty/manual
        correction/adjudication/drift measures by profile and subject-safe
        cohorts. Evidence: quality metrics.
  - [ ] A0.12.d Define cache hit/valid/invalid/corrupt/evict/resource savings
        and compute/storage/egress/model/currency cost attribution. Evidence:
        efficiency metrics.
  - [ ] A0.12.e Define consent verified/mismatch/expiry/revocation/enforcement
        and package/export/adapter semantic read-back correctness. Evidence:
        governance metrics.
  - [ ] A0.12.f Define units/denominators/windows/slices/privacy/cardinality/
        freshness/alerts/owners/budgets and quality guardrails. Evidence: metric
        spec.
- [ ] A0.13 Author and ratify `V1/AJA_MOCAP_CONSOLE_DESIGN_2026-07-23.md` with
      current-state, host, pipeline DAG, contracts, data, APIs, queue, UI,
      consent/privacy, export, migration, rollout, and decision records.
      Evidence: approved doc and resolved review comments.
  - [ ] A0.13.a Document current routes/packages/services/CLI/contracts/
        adapters/stores/workers/formats/pipelines/tests/backing state and gaps.
        Evidence: current-state chapter.
  - [ ] A0.13.b Document host/ownership/roles/boundaries/capability and vertical
        profiles, canonical DAG/stages/artifacts/resources/caches. Evidence:
        architecture chapters.
  - [ ] A0.13.c Document
        capture/motion/rig/annotation/consent/provenance/package contracts and
        stores/APIs/events/queue/workers/adapters. Evidence: platform chapters.
  - [ ] A0.13.d Document consent/privacy/biometric/safety/moderation/threat/
        evaluation/export policies and human authority. Evidence: governance
        chapters.
  - [ ] A0.13.e Document task-led console/preview/semantic alternatives/
        responsive/accessibility/live/offline/performance/error states.
        Evidence: experience chapter.
  - [ ] A0.13.f Document migration/compatibility/rollout/rollback/operations/
        observability/recovery/verification/evidence/launch criteria. Evidence:
        delivery chapters.
  - [ ] A0.13.g Resolve every substantive review comment and obtain named
        mocap/animation/privacy/safety/security/accessibility/engineering
        approval. Evidence: ratified design.
- [ ] A0.14 Reconcile every Aja service/CLI command, static Studio route,
      `libs/aja/*` package, canonical/study adapter, contract, format, pipeline,
      worker, test, and documented feature at atomic-feature level. Evidence:
      explicit dispositions cover video-to-motion, desktop/mobile live capture,
      body/hand/face tracking, 2D/3D/mesh recovery, cleanup, retargeting,
      quality, formats, vertical pipelines, reference video, avatars/rigs,
      batch/queue, SDK/webhooks, privacy/consent, and delivery.
  - [x] A0.14.a Inventory code/routes/CLI/services/packages/contracts/adapters/
        formats/stages/workers/tests/docs/flags/deployments. Evidence:
        reconciliation inputs.
        <!-- evidence: aja-code-census.json — comprehensive launch census: 41 libs
                                                                                                                                                                                                                                                                        packages + 7 app projects, 445 code files + 43 tests, 98 Studio routes, 3
                                                                                                                                                                                                                                                                        classified services, 7 CLI commands, 7 interchange formats, the
                                                                                                                                                                                                                                                                        @oshun/embodiment-aja contract boundary (0 violations), 52 docs, and 7
                                                                                                                                                                                                                                                                        deployment targets (build/dev/lint/serve/start/test/typecheck). Consolidated
                                                                                                                                                                                                                                                                        from fresh git counts + committed A0.2/A0.4/A0.7 evidence. Atomic-feature
                                                                                                                                                                                                                                                                        split + dispositions are A0.14.b+ (semantic). generate-aja-code-census.mjs + 4 tests. -->
  - [ ] A0.14.b Split each source item into atomic user/operator/system
        capabilities with host/audience/owner/profile/state/dependencies/data
        class. Evidence: atomic ledger.
  - [ ] A0.14.c Cover live/mobile/body-hand-face/multi-person/video-to-motion/
        2D-3D-mesh/cleanup/retarget/quality/reference/avatars explicitly.
        Evidence: motion coverage.
  - [ ] A0.14.d Cover formats/verticals/batch-queue/SDK-CLI/webhooks/privacy-
        consent/package-delivery/consumer adapters explicitly. Evidence:
        operations coverage.
  - [ ] A0.14.e Assign keep/consolidate/migrate/replace/deprecate/remove/
        externalize with target owner/host/profile/dependencies/delivery/expiry.
        Evidence: disposition ledger.
  - [ ] A0.14.f Reconcile ledger to routes/packages/contracts/stores/tests and
        fail orphan/duplicate/unknown/unreviewed capabilities. Evidence:
        completeness gate.
- [ ] A0.15 Ratify separate capability and safety profiles for yoga, fitness,
      dance, martial arts, sports, medical/rehabilitation, film/VFX, gaming, and
      other current domain pipelines. Evidence: each profile has authorized
      audiences, intended-use limitations, capture requirements, stages, models,
      quality thresholds, reviewers, outputs, and prohibited claims.
  - [ ] A0.15.a Define profile identity/version/owner/audience/intended and
        excluded use/disclaimer/jurisdiction for each named vertical. Evidence:
        profile registry.
  - [ ] A0.15.b Define required capture devices/views/calibration/reference/
        subject/consent/modalities/motion vocabulary and data classification.
        Evidence: capture matrix.
  - [ ] A0.15.c Define canonical required/optional stages/models/configuration/
        resources and unsupported behavior per vertical. Evidence: DAG profiles.
  - [ ] A0.15.d Define raw measurements/quality floors/uncertainty/human
        reviewer qualifications/quorum/adjudication/export conditions. Evidence:
        quality profiles.
  - [ ] A0.15.e Define output projection/fields/format/watermark/provenance/
        consumer handoff and prohibited medical/coaching/performance claims.
        Evidence: output policy.
  - [ ] A0.15.f Validate independent known-value/privacy/safety fixtures and
        obtain named domain review for every profile; no inherited proof.
        Evidence: ratified matrix.
- [ ] A0.16 Define the boundary among capture-device UI, processing operator
      console, animator/rigger cleanup, clinician/coach/educator consumer
      applications, developer SDK/CLI, and platform administration. Evidence: no
      console preview is misrepresented as medical, coaching, or performance
      advice.
  - [ ] A0.16.a Define audience/trust/data/action/responsibility boundaries for
        each named surface and authoritative owner of every mutation. Evidence:
        surface matrix.
  - [ ] A0.16.b Define capture-device responsibilities for consent/status/
        guidance/record/buffer/upload only, with no unqualified interpretation.
        Evidence: device boundary.
  - [ ] A0.16.c Define console versus animator/rigger cleanup responsibilities,
        exact handoff artifacts/revisions/locks, and quality/approval boundary.
        Evidence: production boundary.
  - [ ] A0.16.d Define consumer projections with intended-use labels, qualified
        review, redacted biometric data, and no console preview as advice.
        Evidence: consumer contracts.
  - [ ] A0.16.e Define SDK/CLI and platform admin credentials/scopes/audit/
        capabilities without content/consent authority expansion. Evidence:
        developer/operator boundary.
  - [ ] A0.16.f Test signed/deep-link/API/adapter handoffs, reauthorization/
        redaction/expiry/return, and cross-surface mutation attempts. Evidence:
        boundary suite.

### A1 — Capture, stage, motion, rig, and consent contracts

- [ ] A1.1 Define capture project, session, take, camera, calibration, subject,
      consent ref, input asset, and capture metadata. Evidence: schema.
  - [ ] A1.1.a Define stable tenant/project/session/take identity, lifecycle/
        revision/times/operator/profile/purpose/location and authorization
        scope. Evidence: capture schemas.
  - [ ] A1.1.b Define camera/sensor/device/lens/stream/channel/frame-rate/
        resolution/orientation/clock/timecode/capability metadata. Evidence:
        device schema.
  - [ ] A1.1.c Define calibration/version/coordinate/units/intrinsic/extrinsic/
        distortion/volume/scale/error/reference relationships. Evidence:
        calibration schema.
  - [ ] A1.1.d Define pseudonymous subject/role/take assignment/multi-person ID
        separately from protected identity/biometrics and exact consent
        reference. Evidence: subject boundary.
  - [ ] A1.1.e Define immutable input assets/checksums/kinds/channels/time
        ranges/ capture/import provenance/privacy class/retention/rights.
        Evidence: asset schema.
  - [ ] A1.1.f Enforce cross-project/reference/state/version/missing/unknown/
        archive invariants with representative fixtures. Evidence: schema tests.
- [ ] A1.2 Define pipeline template as a versioned DAG with stage IDs,
      dependencies, input/output kinds, configuration schema, capability,
      resources, timeout, retry, and cache policy. Evidence: DAG validation.
  - [ ] A1.2.a Define immutable template/version/owner/profile/status/parent/
        effective window and stage/edge order-independent serialization.
        Evidence: template schema.
  - [ ] A1.2.b Define stable stage ID/type/adapter version/typed ports/
        dependencies/conditions/optional-required floor and fan-out/in
        semantics. Evidence: DAG contract.
  - [ ] A1.2.c Define versioned configuration
        schema/defaults/validation/secrets/ migrations and output-affecting
        field declaration. Evidence: config contract.
  - [ ] A1.2.d Define host/model/GPU/CPU-memory/storage/network/licence/consent/
        modality capability requirements and reservations. Evidence: capability
        schema.
  - [ ] A1.2.e Define timeout/heartbeat/retry/backoff/checkpoint/cancel/skip/
        failure/cache key/read-write-expiry/quarantine policy. Evidence:
        execution policy.
  - [ ] A1.2.f Validate acyclic graph/ports/kinds/reachability/required stages/
        resource feasibility/export floors. Evidence: DAG validation suite.
- [ ] A1.3 Define job, stage run, attempt, lease, checkpoint, measurement, log
      ref, resource use, cost, and output refs. Evidence: durable-job schema.
  - [ ] A1.3.a Define immutable job request/template/profile/input/config/
        consent/policy/priority/budget/owner/status/version/idempotency.
        Evidence: job schema.
  - [ ] A1.3.b Define per-stage run/dependency/status/current attempt/cache/
        worker/capability/input-output and downstream invalidation. Evidence:
        stage schema.
  - [ ] A1.3.c Define attempt/lease/worker/heartbeat/start-end/exit/failure/
        retry/cancel/preemption and fencing token. Evidence: lease schema.
  - [ ] A1.3.d Define checkpoint/version/progress/cursor/shard/state/artifact/
        checksum/resume compatibility and expiry. Evidence: checkpoint schema.
  - [ ] A1.3.e Define measurement/log ref/resource usage/cost units/provider/
        time/confidence/protected redaction and raw-versus-verdict relationship.
        Evidence: telemetry schema.
  - [ ] A1.3.f Define output artifact/ref/kind/version/hash/lineage/privacy/
        quality/consent/release state and durable-before-terminal invariants.
        Evidence: job fixtures.
- [ ] A1.4 Define skeleton, joint, hierarchy, coordinate system, units, bind
      pose, rig, mapping, and retarget profile. Evidence: contract fixtures.
  - [ ] A1.4.a Define skeleton/version/joint stable IDs/names/aliases/parent/
        order/root/end effectors/semantics and modality. Evidence: skeleton
        schema.
  - [ ] A1.4.b Define coordinate handedness/up-forward axes/origin/rotation
        order/quaternion convention/linear-angular units/scale and transforms.
        Evidence: coordinate contract.
  - [ ] A1.4.c Define bind/rest/reference pose/local-global transforms/bone
        lengths/orientations/joint limits and confidence/provenance. Evidence:
        pose fixtures.
  - [ ] A1.4.d Define rig controls/deform skeleton/mesh/skin/blend shapes/
        constraints/spaces/IK chains/version/host/format. Evidence: rig schema.
  - [ ] A1.4.e Define source-target mapping/strategy/offset/scale/axis/retarget
        chains/root/contact/twist/finger/face settings and version. Evidence:
        mapping schema.
  - [ ] A1.4.f Validate cycles/duplicates/missing parents/axes/units/bind/
        correspondence and round-trip fixtures. Evidence: contract suite.
- [ ] A1.5 Define motion clip, frame range, sample rate, root motion, contacts,
      events, curves, blend shapes, and source lineage. Evidence: round-trip.
  - [ ] A1.5.a Define motion clip/version/skeleton/rig/timebase/start/end/frame
        count/sample rate/variable timestamps/interpolation and coordinate
        context. Evidence: clip schema.
  - [ ] A1.5.b Define per-frame joint/body/hand/face transforms/confidence/
        occlusion/tracking state and sparse/dense storage. Evidence: motion
        samples.
  - [ ] A1.5.c Define root/global trajectory/in-place extraction/ground plane/
        scale and local versus world motion. Evidence: root schema.
  - [ ] A1.5.d Define contacts/events/markers/segments/labels and scalar/vector/
        quaternion/blend-shape/custom curves with exact anchors. Evidence:
        event/curve schema.
  - [ ] A1.5.e Define immutable source takes/model/stages/cleanup/generated
        ranges/mappings/retarget/render/export lineage. Evidence: provenance
        chain.
  - [ ] A1.5.f Round-trip supported formats/sample rates/coordinates/contacts/
        curves/face/hand data with loss diagnostics. Evidence: motion goldens.
- [ ] A1.6 Define annotation layer, segment, frame/time anchor, label, reviewer,
      status, and provenance. Evidence: schema.
  - [ ] A1.6.a Define annotation set/layer/version/purpose/schema/taxonomy/
        subject motion revision/visibility/owner and immutable parentage.
        Evidence: annotation contract.
  - [ ] A1.6.b Define frame/time/musical/segment/joint/body/hand/face/object/
        region anchors with context and mapping/orphan semantics. Evidence:
        anchor schema.
  - [ ] A1.6.c Define label/value/type/units/confidence/uncertainty/source/
        intended profile and free-text restrictions. Evidence: label schema.
  - [ ] A1.6.d Define draft/submitted/reviewed/accepted/rejected/adjudicated/
        superseded state, reviewer qualification, disagreement, and decisions.
        Evidence: state machine.
  - [ ] A1.6.e Record creator/reviewer/tool/model/version/evidence/time/edit/
        import/export provenance and protected subject projection. Evidence:
        provenance tests.
  - [ ] A1.6.f Validate overlaps/required labels/taxonomy version/anchor
        survival/ cross-project/access and representative fixtures. Evidence:
        schema tests.
- [ ] A1.7 Define technical measurement separately from validation verdict.
      Evidence: policy can recompute without reprocessing.
  - [ ] A1.7.a Define immutable measurement with subject/input/artifact/frame-
        joint-range,
        values/units/evidence/confidence/evaluator/model/calibration
        versions/time. Evidence: measurement schema.
  - [ ] A1.7.b Define separate verdict with measurement references/profile/
        rules/thresholds/outcome/reason/remediation/exception/time. Evidence:
        verdict schema.
  - [ ] A1.7.c Prevent verdict mutation/denormalization of raw values and
        prevent missing/error/unsupported data from defaulting to pass.
        Evidence: invariants.
  - [ ] A1.7.d Recompute new verdicts from retained valid measurements after
        policy/profile threshold changes without rerunning stages. Evidence:
        recomputation tests.
  - [ ] A1.7.e Rerun measurement only on subject/artifact/evaluator/calibration
        invalidation and retain full historical lineage. Evidence: lifecycle
        tests.
- [ ] A1.8 Define consent with subject, collector, purpose, allowed uses,
      territories, recipients, biometric processing, retention, expiry,
      revocation, and evidence. Evidence: invariants.
  - [ ] A1.8.a Define protected subject/representative/minor capacity and
        collector identity/authority/relationship/collection context. Evidence:
        identity schema.
  - [ ] A1.8.b Define purpose/intended-excluded use/profile/project/session/
        modalities/body-hand-face-gaze-mesh/model processing and claims.
        Evidence: purpose schema.
  - [ ] A1.8.c Define allowed outputs/uses/recipients/providers/consumers/
        territories/regions/transfers/exports/publication/model training.
        Evidence: scope schema.
  - [ ] A1.8.d Define retention/deletion/withdrawal/revocation/effective-expiry/
        legal hold/derived artifact/model impact and contact path. Evidence:
        lifecycle schema.
  - [ ] A1.8.e Define informed notice/version/locale/accessibility, affirmative
        signature/intent/time/document hash/witness/guardian and evidence.
        Evidence: consent proof.
  - [ ] A1.8.f Enforce subject/session/purpose/modality/recipient/territory/time
        match, no scope inheritance, and conflict/unknown invariants. Evidence:
        consent tests.
- [ ] A1.9 Define privacy transformations and redacted derivatives. Evidence:
      derivative lineage points to protected source without exposing it.
  - [ ] A1.9.a Define transform kind/version/config/purpose/authorized input
        class/output class/irreversibility/risk and approval. Evidence: privacy
        transform registry.
  - [ ] A1.9.b Define face/body/background/object/license-plate/screen/audio/
        metadata redaction, crop/blur/mask/avatar/skeleton-only derivatives and
        quality tradeoffs. Evidence: transform fixtures.
  - [ ] A1.9.c Generate immutable derivative with input/output hashes, exact
        ranges/regions/tool/model/version/operator and protected opaque link.
        Evidence: lineage tests.
  - [ ] A1.9.d Define authorized role/profile/consumer/search/preview/export/
        retention for derivative and prohibit reverse lookup or source leakage.
        Evidence: projection tests.
  - [ ] A1.9.e Evaluate residual reidentification/utility/quality/temporal
        consistency and require human privacy review at thresholds. Evidence:
        privacy evaluation.
  - [ ] A1.9.f Propagate source revocation/deletion/hold/correction through
        derivatives/providers/caches/consumers with audit. Evidence: lifecycle
        tests.
- [ ] A1.10 Define watermark and provenance records for every derived motion
      asset. Evidence: transformation chain fixture.
  - [ ] A1.10.a Define provenance ingredient/action/tool/model/stage/config/
        subject consent/rights/output identity/hash/version/time DAG. Evidence:
        provenance schema.
  - [ ] A1.10.b Define watermark kind/payload/key/algorithm/strength/placement/
        target/detection/confidence/removal policy without biometric secrets.
        Evidence: watermark schema.
  - [ ] A1.10.c Record
        ingest/estimate/reconstruct/mesh/cleanup/generate/retarget/
        annotate/validate/convert/package transformations and edited ranges.
        Evidence: action lineage.
  - [ ] A1.10.d Bind watermark/provenance to exact motion/skeleton/rig/mesh/
        package formats and preserve/reapply or diagnose loss on conversion.
        Evidence: format tests.
  - [ ] A1.10.e Validate hash/signature/watermark/tamper/stripped/unknown/
        untrusted state and permissioned semantic inspector. Evidence: integrity
        tests.
  - [ ] A1.10.f Run complete protected source→redacted input→motion→cleanup→
        retarget→package transformation chain fixture. Evidence: provenance
        golden.
- [ ] A1.11 Define export package, target format, target rig, mappings,
      measurements, gates, consent lock, watermark, and manifest. Evidence:
      schema.
  - [ ] A1.11.a Define immutable
        package/candidate/version/owner/project/profile/
        target/territory/consumer/status and exact source motion revision.
        Evidence: package schema.
  - [ ] A1.11.b Define target format/profile/version/rig/skeleton/coordinate/
        unit/rate/mapping/retarget/config and compatibility/loss. Evidence:
        target contract.
  - [ ] A1.11.c Pin output motion/mesh/curves/events/annotations/previews/source
        assets plus hashes and transformation provenance. Evidence: artifact
        manifest.
  - [ ] A1.11.d Pin raw measurements/verdicts/profile/evaluator/calibration/
        reviewer approvals and unresolved/waived findings. Evidence: quality
        manifest.
  - [ ] A1.11.e Pin consent/subject/use/modality/recipient/territory/expiry/
        privacy derivative/watermark/provenance lock and recheck at dispatch.
        Evidence: consent lock.
  - [ ] A1.11.f Define canonical serialized manifest/hash/signature/read-back/
        supersession/recall and invalidation on dependency change. Evidence:
        package tests.
- [ ] A1.12 Define immutable revisions and diffs for config, mapping,
      annotation, cleanup, validation, and package. Evidence: golden diffs.
  - [ ] A1.12.a Define common immutable revision/parent(s)/subject/schema/tool/
        actor/cause/time/hash/approval lineage and branch/merge semantics.
        Evidence: revision contract.
  - [ ] A1.12.b Define config/template/stage parameter diff with defaults/
        secrets redaction/output-affecting/material classification. Evidence:
        config goldens.
  - [ ] A1.12.c Define skeleton/rig mapping diff by stable source-target joints/
        offsets/strategies/chains and compatibility impact. Evidence: mapping
        goldens.
  - [ ] A1.12.d Define annotation diff by layer/anchor/label/status/reviewer and
        cleanup motion diff by exact frames/joints/curves/generated ranges.
        Evidence: edit goldens.
  - [ ] A1.12.e Define validation diff by raw measurements/verdict/profile/
        waiver and package diff by artifacts/rig/format/consent/watermark/
        manifest. Evidence: release goldens.
  - [ ] A1.12.f Test add/delete/move/split/merge/restore/concurrent/schema/
        opaque loss and protected projections. Evidence: semantic diff suite.
- [ ] A1.13 Define consumer adapter health/capability projections from actual
      service state. Evidence: healthy/degraded/unhealthy fixtures.
  - [ ] A1.13.a Define versioned adapter capability IDs/operations/modalities/
        profiles/formats/limits/prerequisites and unsupported reasons. Evidence:
        capability schema.
  - [ ] A1.13.b Define actual dependency health from API/queue/workers/models/
        GPU/storage/cache/consent/gates/export versions and freshness. Evidence:
        health inputs.
  - [ ] A1.13.c Compute per-capability available/degraded/unavailable/
        unsupported/not-configured/unknown, reason, safe alternatives, and
        expected impact. Evidence: projection logic.
  - [ ] A1.13.d Redact project/subject/biometric/worker/security/cost details
        and prevent global healthy status from masking failed capability.
        Evidence: privacy tests.
  - [ ] A1.13.e Test healthy/partial/degraded/stale/outage/version-skew/
        permission/consent/config fixtures in Metis and other consumers.
        Evidence: health contract suite.
- [ ] A1.14 Define contract compatibility and format-version negotiation.
      Evidence: old fixture migration and unsupported refusal.
  - [ ] A1.14.a Inventory deployed capture/motion/rig/annotation/consent/job/
        package/adapter/format versions and historical edge fixtures. Evidence:
        version corpus.
  - [ ] A1.14.b Define support window and reader/writer/migrate/export/rollback
        behavior per contract and format. Evidence: compatibility matrix.
  - [ ] A1.14.c Define handshake/Accept or equivalent
        version/capability/profile/ format negotiation with deterministic
        compatible selection. Evidence: negotiation tests.
  - [ ] A1.14.d Implement migrations/adapters for renamed/split/merged/
        coordinate/unit/skeleton/consent fields with explicit loss. Evidence:
        migration tests.
  - [ ] A1.14.e Refuse unsupported/unsafe/ambiguous combinations with required
        upgrade/convert/export alternatives; never default consent/quality.
        Evidence: refusal tests.
  - [ ] A1.14.f Round-trip old/current fixtures across console/service/worker/
        SDK/adapter/format and document irreversible steps. Evidence:
        compatibility report.
- [ ] A1.15 Define body, joint, hand/finger, face/landmark, gaze, expression,
      blend-shape, confidence, occlusion, contact, multi-person identity, and
      tracking-loss contracts separately. Evidence: modality-specific privacy,
      consent, quality, and absence/unsupported semantics.
  - [ ] A1.15.a Define body/root/joint hierarchy/pose/global-local transforms/
        visibility/confidence/occlusion/tracking state and source modality.
        Evidence: body schema.
  - [ ] A1.15.b Define hand/finger side/joints/curls/spread/contact/gesture/
        confidence/occlusion and per-hand supported/absent state. Evidence: hand
        schema.
  - [ ] A1.15.c Define face/landmark/topology/expression/blend shape/
        coefficients/identity-independent versus identity-bearing fields.
        Evidence: face schema.
  - [ ] A1.15.d Define gaze origin/direction/target/confidence/calibration and
        avoid inferring attention/emotion/health beyond intended use. Evidence:
        gaze schema.
  - [ ] A1.15.e Define contacts with body parts/surface/point/normal/start-end/
        confidence and multi-person stable track identity/associations.
        Evidence: contact/identity schemas.
  - [ ] A1.15.f Define tracking loss/partial/occluded/not-observed/
        unsupported/not-requested/consent-blocked distinctly from zero/neutral.
        Evidence: absence semantics.
  - [ ] A1.15.g Map
        consent/privacy/retention/redaction/quality/evaluator/profile
        independently per modality and test round-trips. Evidence: modality
        matrix.
- [ ] A1.16 Define live-capture device, stream, sensor, clock, frame, network,
      calibration, preview, recording, buffer, dropout, reconnect, edge/cloud
      processing, and session-handoff contracts. Evidence: desktop/mobile and
      multi-device compatibility fixtures.
  - [ ] A1.16.a Define device/app/OS/model/sensor/camera/depth/IMU/audio/
        capability/permission/thermal/battery/storage identity and state.
        Evidence: device schema.
  - [ ] A1.16.b Define stream/modality/codec/resolution/rate/orientation/
        timestamps/clock/timecode/synchronization/calibration/frame metadata.
        Evidence: stream schema.
  - [ ] A1.16.c Define live frame/payload/order/drop/duplicate/late/confidence/
        tracking state and preview redaction/latency/quality warning. Evidence:
        frame contract.
  - [ ] A1.16.d Define immutable local recording/chunks/checksums/buffer/quota/
        encryption/upload cursor/dropout markers/recovery. Evidence: recording
        schema.
  - [ ] A1.16.e Define edge versus cloud stage selection, model/version/
        hardware/privacy/rights/latency/fallback and no silent data transfer.
        Evidence: processing profile.
  - [ ] A1.16.f Define disconnect/reconnect/resume/deduplication/clock reset/
        calibration invalidation and signed session/operator handoff. Evidence:
        handoff contract.
  - [ ] A1.16.g Validate desktop/mobile/multi-device/device-loss/network/
        interruption/version/clock fixtures. Evidence: compatibility suite.
- [ ] A1.17 Define vertical-pipeline profile, intended use, excluded use, motion
      vocabulary, reference protocol, required annotations, evaluator, quality
      floor, human qualification, output projection, and disclaimer. Evidence:
      profile schema prevents a film/gaming result from masquerading as a
      clinical or coaching result.
  - [ ] A1.17.a Define stable profile/version/owner/jurisdiction/audience/
        intended use/excluded use/prohibited claims/disclaimer and review
        cadence. Evidence: profile schema.
  - [ ] A1.17.b Define motion vocabulary/taxonomy/actions/phases/body regions/
        expected variability/cultural or domain sources and aliases. Evidence:
        vocabulary contract.
  - [ ] A1.17.c Define capture/reference protocol/devices/views/calibration/
        subjects/consent/modalities/environment/instructions. Evidence: protocol
        schema.
  - [ ] A1.17.d Define required DAG stages/models/configs/resources/annotations/
        raw measurements/evaluator versions/quality floors/uncertainty.
        Evidence: processing contract.
  - [ ] A1.17.e Define human reviewer qualifications/independence/adjudication/
        approval/waiver and consumer output fields/redaction/format/watermark.
        Evidence: governance contract.
  - [ ] A1.17.f Bind every output/package/adapter projection to profile ID/
        intended-use warning/disclaimer and reject cross-profile substitution.
        Evidence: boundary tests.
  - [ ] A1.17.g Validate independent known-value/privacy/consent/safety/ handoff
        fixtures for every ratified vertical. Evidence: profile suite.

### A2 — Intake, consent, calibration, and preprocessing

- [ ] A2.1 Build project/session/take capture and bulk ingest. Evidence:
      keyboard and resumable-upload journey.
  - [ ] A2.1.a Create/select authorized project/profile/purpose and capture
        session with devices/subjects/consent/calibration/time/location/owner.
        Evidence: setup tests.
  - [ ] A2.1.b Create takes with slate/action/performers/cameras/range/markers/
        notes and immutable local/remote input placeholders. Evidence: take
        tests.
  - [ ] A2.1.c Build multi-file/folder/archive manifest preview with detected
        kind/size/hash/associations/duplicates/consent/expected mappings.
        Evidence: bulk preview.
  - [ ] A2.1.d Upload resumably with progress/pause/resume/retry/cancel/quota/
        checksum and partial per-item state. Evidence: upload tests.
  - [ ] A2.1.e Finalize only after quarantine/validation/metadata assignment and
        durable blob/reference, preserving rejected/orphan cleanup. Evidence:
        ingest tests.
  - [ ] A2.1.f Run pointer-free/screen-reader single and mixed bulk ingest,
        interruption/reload/duplicate/permission/recovery. Evidence: E2E.
- [ ] A2.2 Quarantine uploaded video, depth, calibration, skeleton, rig, and
      project archives. Evidence: malicious corpus.
  - [ ] A2.2.a Store every untrusted item in isolated non-executable quarantine
        before preview/parse/index/model/worker use. Evidence: boundary tests.
  - [ ] A2.2.b Verify signature/type/codec/schema/size/count/nesting and scan
        malware/polyglot/active content/unsafe metadata. Evidence: file corpus.
  - [ ] A2.2.c Enforce archive path/symlink/device-file/nesting/member/ratio/
        decompressed-size limits and scan each member. Evidence: archive corpus.
  - [ ] A2.2.d Parse video/depth/calibration/skeleton/rig/project using
        sandboxed bounded parsers without loading scripts/plugins/external
        resources. Evidence: parser tests.
  - [ ] A2.2.e Record scanner/parser/signature/policy versions and release/
        reject/manual-review decision, rescan on update. Evidence: audit.
  - [ ] A2.2.f Test crash/timeout/oversize/encrypted/corrupt/unknown/false-
        positive override and prove no unscanned escape. Evidence: malicious
        suite.
- [ ] A2.3 Validate media container, codec, resolution, frame rate, timestamps,
      duration, orientation, color, audio, and corruption. Evidence: fixture
      matrix.
  - [ ] A2.3.a Probe actual container/streams/codecs/profiles/levels/bit-depth/
        dimensions/pixel formats and compare extension/MIME/declared metadata.
        Evidence: format tests.
  - [ ] A2.3.b Validate resolution/aspect/crop/rotation/orientation/interlace/
        anamorphic/pixel geometry and profile requirements. Evidence: image
        tests.
  - [ ] A2.3.c Validate nominal/average/variable frame rate, time base, PTS/DTS/
        monotonicity/gaps/duplicates/duration/start offset and audio sync.
        Evidence: timing tests.
  - [ ] A2.3.d Validate color primaries/transfer/matrix/range/HDR metadata and
        depth/disparity/calibration channel relationships. Evidence: color/depth
        tests.
  - [ ] A2.3.e Validate audio channels/rate/codec/duration only where captured
        and never infer consent from presence. Evidence: audio tests.
  - [ ] A2.3.f Decode representative/full sampled frames, detect corruption/
        truncation/errors, and emit pass/fail/unsupported/partial with evidence.
        Evidence: fixture matrix.
- [ ] A2.4 Validate multi-view synchronization and camera identity. Evidence:
      aligned, drifted, dropped-frame, and swapped-camera fixtures.
  - [ ] A2.4.a Resolve expected camera/sensor IDs, serial/position/stream/
        calibration assignment and protect identifying metadata. Evidence:
        identity tests.
  - [ ] A2.4.b Compare trusted timecode/hardware clock/timestamps/audio/visual
        sync events with offsets/jitter/drift/confidence and clock resets.
        Evidence: sync measurements.
  - [ ] A2.4.c Detect missing/duplicate/dropped/late/out-of-order frames and
        variable rate per camera with aligned frame availability. Evidence:
        frame tests.
  - [ ] A2.4.d Detect swapped/mislabeled/duplicate cameras using calibration/
        visual/fingerprint evidence and require human correction. Evidence: swap
        tests.
  - [ ] A2.4.e Persist raw offset/drift/identity evidence separately from
        profile tolerance verdict and remediation/resync plan. Evidence: result
        tests.
  - [ ] A2.4.f Run aligned/drifted/dropped/swapped/clock-reset/partial-view
        fixtures. Evidence: synchronization suite.
- [ ] A2.5 Validate calibration completeness, reprojection error, lens, scale,
      coordinate frame, and capture volume. Evidence: known values.
  - [ ] A2.5.a Verify required cameras/sensors/intrinsics/extrinsics/distortion/
        image size/reference observations/board or volume metadata and versions.
        Evidence: completeness tests.
  - [ ] A2.5.b Recompute per-camera/global reprojection residuals/distributions/
        outliers/coverage/confidence from pinned observations. Evidence:
        reprojection tests.
  - [ ] A2.5.c Validate lens model/parameters/ranges/focus-zoom state/
        rolling-shutter where applicable and image-stream match. Evidence: lens
        tests.
  - [ ] A2.5.d Validate scale/reference distance/units/handedness/up-forward/
        origin/world-camera transforms and round-trip closure. Evidence:
        coordinate tests.
  - [ ] A2.5.e Validate capture volume bounds/overlap/triangulation geometry/
        blind zones/ground plane and expected subject coverage. Evidence: volume
        tests.
  - [ ] A2.5.f Persist raw values separately from profile verdict and run known
        calibrated/error/missing/stale fixtures. Evidence: known-value suite.
- [ ] A2.6 Capture subject consent before biometric processing. Evidence:
      missing or mismatched consent blocks stage dispatch.
  - [ ] A2.6.a Identify each subject/representative and exact project/session/
        take/profile/modalities/purpose/collector before processing. Evidence:
        consent request.
  - [ ] A2.6.b Present current accessible localized notice with biometric/
        model/recipient/territory/retention/revocation/excluded-use details.
        Evidence: notice tests.
  - [ ] A2.6.c Record affirmative verified identity/authority/intent/signature/
        time/document hash/evidence and allow decline/partial modality.
        Evidence: consent capture.
  - [ ] A2.6.d Bind consent reference/version to subject tracks and every raw
        input with protected ordinary projection. Evidence: binding tests.
  - [ ] A2.6.e Enforce current exact match at queue dispatch and worker start/
        commit; block missing/mismatched/expired/revoked state. Evidence:
        dispatch race tests.
- [ ] A2.7 Verify consent scope against project purpose and intended exports.
      Evidence: purpose/territory/recipient mismatch tests.
  - [ ] A2.7.a Resolve exact project/profile intended use/modalities/stages/
        models/providers and proposed output/package/export/consumer plan.
        Evidence: scope inputs.
  - [ ] A2.7.b Compare subject consent purpose/uses/excluded uses/modalities/
        biometric processing/model training/derivatives. Evidence: purpose
        tests.
  - [ ] A2.7.c Compare territory/region/recipient/provider/consumer/host/
        publication/transfer and data residency. Evidence: recipient tests.
  - [ ] A2.7.d Compare term/expiry/retention/revocation/status and allowed
        watermark/redaction/output format/intended claims. Evidence: temporal/
        output tests.
  - [ ] A2.7.e Return eligible/blocked/partial/manual-review with exact
        mismatch, safe alternatives, and no automatic scope expansion. Evidence:
        result contract.
  - [ ] A2.7.f Recheck on project/pipeline/export/recipient/territory/consent
        change and test mismatch matrix. Evidence: lifecycle tests.
- [ ] A2.8 Run input content moderation and privacy classification. Evidence:
      versioned results and manual-review state.
  - [ ] A2.8.a Define profile/jurisdiction/modality-specific categories,
        prohibited/restricted/review thresholds, human qualifications, and
        privacy classes. Evidence: policy registry.
  - [ ] A2.8.b Analyze pinned redacted or protected video/depth/audio/metadata/
        rig/project inputs for unsafe content, minors/vulnerability/nudity/
        violence/identifiers/background/sensitive setting. Evidence: analyzer
        tests.
  - [ ] A2.8.c Persist raw findings/ranges/regions/confidence/model/policy/
        corpus versions separately from moderation/privacy verdict. Evidence:
        result schema.
  - [ ] A2.8.d Route uncertain/high-risk/disagreement to authorized protected
        manual review with minimization and no ordinary thumbnail/log leakage.
        Evidence: review tests.
  - [ ] A2.8.e Apply accept/restrict/redact/quarantine/reject/escalate decision
        and allowed downstream stage/role/export projections. Evidence:
        enforcement tests.
  - [ ] A2.8.f Recompute/invalidate on input/model/policy/profile change and
        evaluate calibrated subgroup/privacy errors. Evidence: versioned report.
- [ ] A2.9 Build face/background/identifier redaction options before wider
      access. Evidence: role projections use the derivative.
  - [ ] A2.9.a Define redaction request pinned to
        source/ranges/subjects/profile/ purpose and face/background/identifier
        types with authorized reviewer. Evidence: request schema.
  - [ ] A2.9.b Detect/track faces, bodies/people, background regions, screens/
        plates/documents/logos/location/device identifiers with confidence.
        Evidence: detection tests.
  - [ ] A2.9.c Allow protected human adjust/add/remove tracks/masks/regions and
        choose blur/pixelate/mask/crop/replace/avatar/metadata strip settings.
        Evidence: editor tests.
  - [ ] A2.9.d Render immutable temporally consistent derivative with exact
        source link, tool/model/settings/ranges/hashes and residual-risk
        measurements. Evidence: render tests.
  - [ ] A2.9.e Map ordinary roles/search/preview/annotation/model/export to
        derivative only and block raw fallback when derivative fails. Evidence:
        projection tests.
  - [ ] A2.9.f Test occlusion/reentry/motion blur/profile/edge frames/audio/
        metadata/cache/download and role revoke. Evidence: redaction suite.
- [ ] A2.10 Build capture quality preview with exposure, blur, occlusion,
      framing, dropped frames, and coverage. Evidence: known clips.
  - [ ] A2.10.a Generate permission-filtered synchronized low-risk previews/
        thumbnails/overlays from exact take/calibration with source time.
        Evidence: preview contract.
  - [ ] A2.10.b Measure exposure/clipping/dynamic range/color/blur/shutter/
        focus/noise per camera/time with confidence. Evidence: image tests.
  - [ ] A2.10.c Measure subject/body-part visibility/occlusion/truncation/
        framing/scale/angle/capture-volume and modality coverage. Evidence:
        coverage tests.
  - [ ] A2.10.d Show dropped/duplicate/late frames, sync/drift/calibration/
        camera identity issues and affected ranges/views. Evidence: temporal
        tests.
  - [ ] A2.10.e Present raw values/units/profile thresholds/unknowns/
        recommendations separately from intake decision, with semantic tables.
        Evidence: UI tests.
  - [ ] A2.10.f Validate known good/bad/borderline/multiview/multi-person/
        protected clips and no unredacted preview leak. Evidence: known clip
        suite.
- [ ] A2.11 Build take grouping, slate, performer, action, marker, and notes.
      Evidence: metadata schema and search.
  - [ ] A2.11.a Define take group/set/version/order/relationship for repeated
        actions, multi-camera parts, calibration, sessions, and variants.
        Evidence: grouping schema.
  - [ ] A2.11.b Edit slate/scene-shot-take/action/motion class/profile/date/
        location/operator/device labels with governed vocabularies. Evidence:
        metadata tests.
  - [ ] A2.11.c Assign pseudonymous performer/subject tracks and consent status
        without exposing protected identity. Evidence: performer tests.
  - [ ] A2.11.d Add time/frame markers/ranges/cues/events/notes with anchors,
        visibility, authorship, revision, and protected fields. Evidence:
        annotation tests.
  - [ ] A2.11.e Support bulk edit/reorder/merge/split/duplicate handling with
        impact/concurrency/undo/audit and immutable input lineage. Evidence:
        operation tests.
  - [ ] A2.11.f Index safe metadata for permission-aware search/facets and test
        protected title/count/snippet leakage. Evidence: search tests.
- [ ] A2.12 Build duplicate and near-duplicate detection. Evidence: exact hash
      and perceptual similarity remain distinct.
  - [ ] A2.12.a Compute/verify cryptographic content hashes and classify exact
        byte duplicates independently of metadata/path/name. Evidence: exact
        dedupe tests.
  - [ ] A2.12.b Compute versioned video/depth/motion/audio perceptual
        fingerprints/features on authorized derivatives with range/alignment.
        Evidence: near-dedupe tests.
  - [ ] A2.12.c Generate candidates with exact versus perceptual reason, aligned
        ranges, transformations, score/confidence, corpus/index version.
        Evidence: candidate contract.
  - [ ] A2.12.d Present protected-aware compare and let human keep/link/replace/
        merge metadata/mark-not-duplicate; never delete automatically. Evidence:
        review tests.
  - [ ] A2.12.e Preserve distinct captures/consent/provenance/calibration even
        when bytes/content match and enforce cross-project isolation. Evidence:
        integrity tests.
  - [ ] A2.12.f Evaluate precision/recall/transforms/partial
        overlaps/multi-view/ identity leakage and stale index. Evidence: dedupe
        report.
- [ ] A2.13 Build resumable ingest with checksum and orphan cleanup. Evidence:
      interruption/retry test.
  - [ ] A2.13.a Define upload
        session/file/chunk/offset/size/hash/tenant/project/
        quota/expiry/idempotency and intended asset metadata. Evidence: upload
        contract.
  - [ ] A2.13.b Upload chunks with range/order/duplicate/retry validation,
        per-chunk hashes, pause/resume/cancel, and bounded concurrency.
        Evidence: transfer tests.
  - [ ] A2.13.c Finalize only after complete size/hash/type/quarantine scan and
        durable blob/reference transaction; handle timeout after commit.
        Evidence: completion tests.
  - [ ] A2.13.d Resume after network/browser/app/worker restart with current
        authorization and no duplicate bytes/assets. Evidence: recovery tests.
  - [ ] A2.13.e Track abandoned/expired/cancelled/unreferenced chunks/blobs and
        clean after safe retention, active lease, legal hold, and
        reconciliation. Evidence: cleanup tests.
  - [ ] A2.13.f Inject interruption/out-of-order/corruption/quota/role revoke/
        process crash/DB-blob mismatch and verify checksums/orphan cleanup.
        Evidence: ingest fault suite.
- [ ] A2.14 Build intake decision records for accept, reject, recapture,
      quarantine, or redact. Evidence: rationale and affected files.
  - [ ] A2.14.a Define immutable decision identity/type/actor-role/time/profile/
        project/session/take/affected asset revisions and input findings.
        Evidence: decision schema.
  - [ ] A2.14.b Require rationale/evidence/quality/consent/privacy/moderation/
        technical considerations, uncertainty, and conditions. Evidence:
        validation tests.
  - [ ] A2.14.c For accept, create eligible intake revision without asserting
        downstream quality; for reject, retain evidence/retention disposition.
        Evidence: disposition tests.
  - [ ] A2.14.d For recapture, record exact deficiencies/protocol/settings/
        deadline/assignment and link replacement take. Evidence: recapture
        tests.
  - [ ] A2.14.e For quarantine/redact, restrict access/stages, create protected
        review or derivative workflow, and preserve source. Evidence:
        enforcement tests.
  - [ ] A2.14.f Enforce role/consent/concurrency and immutable reversals/new
        decisions with audit/notifications. Evidence: authority tests.
- [ ] A2.15 Build desktop live capture with camera/sensor discovery, permission,
      preview, device settings, calibration, clock/sync, subject/ consent match,
      recording, pause, reconnect, local buffer, upload, and operator handoff.
      Evidence: device and network fault journeys.
  - [ ] A2.15.a Discover/identify/capability-probe/hot-plug cameras/depth/IMU/
        audio/sync devices with stable aliases and privacy-safe metadata.
        Evidence: discovery tests.
  - [ ] A2.15.b Request/revoke OS permissions and configure device/source/
        resolution/rate/exposure/focus/orientation/depth/audio/clock with
        unsupported state. Evidence: device tests.
  - [ ] A2.15.c Show low-latency redacted synchronized preview, framing/
        exposure/blur/occlusion/coverage/drop/sync/thermal/storage/network
        quality warnings. Evidence: preview tests.
  - [ ] A2.15.d Capture/validate calibration and exact subject-track/consent/
        profile/purpose/modalities match before arm/processing. Evidence:
        preflight tests.
  - [ ] A2.15.e Implement record/pause/resume/stop/slate/marker/countdown/
        emergency stop with monotonic clocks and immutable crash-safe local
        chunks. Evidence: recording tests.
  - [ ] A2.15.f Handle device/clock/network/process loss with visible state,
        local buffer/quota, dropout markers, reconnect/recalibration, and no
        false continuity. Evidence: fault tests.
  - [ ] A2.15.g Upload resumably with checksums/deduplication/encryption/
        consent scope and reconcile authoritative session/take/assets. Evidence:
        upload tests.
  - [ ] A2.15.h Hand off signed exact session/take/device/calibration/subject/
        consent/quality/upload context to authorized operator and run complete
        fault journeys. Evidence: desktop live E2E.
- [ ] A2.16 Build mobile live capture with supported camera/depth/IMU inputs,
      orientation, thermal/battery/storage/network state, consent, guidance,
      quality feedback, encrypted local buffering, background/interruption
      recovery, and session handoff. Evidence: applicable mobile automation and
      lost-device/privacy tests.
  - [ ] A2.16.a Define supported device/OS/app/camera/depth/IMU/audio/
        orientation/profile combinations and capability negotiation. Evidence:
        mobile matrix.
  - [ ] A2.16.b Request least-privilege permissions and configure capture rate/
        resolution/orientation/lens/depth/IMU sync while handling rotation/
        sensor changes. Evidence: setup tests.
  - [ ] A2.16.c Present accessible localized consent/profile/purpose/modalities/
        privacy/retention notice and bind each subject before capture. Evidence:
        consent tests.
  - [ ] A2.16.d Provide framing/distance/lighting/occlusion/coverage/motion/
        calibration guidance and real-time warnings without medical/coaching
        claims. Evidence: guidance tests.
  - [ ] A2.16.e Monitor thermal/battery/storage/quota/network/background/
        interruption and adapt or stop with explicit quality impact. Evidence:
        resource tests.
  - [ ] A2.16.f Record encrypted crash-safe local chunks/metadata/checksums with
        pause/resume/background-call-lock-screen recovery and dropout markers.
        Evidence: recording tests.
  - [ ] A2.16.g Upload resumably/reconcile/deduplicate, delete by policy after
        durable receipt, and hand off signed session/consent/quality context.
        Evidence: handoff tests.
  - [ ] A2.16.h Run mobile automation for orientation/background/interruption/
        low battery/thermal/storage/network/lost-stolen device/remote revoke/
        privacy. Evidence: mobile E2E.
- [ ] A2.17 Build multi-person take identity and tracking assignment with
      consent per subject, occlusion/crossing review, split/merge correction,
      and protected biometrics. Evidence: identity swaps cannot silently enter
      downstream motion.
  - [ ] A2.17.a Define pseudonymous subject/track/take identity, protected
        biometric/reference features, consent link, and noninferable ordinary
        labels. Evidence: identity schema.
  - [ ] A2.17.b Initialize/associate subjects across cameras/modalities/time
        using authorized evidence/confidence and require consent before
        biometric matching. Evidence: assignment tests.
  - [ ] A2.17.c Detect occlusion/entry-exit/crossing/identity-switch/fragment/
        merge ambiguity with affected frames/views/confidence. Evidence:
        tracking tests.
  - [ ] A2.17.d Provide synchronized protected review with candidate tracks/
        source views and human confirm/reassign/split/merge/unknown controls.
        Evidence: review tests.
  - [ ] A2.17.e Apply corrections as versioned mapping operations, propagate to
        body/hand/face/mesh/annotations/consent, and invalidate downstream
        stages/gates. Evidence: correction tests.
  - [ ] A2.17.f Enforce per-subject modality/purpose/recipient consent
        throughout multi-person outputs; block unconsented track rather than
        empty it silently. Evidence: consent tests.
  - [ ] A2.17.g Exclude biometrics from ordinary previews/search/logs/cache/
        exports and test two tenants/roles/revocation. Evidence: privacy tests.
  - [ ] A2.17.h Run crossing/occlusion/clothing-similar/exit-reentry/swapped
        camera/partial consent fixtures and prove no silent identity swap.
        Evidence: identity safety suite.

### A3 — Pipeline templates, execution, and stage control

- [ ] A3.1 Build pipeline-template registry and lifecycle. Evidence: draft,
      review, publish, deprecate, and archive journey.
  - [ ] A3.1.a Build permissioned registry/search/filter/view for template ID/
        version/profile/owner/status/capabilities/formats/models/freshness.
        Evidence: registry tests.
  - [ ] A3.1.b Create/clone/import/edit immutable draft versions with
        DAG/config/ resource/cache/consent/gate/export metadata validation.
        Evidence: draft tests.
  - [ ] A3.1.c Route review comments/changes/domain-privacy-safety-engineering
        approvals on exact version with semantic diff. Evidence: review tests.
  - [ ] A3.1.d Publish/activate with effective window/compatibility/migration/
        rollback, prohibit mutation, and dispatch only approved versions.
        Evidence: publish tests.
  - [ ] A3.1.e Deprecate/supersede/archive with dependent project/job/profile/
        SDK impact, migration alternatives, and immutable history. Evidence:
        lifecycle journey.
- [ ] A3.2 Build DAG editor with typed ports, cycle prevention, required-stage
      floors, keyboard editing, and semantic list parity. Evidence: a11y and
      validation tests.
  - [ ] A3.2.a Render versioned stage catalog and DAG nodes/ports/edges/
        conditions/resources/status with stable selection and schema-driven
        configuration. Evidence: editor tests.
  - [ ] A3.2.b Add/remove/duplicate/configure/connect/disconnect/reorder/group
        stages with typed kind/cardinality/version/capability validation.
        Evidence: graph operation tests.
  - [ ] A3.2.c Detect cycles/unreachable outputs/missing required input/
        incompatible optional branch/fan-in/resource/export path. Evidence: DAG
        validation.
  - [ ] A3.2.d Enforce profile/code-owned ingest/consent/privacy/validation/
        package floors that template edits cannot bypass. Evidence: floor tests.
  - [ ] A3.2.e Provide source/target/port forms, ordered stage list/dependency
        table, before-after moves, complete keyboard/screen-reader/non-drag
        parity. Evidence: semantic tests.
  - [ ] A3.2.f Support undo/redo/diff/concurrent draft conflict/large graph
        performance and fail invalid publish. Evidence: editor suite.
- [ ] A3.3 Build per-stage configuration forms from versioned schemas. Evidence:
      unknown properties and incompatible versions fail.
  - [ ] A3.3.a Resolve exact stage type/adapter/config schema/UI schema/default/
        capability/profile version and migration. Evidence: schema resolver.
  - [ ] A3.3.b Render accessible typed controls for scalar/enum/range/unit/file/
        artifact/model/rig/mapping/secret/conditional/repeated fields. Evidence:
        form tests.
  - [ ] A3.3.c Validate required/type/range/unit/cross-field/port/resource/
        consent/profile/format constraints client and server. Evidence:
        validation tests.
  - [ ] A3.3.d Store explicit values separately from defaults/secrets, canonical
        serialize/hash, show output-affecting change and downstream cache
        impact. Evidence: persistence tests.
  - [ ] A3.3.e Migrate supported older configs with semantic diff; reject
        unknown properties/incompatible schema/adapter versions without
        defaulting. Evidence: compatibility tests.
- [ ] A3.4 Implement ingest and preprocessing stage adapters. Evidence: exact
      input/output contracts.
  - [ ] A3.4.a Define typed accepted quarantined input assets/calibration/
        metadata/consent/privacy and normalized output kinds/version. Evidence:
        adapter contract.
  - [ ] A3.4.b Implement decode/extract/orient/color/depth/timebase/frame/audio/
        metadata normalization without altering immutable originals. Evidence:
        processing tests.
  - [ ] A3.4.c Implement resize/crop/redaction/sync/resample/frame selection/
        segmentation-ready derivatives with exact transformation lineage.
        Evidence: derivative tests.
  - [ ] A3.4.d Emit per-view/range quality/availability/dropped/corrupt/
        unsupported findings and never fabricate missing frames. Evidence:
        output tests.
  - [ ] A3.4.e Pin tool/library/config/hardware/input-output hashes, resource/
        cost, logs, and deterministic cache/replay eligibility. Evidence:
        provenance tests.
  - [ ] A3.4.f Inject malformed/partial/missing calibration/consent revoke/
        parser/worker/storage faults. Evidence: adapter fault suite.
- [ ] A3.5 Implement 2D pose-estimation stage adapter. Evidence: provider/model
      provenance and confidence output.
  - [ ] A3.5.a Define accepted image/video views/ranges/subjects/profile/
        modality/model/crop/calibration/consent and 2D output schema. Evidence:
        adapter contract.
  - [ ] A3.5.b Negotiate eligible local/provider model/version/body-hand-face/
        multi-person capabilities, resources, privacy, and budget. Evidence:
        capability tests.
  - [ ] A3.5.c Execute inference with deterministic preprocessing/batching/
        coordinate transform and emit landmarks/joints/tracks/confidence/
        visibility/occlusion/tracking loss. Evidence: output tests.
  - [ ] A3.5.d Map outputs back to exact source camera/frame/pixel-normalized
        coordinates and subject identities with uncertainty. Evidence: alignment
        tests.
  - [ ] A3.5.e Persist
        provider/model/weights/config/hardware/corpus/input-output
        hashes/resource/cost and attributed proposal state. Evidence: provenance
        tests.
  - [ ] A3.5.f Test no-person/multi-person/occlusion/crop/rotation/low quality/
        missing modality/provider failure/consent block. Evidence: adapter
        suite.
- [ ] A3.6 Implement pose-lifting/3D reconstruction stage adapter. Evidence:
      coordinate and scale contract.
  - [ ] A3.6.a Define accepted synchronized 2D/multiview/depth/calibration/
        skeleton/subject inputs and 3D joint/trajectory/confidence outputs.
        Evidence: adapter contract.
  - [ ] A3.6.b Negotiate monocular/multiview/depth/model/profile capabilities,
        coordinate/scale recovery assumptions, resources, and consent. Evidence:
        capability tests.
  - [ ] A3.6.c Execute triangulation/lifting/optimization with exact camera/
        calibration/time associations and uncertainty propagation. Evidence:
        reconstruction tests.
  - [ ] A3.6.d Transform to canonical coordinate/units/ground/root frame and
        retain ambiguity/scale confidence rather than inventing absolute scale.
        Evidence: coordinate tests.
  - [ ] A3.6.e Persist model/config/calibration/input-output
        hashes/measurements/ resources/cost and proposal lineage. Evidence:
        provenance tests.
  - [ ] A3.6.f Test known 3D/calibration error/single-view/occlusion/drift/
        swapped camera/low-confidence/failure fixtures. Evidence: adapter suite.
- [ ] A3.7 Implement human-mesh-recovery stage adapter. Evidence: topology and
      subject privacy controls.
  - [ ] A3.7.a Define accepted images/video/2D-3D pose/calibration/subject/
        profile/consent/model inputs and mesh/body-parameter/texture outputs.
        Evidence: adapter contract.
  - [ ] A3.7.b Negotiate approved topology/model/version/modality/hardware/
        privacy/output use and block face/body detail outside consent. Evidence:
        capability tests.
  - [ ] A3.7.c Execute recovery with canonical coordinate/scale/frame alignment,
        stable topology/vertex-joint correspondence, confidence/occlusion.
        Evidence: mesh tests.
  - [ ] A3.7.d Generate redacted/identity-minimized mesh derivatives or suppress
        texture/shape fields by role/profile; keep protected source separated.
        Evidence: privacy tests.
  - [ ] A3.7.e Persist model/weights/config/input-output hashes/topology
        version/ resources/cost/provenance and generated disclosure. Evidence:
        lineage tests.
  - [ ] A3.7.f Test topology mismatch/partial body/loose clothing/multi-person/
        privacy restriction/consent revoke/failure. Evidence: adapter suite.
- [ ] A3.8 Implement skeleton mapping and retargeting stage adapters. Evidence:
      canonical, neural, semantic, and proportional strategy fixtures.
  - [ ] A3.8.a Define source/target skeleton-rig/motion/bind/coordinate/unit/
        mapping/profile/strategy inputs and retargeted output contract.
        Evidence: adapter schemas.
  - [ ] A3.8.b Implement explicit canonical joint-name mapping with hierarchy/
        parent/end-effector/root/twist/finger/face validation. Evidence:
        canonical fixtures.
  - [ ] A3.8.c Implement semantic candidate mapping with confidence/evidence and
        human-confirmed ambiguous/unmapped joints. Evidence: semantic fixtures.
  - [ ] A3.8.d Implement proportional retarget with bone lengths/scale/offset/
        orientation/pose/contact/root constraints and disclosed assumptions.
        Evidence: proportional fixtures.
  - [ ] A3.8.e Implement neural mapping/retarget only through approved model/
        training domain/consent/capability with attributed proposal output.
        Evidence: neural fixtures.
  - [ ] A3.8.f Preserve source and version mapping/output lineage; run quality
        measures, missing-joint/coordinate/version/failure tests. Evidence:
        strategy suite.
- [ ] A3.9 Implement IK/cleanup, contact, jitter, interpolation, and root-motion
      stage adapters. Evidence: non-destructive revision links.
  - [ ] A3.9.a Define distinct stage variants with exact source motion/ranges/
        joints/contacts/constraints/config/profile and preserved original.
        Evidence: stage contracts.
  - [ ] A3.9.b Implement IK/end-effector/contact constraints with chain/pole/
        limits/weights/iterations, residuals, and failure/overconstraint.
        Evidence: IK tests.
  - [ ] A3.9.c Implement jitter/noise filtering with cutoff/window/adaptive/
        confidence handling and no unintended timing/pose drift. Evidence:
        smoothing tests.
  - [ ] A3.9.d Implement gap interpolation for missing frames/joints using
        bounded duration/method/context/confidence and disclose generated
        ranges. Evidence: interpolation tests.
  - [ ] A3.9.e Implement root extraction/in-place/world trajectory/ground/
        orientation/contact preservation and coordinate-unit consistency.
        Evidence: root tests.
  - [ ] A3.9.f Emit immutable new revision with per-frame/joint operation
        lineage; verify unchanged ranges stable, undo/compare, and quality
        measurements. Evidence: cleanup suite.
- [ ] A3.10 Implement blend-shape/facial retargeting only under valid consent.
      Evidence: scope test.
  - [ ] A3.10.a Resolve source face/landmark/expression/blend-shape tracks,
        subject identity, exact consent modalities/uses/target/territory/expiry,
        and privacy profile. Evidence: preflight tests.
  - [ ] A3.10.b Define source-target face topology/landmark/blend-shape/neutral/
        range/sign/scale mapping with confidence and version. Evidence: mapping
        contract.
  - [ ] A3.10.c Retarget curves/poses with calibration/smoothing/limits/
        temporal alignment and preserve tracking loss/occlusion. Evidence:
        output tests.
  - [ ] A3.10.d Emit redacted identity-minimized motion where required, exact
        model/tool/config/source-output lineage, watermark, and disclosure.
        Evidence: privacy/provenance tests.
  - [ ] A3.10.e Recheck consent at dispatch/commit/package, cancel/quarantine/
        invalidate on revoke, and test purpose/recipient/territory mismatch.
        Evidence: scope suite.
- [ ] A3.11 Implement validation and packaging stages as explicit DAG nodes.
      Evidence: export cannot bypass them.
  - [ ] A3.11.a Define typed validation node consuming exact motion/rig/mapping/
        profile/ground truth and producing raw measurements plus versioned
        verdicts. Evidence: validation contract.
  - [ ] A3.11.b Define review/waiver/adjudication prerequisites and block
        packaging on nonpass/stale/unavailable/low-confidence according to
        profile. Evidence: gate tests.
  - [ ] A3.11.c Define packaging node consuming validated approved revision,
        target format/rig/mapping/consent/watermark/provenance and outputs.
        Evidence: package contract.
  - [ ] A3.11.d Enforce DAG reachability/required-stage floors and server/worker
        authorization so export APIs cannot skip nodes or forge results.
        Evidence: bypass tests.
  - [ ] A3.11.e Recheck all dependencies/consent/gate versions atomically before
        package commit and invalidate downstream on change. Evidence: race
        tests.
  - [ ] A3.11.f Test direct API/CLI/worker/cache/imported artifact/export target
        bypass and preserve immutable audit. Evidence: enforcement suite.
- [ ] A3.12 Implement honest `unavailable` for missing GPU, model, reference,
      rig, host, or calibration. Evidence: no placeholder output.
  - [ ] A3.12.a Define required capability/dependency resolution and distinct
        missing/not-configured/unsupported/unauthorized/offline/degraded/
        unavailable reason codes. Evidence: state contract.
  - [ ] A3.12.b Check worker GPU/CPU/memory/architecture/model/licence/version/
        host/profile capability before enqueue and lease. Evidence: compute
        tests.
  - [ ] A3.12.c Check required
        source/reference/calibration/skeleton/rig/mapping/
        consent/rights/artifacts and exact versions. Evidence: dependency tests.
  - [ ] A3.12.d Return no synthetic/empty/identity/placeholder artifact; retain
        safe diagnostics, alternatives, remediation, and retry eligibility.
        Evidence: output tests.
  - [ ] A3.12.e Propagate unavailable to stage/job/batch/console/adapter/package
        without displaying success/healthy. Evidence: projection tests.
  - [ ] A3.12.f Exercise every missing combination plus resource loss after
        enqueue/lease and verify no placeholder output. Evidence: state suite.
- [ ] A3.13 Implement enqueue, priority, dependency, pause, resume, cancel,
      retry, skip-if-optional, and rerun-from-stage. Evidence: state-machine
      tests.
  - [ ] A3.13.a Define canonical job/stage/attempt control state machine, actor/
        authorization/idempotency/expected version and legal transitions.
        Evidence: state contract.
  - [ ] A3.13.b Enqueue exact template/input/config/consent/profile/budget with
        dependency readiness and bounded priority/fairness. Evidence: enqueue
        tests.
  - [ ] A3.13.c Pause/resume at safe stage/checkpoint boundaries with lease/
        partial artifact/local state preservation and dependency revalidation.
        Evidence: pause tests.
  - [ ] A3.13.d Cancel queued/running/worker/provider/commit phases with
        requested-versus-completed state, fencing, cleanup, and partial outputs.
        Evidence: cancel tests.
  - [ ] A3.13.e Retry failed attempts under policy and allow skip only for
        declared optional stage with explicit downstream impact. Evidence:
        retry/skip tests.
  - [ ] A3.13.f Rerun from selected stage with new immutable config/input/
        versions, cache/downstream invalidation, cost preview, and no history
        rewrite. Evidence: state-machine suite.
- [ ] A3.14 Implement stage cache keys over every output-affecting input,
      version, model, and policy. Evidence: cache invalidation tests.
  - [ ] A3.14.a Enumerate all output-affecting input artifact hashes/ranges/
        configs/defaults/calibration/skeleton/rig/mapping/reference/profile/
        consent-privacy policy fields. Evidence: key manifest.
  - [ ] A3.14.b Include stage/adapter/code/container/model/weights/library/
        hardware-sensitive/runtime/policy/evaluator/format versions and seed.
        Evidence: version key tests.
  - [ ] A3.14.c Canonical serialize/normalize order/units/defaults without
        secrets and hash with versioned algorithm/namespace/tenant/privacy
        boundary. Evidence: key tests.
  - [ ] A3.14.d Validate cached artifact manifest/hash/completeness/lineage/
        consent/access/profile/expiry before hit; quarantine mismatch. Evidence:
        read tests.
  - [ ] A3.14.e Invalidate on any dependency/revocation/policy/version change
        and propagate to downstream stage/results/gates/packages. Evidence:
        invalidation tests.
  - [ ] A3.14.f Mutate each key dimension individually plus ordering/no-op
        fields and run collision/cross-tenant/corrupt cache tests. Evidence:
        cache suite.
- [ ] A3.15 Implement checkpoint/recovery for long stages. Evidence: worker
      restart test.
  - [ ] A3.15.a Define checkpointable stage boundaries/state/shard/range/
        progress/model/runtime/random state/artifacts/version and safe
        frequency. Evidence: checkpoint contract.
  - [ ] A3.15.b Write checkpoints atomically with hashes/fencing/job-stage-
        attempt identity and durable artifact references before acknowledging.
        Evidence: persistence tests.
  - [ ] A3.15.c On worker restart/loss, validate current lease/input/config/
        model/policy/consent and resume compatible checkpoint or restart safely.
        Evidence: recovery tests.
  - [ ] A3.15.d Deduplicate/reconcile partial outputs/logs/measurements/resource
        usage/cost and prevent stale worker commit after reassignment. Evidence:
        fencing tests.
  - [ ] A3.15.e Expire/clean checkpoints only after terminal retention/active
        lease/legal hold/replay rules; quarantine corrupt state. Evidence:
        cleanup tests.
  - [ ] A3.15.f Kill workers at every boundary and verify final output/order/
        lineage/equivalence or disclosed nondeterminism. Evidence: restart
        suite.
- [ ] A3.16 Implement deterministic replay eligibility. Evidence: missing
      dependency is explicit.
  - [ ] A3.16.a Define determinism profile per stage covering
        code/model/runtime/ hardware/seed/parallelism/libraries/external
        provider/input/config. Evidence: replay contract.
  - [ ] A3.16.b Verify all immutable input/output-affecting dependencies and
        artifacts remain available, authorized, consent-valid, and
        hash-verified. Evidence: eligibility tests.
  - [ ] A3.16.c Classify exact deterministic/tolerance deterministic/
        nondeterministic/nonreplayable with reason and expected comparison
        method. Evidence: classification.
  - [ ] A3.16.d Replay in isolated namespace using pinned dependencies, prevent
        cache masking when auditing, and record substitutions. Evidence: replay
        tests.
  - [ ] A3.16.e Compare outputs/measurements/events semantically and numerically
        within declared tolerances; preserve both versions. Evidence: comparison
        report.
  - [ ] A3.16.f Expose missing model/weight/runtime/calibration/input/seed/
        licence/consent/provider as explicit blocker, never approximate
        silently. Evidence: failure tests.
- [ ] A3.17 Implement resource/cost estimates and reservations per stage.
      Evidence: over-budget prevents dispatch.
  - [ ] A3.17.a Define resource estimator inputs and outputs for CPU/GPU type/
        memory/storage/network/time/energy/provider units/currency/uncertainty.
        Evidence: estimate schema.
  - [ ] A3.17.b Estimate from stage/model/input frames-duration-resolution/
        modalities/batch/worker history with model/version and confidence.
        Evidence: estimator tests.
  - [ ] A3.17.c Aggregate critical-path and parallel DAG estimates including
        cache likelihood, retries, fan-out/in, artifacts/egress, and target.
        Evidence: plan estimate.
  - [ ] A3.17.d Authorize budget/quotas/priority and reserve capacity/currency/
        storage against owner/project/tenant with expiry and idempotency.
        Evidence: reservation tests.
  - [ ] A3.17.e Recheck before dispatch/lease/scale-up; block over-budget or
        insufficient capacity and require scoped reapproval. Evidence: dispatch
        tests.
  - [ ] A3.17.f Reconcile estimate/reservation/actual/release/refund/overrun and
        calibrate estimators without leaking subject data. Evidence: cost
        report.
- [ ] A3.18 Implement batch submission and fan-out/fan-in. Evidence: partial
      failure and cancellation semantics.
  - [ ] A3.18.a Define batch/item identity/template/profile/input/config/
        priority/budget/consent/idempotency/dependency and aggregate state.
        Evidence: batch schema.
  - [ ] A3.18.b Preflight each item independently for formats/consent/
        capabilities/resources/cache/gates and show eligible/blocked/degraded/
        cost. Evidence: preview tests.
  - [ ] A3.18.c Fan out jobs/stages/shards with bounded concurrency/fairness/
        capacity/priority and preserve item/subject isolation. Evidence:
        dispatch tests.
  - [ ] A3.18.d Fan in only required successful compatible outputs with
        deterministic order/deduplication/aggregation and explicit missing/
        partial inputs. Evidence: aggregation tests.
  - [ ] A3.18.e Cancel whole batch/selected items/queued or running fan-out with
        truthful partial completion and retained successful artifacts. Evidence:
        cancellation tests.
  - [ ] A3.18.f Retry safe failed items/stages, never rerun successes by
        default, and report aggregate partial/failed/complete without false
        success. Evidence: partial failure suite.
- [ ] A3.19 Implement explicit full-body, hand/finger, face/landmark,
      gaze/expression, and multi-person tracking stages with capability
      negotiation. Evidence: missing modality/model/consent yields an honest
      unsupported or blocked state rather than empty tracks.
  - [ ] A3.19.a Define separate typed stage contracts/capability IDs for body,
        hand/finger, face/landmark, gaze/expression, and multi-person identity/
        association. Evidence: modality registry.
  - [ ] A3.19.b Resolve requested input sensors/views/calibration/subject
        tracks/ profile/model/hardware/privacy/consent per modality before
        dispatch. Evidence: negotiation tests.
  - [ ] A3.19.c Execute body and hand stages with skeleton/joint/side/contact/
        confidence/occlusion/tracking-loss outputs and source-frame alignment.
        Evidence: body/hand tests.
  - [ ] A3.19.d Execute face/expression/blend-shape and gaze stages only under
        exact biometric scope, with identity-minimized output where required.
        Evidence: face/gaze tests.
  - [ ] A3.19.e Execute multi-person detection/tracking/association across views
        with protected IDs, crossing/occlusion ambiguity, and correction
        proposals. Evidence: multi-person tests.
  - [ ] A3.19.f Persist independent
        model/config/input-output/provenance/quality/ cost per modality and
        never infer one modality from empty another. Evidence: lineage tests.
  - [ ] A3.19.g Emit unsupported/not-configured/consent-blocked/no-observation/
        tracking-lost/low-confidence distinctly, not empty successful tracks.
        Evidence: absence tests.
  - [ ] A3.19.h Run modality combinations, partial consent, missing
        sensor/model/ GPU, multi-person crossings, revocation, and privacy
        projections. Evidence: capability suite.
- [ ] A3.20 Implement video-analysis stages for action/segment/event detection,
      camera motion, occlusion, contacts, tracking confidence, and review
      proposals. Evidence: model output remains attributed and correctable.
  - [ ] A3.20.a Define separate stage/output contracts for temporal segments/
        actions/events/camera motion/occlusion/contacts/tracking-quality
        proposals. Evidence: analysis registry.
  - [ ] A3.20.b Pin source take/views/ranges/profile/taxonomy/reference/model/
        calibration/subjects/consent and exclude prohibited inferences.
        Evidence: request tests.
  - [ ] A3.20.c Detect action/phase/segment/event candidates with start/end/
        label/confidence/alternatives and exact source locators. Evidence:
        temporal tests.
  - [ ] A3.20.d Estimate camera motion/shots and subject/joint visibility/
        occlusion/contact/tracking confidence with raw measurements. Evidence:
        geometry tests.
  - [ ] A3.20.e Persist model/weights/config/taxonomy/corpus/input-output
        hashes/ uncertainty/cost and proposal-only status. Evidence: provenance
        tests.
  - [ ] A3.20.f Provide synchronized preview/table review and human accept/edit/
        split/merge/relabel/reject/unknown with versioned annotations. Evidence:
        review tests.
  - [ ] A3.20.g Evaluate known actions/segments/events/camera/occlusion/contact/
        tracking fixtures, subgroup/profile slices, and false claims. Evidence:
        calibrated report.
- [ ] A3.21 Implement motion enhancement, generative completion, animation
      blending, retiming, gap filling, segmentation, and IK as non-destructive
      stage variants with exact edited ranges and source lineage. Evidence:
      unchanged frames remain stable and generated ranges are disclosed.
  - [ ] A3.21.a Define distinct operation stage contracts with exact source
        clip/ranges/joints/curves/masks/context/constraints/model/seed/profile
        and allowed output. Evidence: operation registry.
  - [ ] A3.21.b Implement filtering/enhancement and segmentation as versioned
        proposals with raw measurements, boundaries, and no destructive source
        edit. Evidence: enhancement tests.
  - [ ] A3.21.c Implement gap filling/generative completion with bounded missing
        frames/joints, context/contact/environment constraints, alternatives,
        and uncertainty. Evidence: completion tests.
  - [ ] A3.21.d Implement blend/transition between motions with alignment/
        contact/root/phase/duration masks and seam measurements. Evidence:
        blending tests.
  - [ ] A3.21.e Implement retiming/time warp with source-destination map,
        contact/event preservation, derivative curves, and monotonicity.
        Evidence: retiming tests.
  - [ ] A3.21.f Implement IK constraint correction with chain/joint limits/
        end-effectors/contacts/iterations/residuals and overconstraint failure.
        Evidence: IK tests.
  - [ ] A3.21.g Produce immutable candidates with per-frame/joint generated/
        changed/unchanged labels, model/tool/config/seed/source lineage and
        disclosure. Evidence: provenance tests.
  - [ ] A3.21.h Audition/compare/accept-edit-reject exact ranges and verify
        promised unchanged frames/curves stable within zero or declared
        tolerance. Evidence: preservation suite.
- [ ] A3.22 Implement profile-specific DAG templates for yoga, fitness, dance,
      martial arts, sports, medical/rehabilitation, film/VFX, and gaming.
      Evidence: each template satisfies A0.15 requirements and has independent
      known-value fixtures; shared stages do not imply profile proof.
  - [ ] A3.22.a Define the common template contract for version, capture
        protocol, intended/prohibited use, stages/models, annotations,
        evaluators/floors, qualified reviewer, outputs, warnings, and handoff.
        Evidence: template schema.
  - [ ] A3.22.b Ratify yoga and fitness templates with distinct pose/exercise
        vocabulary, repetitions/holds, reference measures, safety boundaries,
        and reviewer qualifications. Evidence: independent fixtures.
  - [ ] A3.22.c Ratify dance and martial-arts templates with distinct
        choreography/technique, rhythm/phase, partner/contact, weapon/safety,
        and performance-review semantics. Evidence: independent fixtures.
  - [ ] A3.22.d Ratify sport-specific templates without collapsing sports into
        one profile; declare event/technique measures, equipment/environment,
        prohibited claims, and qualified review. Evidence: sport registry and
        independent fixtures.
  - [ ] A3.22.e Ratify medical and rehabilitation templates with approved
        protocol, intended use, contraindication/escalation, measurement
        validity, privacy, and qualified clinical review. Evidence: independent
        protected fixtures.
  - [ ] A3.22.f Ratify film/VFX and gaming templates with distinct shot/runtime
        constraints, retarget/loop/root-motion needs, deliverables, and creative
        versus technical approval. Evidence: independent fixtures.
  - [ ] A3.22.g Prove every profile selects only compatible stages/models and
        cannot inherit another profile's thresholds, claims, review, or release
        evidence. Evidence: cross-profile negative suite.
- [ ] A3.23 Implement format conversion among every ratified video, mocap,
      skeleton, animation, and engine/DCC package with loss preview,
      coordinate/unit mapping, embedded metadata, and round-trip diagnostics.
      Evidence: per-format-pair compatibility matrix.
  - [ ] A3.23.a Register each source/target format and profile version with
        parse/write capabilities, extensions/MIME, limits, and unsupported
        constructs. Evidence: conversion registry.
  - [ ] A3.23.b Parse and normalize source time base, coordinate system, units,
        skeleton/rig, animation channels, markers, metadata, and embedded media
        without discarding unknown data silently. Evidence: parse goldens.
  - [ ] A3.23.c Define explicit video, mocap, skeleton, animation, and
        engine/DCC source-target mappings, including required intermediate
        representations. Evidence: pairwise mapping matrix.
  - [ ] A3.23.d Preview dropped, approximated, baked, resampled, renamed, or
        synthesized data and require acceptance when the target is lossy.
        Evidence: loss-decision records.
  - [ ] A3.23.e Convert with deterministic settings and embed source, mapping,
        tool/version, checksum, rights/consent, and transformation provenance.
        Evidence: package inspection.
  - [ ] A3.23.f Read back every supported target and compare timing, hierarchy,
        transforms, curves, contacts, metadata, and declared loss against the
        source. Evidence: per-pair round-trip suite.
- [ ] A3.24 Implement video-to-CG replacement for actor segmentation, tracking,
      pose/face/hand transfer, target avatar/rig, occlusion, lighting/shadow,
      rendering, compositing, temporal quality, review, and delivery through
      governed Isis/Yemaya/Bellona seams. Evidence: subject consent, source/
      derivative provenance, generated-region disclosure, and per-shot
      replacement tests.
  - [ ] A3.24.a Register the source clip, shot ranges, subjects, consent scope,
        intended use, target project, and protected-source handling before
        analysis. Evidence: admissibility tests.
  - [ ] A3.24.b Segment and track each authorized actor with stable IDs,
        confidence, occlusion states, masks, and reviewer corrections. Evidence:
        tracking fixtures.
  - [ ] A3.24.c Select a rights-cleared target avatar/rig and verify body, face,
        hand, clothing, material, and output compatibility. Evidence:
        compatibility gate.
  - [ ] A3.24.d Transfer body, face, and hand motion as separately attributable
        stages with source ranges, mappings, confidence, corrections, and
        unchanged-region guarantees. Evidence: transfer tests.
  - [ ] A3.24.e Reconstruct occlusion, camera, lighting, contact shadows, and
        scene interactions with explicit observed versus generated regions.
        Evidence: per-shot scene report.
  - [ ] A3.24.f Render through the governed Bellona/Yemaya boundary and
        composite through the governed Isis/Yemaya boundary without granting Aja
        foreign mutation authority. Evidence: seam contracts.
  - [ ] A3.24.g Measure temporal identity, mask, edge, motion, lighting, shadow,
        occlusion, and source-background continuity across every shot. Evidence:
        known-value and perceptual gates.
  - [ ] A3.24.h Review, revise, approve, package, deliver, and read back exact
        shots with subject/derivative provenance, generated-region disclosure,
        rights/consent state, and recall hooks. Evidence: replacement journey.
- [ ] A3.25 Implement text/reference-to-motion and physics-aware generative
      animation as proposal stages with motion intent, duration, skeleton,
      contacts, environment constraints, seed/model, alternatives, validation,
      and human acceptance. Evidence: generated frames are attributable and
      cannot bypass consent, quality, intended-use, or export gates.
  - [ ] A3.25.a Define separate text, image/video reference, pose/keyframe,
        trajectory, and mixed-conditioning request contracts. Evidence: typed
        input schemas.
  - [ ] A3.25.b Resolve motion intent, duration, motion class, skeleton/rig,
        coordinate/time basis, environment, contacts, obstacles, forces, and
        prohibited uses before generation. Evidence: constraint plan.
  - [ ] A3.25.c Enforce reference rights/consent and detect requests that would
        reproduce a protected performer or unsupported medical/performance
        claim. Evidence: admissibility suite.
  - [ ] A3.25.d Generate multiple immutable candidates with model/version,
        configuration, seed, inputs, constraints, nondeterminism, and
        per-frame/joint generation provenance. Evidence: candidate manifests.
  - [ ] A3.25.e Validate kinematics, contacts, collisions, balance, dynamics,
        skeleton compatibility, temporal continuity, and profile-specific
        quality without rewriting a failed candidate. Evidence: gate suite.
  - [ ] A3.25.f Let an authorized human compare, annotate, accept, edit, or
        reject candidates while preserving the original proposal and decision
        rationale. Evidence: review journey.
  - [ ] A3.25.g Recompute consent, intended-use, quality, compatibility, and
        export gates on the accepted revision and disclose generated ranges in
        every derivative/package. Evidence: fail-closed release test.

### A4 — Motion quality, validation, and calibrated evaluation

- [ ] A4.1 Define versioned gate profiles by motion type, consumer, and export
      target. Evidence: every publishable format has a profile.
  - [ ] A4.1.a Define the gate-profile schema for motion class, intended use,
        consumer, target, applicable measurements, evaluator versions,
        thresholds, human-review bands, and failure policy. Evidence: schema
        tests.
  - [ ] A4.1.b Register a separately owned profile for every publishable
        motion-type/consumer/export combination. Evidence: coverage matrix.
  - [ ] A4.1.c Version, approve, activate, retire, and migrate profiles without
        rewriting historical verdicts. Evidence: lifecycle tests.
  - [ ] A4.1.d Reject validation or release when no current compatible profile
        exists. Evidence: missing-profile negative test.
- [ ] A4.2 Implement skeleton hierarchy, joint naming, bind pose, unit, axis,
      and frame-rate validation. Evidence: broken fixtures.
  - [ ] A4.2.a Validate hierarchy topology, unique joints, parentage, roots, and
        required bones against the selected skeleton profile. Evidence:
        hierarchy fixtures.
  - [ ] A4.2.b Validate canonical/aliased joint names and surface unknown,
        duplicate, missing, or ambiguous mappings. Evidence: naming fixtures.
  - [ ] A4.2.c Validate bind/rest pose, transforms, orientation, scale, units,
        axes/handedness, and coordinate metadata. Evidence: transform fixtures.
  - [ ] A4.2.d Validate source and declared frame rate/time base without
        silently resampling. Evidence: timing fixtures.
- [ ] A4.3 Implement missing, duplicate, discontinuous, and non-monotonic frame
      checks. Evidence: crafted clips.
  - [ ] A4.3.a Parse expected and observed frame/sample indices and timestamps.
        Evidence: frame-ledger fixture.
  - [ ] A4.3.b Detect missing and duplicate frames/samples with exact ranges and
        counts. Evidence: gap/duplicate fixtures.
  - [ ] A4.3.c Detect non-monotonic timestamps, discontinuities, resets, and
        inconsistent time steps. Evidence: temporal fixtures.
  - [ ] A4.3.d Separate raw findings from profile verdict and proposed repair.
        Evidence: policy-recompute test.
- [ ] A4.4 Implement foot/hand contact and sliding measurements. Evidence:
      independently derived fixture values.
  - [ ] A4.4.a Detect or ingest foot/hand contact events with side, effector,
        frame range, confidence, and ground/object reference. Evidence: contact
        fixtures.
  - [ ] A4.4.b Measure contact-point displacement, velocity, rotation, and
        penetration in the declared coordinate/time basis. Evidence:
        independently calculated values.
  - [ ] A4.4.c Classify planted, transitioning, airborne, supported, and unknown
        intervals without treating uncertainty as pass. Evidence: state tests.
  - [ ] A4.4.d Apply motion/profile-specific sliding thresholds and retain raw
        event measurements for review. Evidence: verdict tests.
- [ ] A4.5 Implement jitter/noise and temporal smoothness measurements.
      Evidence: synthetic signal fixtures.
  - [ ] A4.5.a Define joint/channel/range-specific position, rotation, velocity,
        acceleration, jerk, and frequency-domain signals. Evidence: measurement
        spec.
  - [ ] A4.5.b Compute signals at the declared sample rate with explicit unwrap,
        boundary, missing-data, and filter behavior. Evidence: synthetic
        goldens.
  - [ ] A4.5.c Detect local spikes, sustained noise, discontinuities, and
        over-smoothed loss of intended motion separately. Evidence: crafted
        fixtures.
  - [ ] A4.5.d Report exact offending joints/ranges and raw values before
        profile thresholds determine verdict. Evidence: diagnostic tests.
- [ ] A4.6 Implement bone-length and joint-limit consistency. Evidence:
      anatomical fixtures.
  - [ ] A4.6.a Resolve skeleton-specific reference bone lengths, tolerances,
        scale rules, and joint-limit definitions. Evidence: profile fixtures.
  - [ ] A4.6.b Measure per-frame bone-length drift and distinguish scale or
        mapping errors from subject-independent anatomical assumptions.
        Evidence: known-value tests.
  - [ ] A4.6.c Detect angular, translational, twist, and hyperextension limit
        violations with exact joints/ranges. Evidence: limit fixtures.
  - [ ] A4.6.d Mark unsupported anatomy/profile combinations unavailable rather
        than inferring clinical validity. Evidence: negative tests.
- [ ] A4.7 Implement balance, center-of-mass, ground penetration, velocity,
      acceleration, and physics plausibility diagnostics. Evidence: known motion
      corpus.
  - [ ] A4.7.a Resolve mass model, support contacts/polygon, ground plane,
        gravity, environment, time basis, and intended-use assumptions.
        Evidence: diagnostic manifest.
  - [ ] A4.7.b Compute center of mass, support margin, penetration, velocity,
        acceleration, momentum, and discontinuity signals with uncertainty.
        Evidence: analytical fixtures.
  - [ ] A4.7.c Detect incompatible contact, collision, ballistic, force, and
        balance behavior by motion phase. Evidence: known-motion corpus.
  - [ ] A4.7.d Separate animation plausibility warnings from medical,
        biomechanical, or safety claims requiring qualified validation.
        Evidence: claim-boundary tests.
- [ ] A4.8 Implement pose, trajectory, contact, and timing fidelity against
      ground truth when available. Evidence: calibrated dataset.
  - [ ] A4.8.a Admit ground truth only with source, rights/consent, coordinate,
        skeleton, timing, annotation, and calibration provenance. Evidence:
        dataset gate.
  - [ ] A4.8.b Align prediction and reference across coordinate system, scale,
        skeleton mapping, temporal offset, and valid comparison ranges.
        Evidence: alignment goldens.
  - [ ] A4.8.c Compute pose, joint, trajectory, contact-event, duration, phase,
        and timing metrics with missing/occluded labels handled explicitly.
        Evidence: independently derived values.
  - [ ] A4.8.d Report per-sequence/per-joint/per-phase results, uncertainty, and
        calibration limitations before verdict. Evidence: calibrated report.
- [ ] A4.9 Implement retarget fidelity for end effectors, root, silhouette,
      contacts, and joint rotations. Evidence: source/target fixture pairs.
  - [ ] A4.9.a Lock source motion, target rig, mapping, rest poses, scale,
        retarget settings, and comparison coordinate/time basis. Evidence:
        retarget manifest.
  - [ ] A4.9.b Measure end-effector, root trajectory/orientation, joint
        rotation, contact, and silhouette deviations separately. Evidence:
        pairwise goldens.
  - [ ] A4.9.c Detect target-limit violations, foot/hand drift, self-
        intersection, and proportion-induced unsupported comparisons. Evidence:
        retarget fixtures.
  - [ ] A4.9.d Preserve raw source-target measurements and profile-specific
        verdicts for exact frame review. Evidence: diagnostic report.
- [ ] A4.10 Implement perceptual quality evaluation with model/version,
      uncertainty, and human-review band. Evidence: calibrated report.
  - [ ] A4.10.a Define the evaluation corpus, labels, sampling, consent, motion
        and demographic cohorts, exclusions, and human adjudication protocol.
        Evidence: approved dataset card.
  - [ ] A4.10.b Register evaluator model/version, preprocessing, configuration,
        input/output schema, and deterministic or stochastic behavior. Evidence:
        evaluator manifest.
  - [ ] A4.10.c Calibrate scores and uncertainty against held-out human ratings
        by supported cohort and intended use. Evidence: calibration report.
  - [ ] A4.10.d Set pass, fail, and mandatory-human-review bands without using
        low-confidence or out-of-distribution scores as pass. Evidence:
        threshold tests.
  - [ ] A4.10.e Monitor drift, subgroup error, calibration expiry, and model
        replacement with historical replay. Evidence: evaluator release gate.
- [ ] A4.11 Implement motion-loop seam and blend compatibility checks. Evidence:
      loop fixtures.
  - [ ] A4.11.a Resolve loop endpoints or blend windows, phase/contact state,
        root-motion policy, and channels included in comparison. Evidence: seam
        manifest.
  - [ ] A4.11.b Measure pose, rotation, velocity, acceleration, root, contact,
        curve, and event discontinuity across the seam. Evidence: analytical
        loop fixtures.
  - [ ] A4.11.c Evaluate pairwise blend compatibility over permitted alignment,
        duration, contact, and phase choices. Evidence: blend matrix.
  - [ ] A4.11.d Report exact incompatible channels/ranges and avoid silently
        applying a destructive repair. Evidence: failure journey.
- [ ] A4.12 Implement blend-shape/facial quality gates under consent. Evidence:
      protected evaluation.
  - [ ] A4.12.a Admit facial source, labels, and evaluation only under current
        subject consent, purpose, retention, and reviewer authorization.
        Evidence: protected-data gate.
  - [ ] A4.12.b Validate blend-shape names, ranges, exclusivity/coactivation,
        neutral pose, temporal completeness, and rig compatibility. Evidence:
        facial fixtures.
  - [ ] A4.12.c Measure facial landmark/expression fidelity, jitter, popping,
        lip/eye timing, symmetry where applicable, and uncertainty. Evidence:
        protected known values.
  - [ ] A4.12.d Revoke, restrict, or recompute facial evidence when consent,
        model, rig, or policy changes. Evidence: revocation test.
- [ ] A4.13 Implement export-format conformance and round-trip checks. Evidence:
      per-format goldens.
  - [ ] A4.13.a Select the exact target format/profile/toolchain version and
        required metadata, hierarchy, timing, naming, and packaging rules.
        Evidence: conformance registry.
  - [ ] A4.13.b Validate the emitted bytes/package against official and pinned
        custom validators. Evidence: conformance goldens.
  - [ ] A4.13.c Re-import/read back through an independent target reader and
        compare motion, rig, curves, timing, contacts, metadata, and files.
        Evidence: round-trip suite.
  - [ ] A4.13.d Surface unsupported/lossy features and fail release when loss
        exceeds the accepted target profile. Evidence: loss tests.
- [ ] A4.14 Persist raw metrics separately from verdicts. Evidence: policy
      recompute.
  - [ ] A4.14.a Store immutable measurement identity, inputs, units, ranges,
        algorithm/version, configuration, and raw values. Evidence: metric
        schema.
  - [ ] A4.14.b Store verdict identity, profile/version, thresholds, policy
        inputs, status, reason, and reviewer action separately. Evidence:
        verdict schema.
  - [ ] A4.14.c Recompute a new verdict from unchanged raw metrics under a new
        policy without rewriting historical results. Evidence: recompute test.
- [ ] A4.15 Version algorithms, models, ground truth, calibration, thresholds,
      and profile. Evidence: result provenance.
  - [ ] A4.15.a Assign immutable versions and checksums to evaluator code,
        model, ground truth, calibration, threshold set, and gate profile.
        Evidence: registry.
  - [ ] A4.15.b Persist the complete version tuple and runtime environment on
        each measurement and verdict. Evidence: provenance query.
  - [ ] A4.15.c Detect stale, revoked, incompatible, or expired dependencies and
        identify affected assets/runs/releases. Evidence: impact test.
- [ ] A4.16 Add unavailable, insufficient-ground-truth, low-confidence, and
      evaluator-failure states. Evidence: none renders as pass.
  - [ ] A4.16.a Define typed non-pass states with reason codes, diagnostic
        detail, retryability, owner, and permitted next action. Evidence: state
        contract.
  - [ ] A4.16.b Emit unavailable when a required evaluator, model, profile,
        dependency, or environment cannot run. Evidence: outage test.
  - [ ] A4.16.c Emit insufficient-ground-truth and low-confidence when required
        comparison or calibration support is absent. Evidence: boundary tests.
  - [ ] A4.16.d Emit evaluator-failure separately from domain-quality failure
        and prevent all non-pass states from satisfying release. Evidence:
        fail-closed UI/API tests.
- [ ] A4.17 Build manual QA sampling and adjudication. Evidence: reviewer
      decisions link to exact frames and measurements.
  - [ ] A4.17.a Define risk-weighted, random, drift-triggered, disagreement, and
        mandatory-review sampling rules. Evidence: sampling policy.
  - [ ] A4.17.b Create immutable review assignments with qualified reviewer,
        conflict rules, due state, exact asset/revision, and blinded context
        where required. Evidence: assignment tests.
  - [ ] A4.17.c Capture decisions, reason codes, annotations, exact frames/
        joints/measurements, confidence, and requested remediation. Evidence:
        adjudication record.
  - [ ] A4.17.d Escalate disagreement and update calibration/threshold evidence
        without overwriting original machine or human decisions. Evidence:
        adjudication journey.
- [ ] A4.18 Build drift monitoring by capture setup, subject-safe cohort, motion
      class, model, rig, and version. Evidence: privacy-approved report.
  - [ ] A4.18.a Define privacy-safe dimensions, minimum cohort size, baselines,
        windows, statistical tests, and alert thresholds. Evidence: approved
        monitoring spec.
  - [ ] A4.18.b Aggregate quality, failure, uncertainty, and reviewer outcomes
        by capture setup, safe cohort, motion class, model, rig, and version.
        Evidence: bounded metrics.
  - [ ] A4.18.c Suppress or combine sparse cohorts and exclude subject identity
        or raw biometric/media data. Evidence: privacy tests.
  - [ ] A4.18.d Investigate alerts through versioned slices and link confirmed
        drift to model/profile rollback, recalibration, or review expansion.
        Evidence: drift drill.

### A5 — Preview, cleanup, retargeting, annotation, and library

- [ ] A5.1 Build synchronized source video, 2D pose, 3D skeleton, mesh, and
      retarget preview. Evidence: deterministic time alignment.
  - [ ] A5.1.a Define the shared time basis, offsets, rates, dropped/missing
        samples, coordinate transforms, and stream availability contract.
        Evidence: synchronization schema.
  - [ ] A5.1.b Load authorized source video and derived 2D pose without exposing
        protected media to unauthorized viewers. Evidence: access tests.
  - [ ] A5.1.c Render 3D skeleton, mesh, and retarget streams at the same
        selected source instant with explicit interpolation behavior. Evidence:
        alignment goldens.
  - [ ] A5.1.d Keep transport, seek, range, loop, and selected subject/joint
        synchronized across every available view. Evidence: Playwright journey.
  - [ ] A5.1.e Surface missing, stale, offset, low-confidence, and unavailable
        streams without fabricating visual alignment. Evidence: state matrix.
- [ ] A5.2 Build before/after and multi-run comparison with synchronized camera,
      transport, speed, loop, and overlays. Evidence: accessible comparison.
  - [ ] A5.2.a Select immutable source/run/artifact revisions and validate
        skeleton, timing, and view compatibility. Evidence: selection tests.
  - [ ] A5.2.b Synchronize camera, transport, speed, range, loop, subject,
        skeleton, and overlay settings across compared revisions. Evidence:
        interaction tests.
  - [ ] A5.2.c Support side-by-side, overlay, difference, and A/B switching
        without losing revision identity. Evidence: comparison journey.
  - [ ] A5.2.d Provide a semantic difference table, keyboard controls, and
        announced current revision/value for non-visual review. Evidence:
        accessibility suite.
- [ ] A5.3 Build joint/contact/error heatmaps with semantic table parity.
      Evidence: non-visual access to exact values.
  - [ ] A5.3.a Define color-independent scales, units, thresholds, missing/
        unavailable states, and selected measure/profile/version. Evidence:
        legend tests.
  - [ ] A5.3.b Map per-frame/joint/contact/error values to the visual skeleton
        and timeline without aggregating away critical failures. Evidence:
        mapping goldens.
  - [ ] A5.3.c Provide a sortable/filterable semantic table with exact joint,
        range, raw value, unit, threshold, status, and source measure. Evidence:
        parity tests.
  - [ ] A5.3.d Keep visual and table focus/selection synchronized by keyboard
        and assistive technology. Evidence: Playwright accessibility journey.
- [ ] A5.4 Build frame/time/joint-range annotation. Evidence: anchors survive
      unrelated revisions.
  - [ ] A5.4.a Define annotation anchors for artifact revision, source time/
        frame range, subject, joint/channel, coordinate basis, and selected
        measurements. Evidence: anchor schema.
  - [ ] A5.4.b Create, edit, reply, resolve, reopen, and delete annotations with
        authorization, audit, and concurrency control. Evidence: API/UI tests.
  - [ ] A5.4.c Rebase anchors only through explicit mapping when a related
        revision changes timing or skeleton; mark ambiguous/orphaned anchors.
        Evidence: revision fixtures.
  - [ ] A5.4.d Verify unrelated revisions do not move or mutate existing
        annotation anchors. Evidence: stability test.
- [ ] A5.5 Build cleanup operations as versioned commands, never destructive
      source edits. Evidence: undo/redo and lineage.
  - [ ] A5.5.a Register typed cleanup commands with exact input revision, target
        ranges/joints/channels, parameters, preconditions, and impact preview.
        Evidence: command registry.
  - [ ] A5.5.b Execute each command into a new immutable revision with operator,
        tool/version, settings, changed ranges, and source lineage. Evidence:
        mutation tests.
  - [ ] A5.5.c Implement undo/redo as revision navigation or compensating
        commands rather than source mutation. Evidence: history journey.
  - [ ] A5.5.d Compare and validate the resulting revision before it can replace
        a selected candidate. Evidence: acceptance gate.
- [ ] A5.6 Build skeleton and bone mapping editor with source/target validation.
      Evidence: keyboard and list parity.
  - [ ] A5.6.a Load immutable source/target skeleton revisions, naming aliases,
        required joints, rest poses, units, and mapping constraints. Evidence:
        editor contract.
  - [ ] A5.6.b Create, remove, remap, mirror, and auto-propose mappings without
        accepting ambiguous or incompatible suggestions silently. Evidence:
        mapping tests.
  - [ ] A5.6.c Validate completeness, uniqueness, hierarchy, orientation, scale,
        unmapped influence, and target-limit compatibility. Evidence: validation
        fixtures.
  - [ ] A5.6.d Provide keyboard/list operations and semantic source-to-target
        status equivalent to the visual mapping view. Evidence: accessibility
        journey.
  - [ ] A5.6.e Save a versioned mapping draft and require explicit review/
        promotion before production use. Evidence: lifecycle test.
- [ ] A5.7 Build retarget profile compare and decision record. Evidence:
      selected mapping remains linked.
  - [ ] A5.7.a Select compatible source motion, target rig, mapping, retarget
        profiles, evaluator profile, and exact revisions. Evidence: setup tests.
  - [ ] A5.7.b Run and display comparable outputs, raw fidelity metrics,
        warnings, limitations, and cost for each profile. Evidence: comparison
        fixture.
  - [ ] A5.7.c Capture the selected profile/mapping/revisions, reviewer,
        rationale, rejected alternatives, and intended consumer. Evidence:
        decision record.
  - [ ] A5.7.d Detect stale decisions when any linked mapping, rig, profile, or
        motion revision changes. Evidence: invalidation test.
- [ ] A5.8 Build avatar/rig library search, compatibility, preview, rights, and
      version selection. Evidence: ineligible rig reasons.
  - [ ] A5.8.a Index only authorized avatar/rig metadata, versions, skeleton,
        features, formats, rights, license, consent constraints, and lifecycle
        state. Evidence: search policy tests.
  - [ ] A5.8.b Filter and rank by current project, motion, pipeline, target, and
        rights/compatibility requirements. Evidence: eligibility fixtures.
  - [ ] A5.8.c Preview authorized geometry, skeleton, materials, blend shapes,
        motions, limitations, and provenance at an exact version. Evidence:
        preview tests.
  - [ ] A5.8.d Explain each ineligible, quarantined, revoked, incompatible, or
        unavailable rig without leaking protected metadata. Evidence: reason
        matrix.
  - [ ] A5.8.e Lock the selected rig revision and recheck it at execution and
        export. Evidence: race test.
- [ ] A5.9 Build motion asset library with tags, motion class, skeleton,
      quality, consent, watermark, lineage, and release status. Evidence:
      permission-aware search.
  - [ ] A5.9.a Define the motion-asset projection from authoritative artifact,
        skeleton, quality, consent, rights, watermark, lineage, and release
        records. Evidence: projection contract.
  - [ ] A5.9.b Index approved searchable fields while excluding protected
        subject/source identity and unauthorized media-derived metadata.
        Evidence: index tests.
  - [ ] A5.9.c Filter and facet by governed tags, motion class, skeleton,
        compatibility, quality freshness, consent/use, watermark, lineage, and
        release state. Evidence: search fixtures.
  - [ ] A5.9.d Show exact version, eligibility reasons, dependencies, preview
        availability, and current recall state before selection. Evidence:
        library journey.
  - [ ] A5.9.e Reconcile index/library state after supersession, consent change,
        quarantine, unpublish, or recall. Evidence: propagation test.
- [ ] A5.10 Build exact and perceptual dedupe with merge/keep decisions.
      Evidence: no automatic destructive merge.
  - [ ] A5.10.a Compute exact identity from normalized bytes/checksums while
        preserving distinct ownership and provenance records. Evidence: exact
        duplicate fixtures.
  - [ ] A5.10.b Compute versioned perceptual candidates from authorized motion
        features with score, uncertainty, and compatible skeleton/time basis.
        Evidence: near-duplicate fixtures.
  - [ ] A5.10.c Present source, rights/consent, quality, versions, dependencies,
        and exact differences for reviewer comparison. Evidence: review journey.
  - [ ] A5.10.d Record keep-both, relate, consolidate-reference, or reject
        decisions without deleting source artifacts or lineage. Evidence:
        non-destructive tests.
- [ ] A5.11 Build collections, sets, variants, related motions, and retirement
      through curation bundles. Evidence: no direct production writes.
  - [ ] A5.11.a Define versioned curation-bundle operations for collection/set
        membership, variant groups, relationships, metadata, and retirement.
        Evidence: bundle schema.
  - [ ] A5.11.b Validate referenced asset versions, permissions, lifecycle,
        rights/consent, compatibility, and dependency impact. Evidence:
        preflight tests.
  - [ ] A5.11.c Review, approve, apply, reject, or rollback a bundle with
        idempotency, concurrency, and audit. Evidence: workflow tests.
  - [ ] A5.11.d Prohibit direct production mutation outside the approved bundle
        applier and verify read-back. Evidence: authorization test.
- [ ] A5.12 Build reference video library with protected-source controls.
      Evidence: subject identity and raw media do not leak.
  - [ ] A5.12.a Register reference videos with subject/owner, consent purpose,
        retention, geography, access compartment, checksums, and lineage.
        Evidence: protected-source schema.
  - [ ] A5.12.b Produce privacy-minimized searchable projections that omit
        subject identity, raw frames, biometrics, and sensitive inferred
        metadata. Evidence: index inspection.
  - [ ] A5.12.c Enforce object/property authorization, watermarking, bounded
        preview, download restrictions, and audited access. Evidence: access
        tests.
  - [ ] A5.12.d Revoke, expire, quarantine, retain under hold, or delete source
        and derived previews according to policy. Evidence: lifecycle drill.
- [ ] A5.13 Build consent expiry/revocation impact view per asset. Evidence:
      released and derived assets appear.
  - [ ] A5.13.a Traverse from consent grant/subject/source to every capture,
        artifact, derivative, library entry, package, release, consumer, and
        retained copy. Evidence: dependency graph test.
  - [ ] A5.13.b Distinguish current, expiring, expired, revoked, disputed,
        unknown, and legally held states with effective times. Evidence: state
        matrix.
  - [ ] A5.13.c Show affected revisions, release status, downstream reach,
        remediation owner, deadline, and unavailable dependencies without
        leaking protected data. Evidence: impact-view tests.
  - [ ] A5.13.d Launch authorized quarantine/recall workflows only after an
        exact impact preview and confirm closure by consumer read-back.
        Evidence: revocation journey.
- [ ] A5.14 Build accessible non-3D alternatives for preview and QA. Evidence:
      screen-reader/keyboard review journey.
  - [ ] A5.14.a Define semantic representations for time, skeleton hierarchy,
        pose/channel values, contacts, annotations, errors, and comparison
        differences. Evidence: parity specification.
  - [ ] A5.14.b Implement keyboard-operable timeline/range/joint selection and
        synchronized structured tables. Evidence: keyboard suite.
  - [ ] A5.14.c Announce transport, selection, validation, loading, stale, and
        failure state changes without excessive verbosity. Evidence: screen-
        reader tests.
  - [ ] A5.14.d Prove every QA decision possible in 3D has a non-visual path to
        the same raw evidence and action. Evidence: parity journey.
- [ ] A5.15 Build UI-governed motion, action, skeleton, rig, capture, quality,
      consent, format, and catalog taxonomies over code-owned floors. Evidence:
      versioned review, alias, deprecation, and migration journey.
  - [ ] A5.15.a Define separate schemas, code-owned invariants, ownership, and
        allowed relationships for each taxonomy. Evidence: taxonomy registry.
  - [ ] A5.15.b Create draft terms with canonical IDs, labels/locales,
        definitions, aliases, parent/mapping relations, and usage constraints.
        Evidence: authoring tests.
  - [ ] A5.15.c Review, approve, publish, deprecate, supersede, and retire
        versioned terms without reinterpreting historical records. Evidence:
        lifecycle journey.
  - [ ] A5.15.d Reject collisions, cycles, invalid mappings, floor violations,
        and destructive deletion of referenced terms. Evidence: validation
        suite.
  - [ ] A5.15.e Migrate references through previewed, authorized, resumable jobs
        with per-record results and rollback/compensation. Evidence: migration
        journey.
- [ ] A5.16 Build taxonomy change impact over pipeline templates, search,
      assets, gates, exports, adapters, and collections. Evidence: read-only
      preview precedes an authorized migration job.
  - [ ] A5.16.a Build a complete reference index from taxonomy terms/versions to
        templates, assets, search documents, gates, exports, adapters, and
        collections. Evidence: zero-orphan fixture.
  - [ ] A5.16.b Preview affected objects, incompatible mappings, search changes,
        gate/profile changes, release risk, and migration cost read-only.
        Evidence: impact report.
  - [ ] A5.16.c Require authorized approval of the exact taxonomy and impact
        revisions before queuing migration. Evidence: stale-preview test.
  - [ ] A5.16.d Execute, checkpoint, resume, partially fail, compensate, and
        read back migrations without hidden direct writes. Evidence: job tests.
- [ ] A5.17 Build avatar customization and governed marketplace intake for body/
      proportions, skeleton, blend shapes, materials, clothing, rig/format
      compatibility, rights, license, version, security scan, preview, project
      use, dependency impact, update, quarantine, and takedown. Evidence:
      marketplace assets cannot enter capture/retarget/export without current
      compatibility and rights evidence.
  - [ ] A5.17.a Define separately governed customization parameters, bounds,
        dependencies, compatibility rules, and immutable output revisions for
        body, skeleton, face, materials, and clothing. Evidence: customization
        schema.
  - [ ] A5.17.b Preview customization across representative poses/motions and
        validate deformation, collision, rig, blend-shape, material, clothing,
        and target-format compatibility. Evidence: preview fixtures.
  - [ ] A5.17.c Ingest marketplace submissions with publisher identity, source,
        files/checksums, rights/license, versions, dependencies, supported
        rigs/formats, and declared limitations. Evidence: intake contract.
  - [ ] A5.17.d Scan archives, code/plugins, geometry, textures, materials,
        metadata, dependencies, and licenses in quarantine. Evidence: security
        and policy suite.
  - [ ] A5.17.e Review and promote an exact marketplace version with current
        compatibility, rights, security, quality, and intended-use evidence.
        Evidence: promotion journey.
  - [ ] A5.17.f Lock eligible versions on project use and recompute evidence at
        capture, retarget, package, and export boundaries. Evidence: stale-
        eligibility tests.
  - [ ] A5.17.g Preview dependency impact and propagate update, quarantine,
        revocation, takedown, and recall to every project/package/consumer.
        Evidence: marketplace lifecycle drill.

### A6 — Persistence, APIs, queue, workers, and security

- [ ] A6.1 Decide authoritative stores for capture, jobs, runs, artifacts, rigs,
      mappings, annotations, validation, consent, and packages. Evidence:
      ownership ADR.
  - [x] A6.1.a Inventory every current table, blob, index, cache, event, file,
        and in-memory record for each named entity. Evidence: storage map.
        <!-- aja-storage-inventory.json: storage map of Aja. Of the 7 storage kinds,
                                                                                                                                                                                                                                                                        4 PRESENT (cache, inMemory, file, blob) and 3 ABSENT (table, index, event).
                                                                                                                                                                                                                                                                        KEY FINDING: Aja has 0 relational tables — it is a motion-capture COMPUTE
                                                                                                                                                                                                                                                                        domain with NO relational persistence, unlike Isis(24)/Veritas(67)/Yemaya(38)
                                                                                                                                                                                                                                                                        which are relational service-direct. Aja's storage is the 6-module
                                                                                                                                                                                                                                                                        pipeline-cache subsystem (cache-invalidation/cache-utils/lifting-cache/
                                                                                                                                                                                                                                                                        pipeline-cache-manager/pose-estimation-cache/retargeting-cache) backed by
                                                                                                                                                                                                                                                                        in-memory Map/LRU stores, plus motion file I/O (fbx/bvh/npz). No search index,
                                                                                                                                                                                                                                                                        no EventEmitter (synchronous pipeline). This COMPLETES the cross-domain storage
                                                                                                                                                                                                                                                                        architecture comparison across all V1 domains: relational service-direct
                                                                                                                                                                                                                                                                        (Isis/Veritas/Yemaya) / desktop-file Tauri (Euterpe) / compute-cache (Aja).
                                                                                                                                                                                                                                                                        Precise scoped patterns (the broad "event" keyword over-counted 110→0 real
                                                                                                                                                                                                                                                                        EventEmitters). generate-aja-storage-inventory.mjs + 5 tests, wired into
                                                                                                                                                                                                                                                                        inventory/verify + CI. A6.1.b (one authoritative store/owner per entity) is
                                                                                                                                                                                                                                                                        design and stays unchecked. -->
  - [ ] A6.1.b Assign one authoritative store and owner per entity/property,
        plus projections, retention, consistency, and mutation boundaries.
        Evidence: ownership matrix.
  - [ ] A6.1.c Resolve duplicate or conflicting authorities and define migration
        and compatibility windows. Evidence: approved ADR.
  - [ ] A6.1.d Trace create, update, read, delete/retain, audit, recovery, and
        downstream propagation for each authority. Evidence: lifecycle review.
- [ ] A6.2 Add missing explicit-column schemas and safe migrations. Evidence:
      production-size rehearsal.
  - [ ] A6.2.a Specify normalized tables, explicit columns/types, keys,
        constraints, indexes, tenancy, audit fields, and state invariants.
        Evidence: schema review.
  - [ ] A6.2.b Write additive expand migrations and backfills that are bounded,
        resumable, observable, and safe under concurrent traffic. Evidence:
        migration tests.
  - [ ] A6.2.c Dual-read/write or translate during the compatibility window and
        verify old/new representation parity. Evidence: compatibility suite.
  - [ ] A6.2.d Rehearse production-size backfill, rollback/roll-forward, lock
        duration, failure recovery, and contract cleanup. Evidence: rehearsal
        report.
- [ ] A6.3 Implement immutable blob refs, checksums, encryption, retention,
      legal hold, and derived privacy variants. Evidence: byte rehash.
  - [ ] A6.3.a Define immutable content-addressed refs with tenant/project,
        media type, size, checksum algorithm/value, encryption key version,
        source lineage, and lifecycle class. Evidence: blob contract.
  - [ ] A6.3.b Upload through staging, validate size/type/checksum/malware, and
        atomically promote without permitting ref overwrite. Evidence: ingest
        tests.
  - [ ] A6.3.c Encrypt in transit/at rest, authorize object and variant access,
        rotate keys, and prevent cross-tenant ref inference. Evidence: security
        suite.
  - [ ] A6.3.d Apply retention, expiry, deletion, legal hold, consent freeze,
        and tombstone behavior to originals and derivatives. Evidence: lifecycle
        tests.
  - [ ] A6.3.e Generate privacy-minimized preview/analysis variants with their
        own checksums and source/policy lineage. Evidence: variant tests.
  - [ ] A6.3.f Stream/read back and independently rehash stored bytes before
        release use. Evidence: corruption fixture.
- [ ] A6.4 Implement capture/job/stage/artifact/rig/annotation/consent APIs with
      object/property authorization. Evidence: contract suite.
  - [ ] A6.4.a Define resource, list/filter, create, mutation, transition,
        revision, error, pagination, and idempotency contracts per API.
        Evidence: OpenAPI review.
  - [ ] A6.4.b Enforce identity, tenant/project scope, role/capability, object,
        property, consent compartment, and current revision on every operation.
        Evidence: authorization matrix.
  - [ ] A6.4.c Implement optimistic concurrency, idempotency, transition
        invariants, immutable lineage, and transactional outbox/audit behavior.
        Evidence: race tests.
  - [ ] A6.4.d Filter lists, errors, expansions, fields, counts, and related
        resources so unauthorized existence cannot leak. Evidence: negative
        contract suite.
- [ ] A6.5 Implement durable queue, distributed worker leases, heartbeats,
      checkpoint, retry, dead-letter, and cancel. Evidence: fault injection.
  - [ ] A6.5.a Persist queue item identity, stage/run revision, requirements,
        priority, availability, attempt, deadline, idempotency, and state.
        Evidence: queue schema.
  - [ ] A6.5.b Claim work with capability-aware atomic leases, fencing tokens,
        expiry, and duplicate-delivery tolerance. Evidence: contention tests.
  - [ ] A6.5.c Heartbeat and checkpoint resumable progress without allowing a
        stale worker to commit after lease loss. Evidence: fencing test.
  - [ ] A6.5.d Classify retryable/permanent/cancelled failures, apply bounded
        backoff/jitter/budgets, and retain attempt lineage. Evidence: retry
        tests.
  - [ ] A6.5.e Dead-letter exhausted work with diagnostics and authorized replay
        from an exact revision/configuration. Evidence: replay journey.
  - [ ] A6.5.f Propagate cancel cooperatively, terminate within policy, clean
        partial outputs, and record late results without publishing them.
        Evidence: cancellation fault injection.
- [ ] A6.6 Implement worker capability, version, model, GPU, load, and health
      registration. Evidence: incompatible work never dispatches.
  - [ ] A6.6.a Define signed worker identity and heartbeat schema for runtime,
        adapter, tool/model versions, CPU/GPU/memory/storage, capabilities,
        locality, load, and health. Evidence: registration contract.
  - [ ] A6.6.b Authenticate and authorize registration, rotation, drain,
        quarantine, and removal without trusting self-asserted privilege.
        Evidence: spoof tests.
  - [ ] A6.6.c Match exact stage requirements, compatibility ranges, resources,
        data locality, consent constraints, and health before lease grant.
        Evidence: scheduler fixtures.
  - [ ] A6.6.d Expire stale workers and reconcile leased work without double
        commit. Evidence: heartbeat-loss test.
- [ ] A6.7 Implement queue fairness, priority, capacity, and backpressure.
      Evidence: load tests.
  - [ ] A6.7.a Define tenant/project/class quotas, weights, priorities, aging,
        reservations, concurrency, and starvation limits. Evidence: scheduling
        policy.
  - [ ] A6.7.b Reserve worker/GPU/memory/storage/license capacity atomically
        before dispatch and release it on every terminal path. Evidence:
        reservation tests.
  - [ ] A6.7.c Enforce ingest, queue-depth, cost, deadline, and downstream
        pressure limits with explicit reject/defer states. Evidence:
        backpressure fixtures.
  - [ ] A6.7.d Prove urgent work progresses without starving ordinary tenants
        and noisy tenants cannot monopolize capacity. Evidence: load report.
- [ ] A6.8 Implement pipeline-cache read/write/invalidate/quarantine. Evidence:
      corrupt cache test.
  - [ ] A6.8.a Define complete cache keys from input bytes/revisions, stage/
        adapter/model/tool/config versions, profile, environment, and relevant
        policy/consent state. Evidence: key tests.
  - [ ] A6.8.b Write immutable entries only after successful validation with
        checksums, output manifest, creation lineage, TTL, and eligibility.
        Evidence: write tests.
  - [ ] A6.8.c Read only compatible, current, authorized, checksum-valid entries
        and record cache-hit provenance on the run. Evidence: read tests.
  - [ ] A6.8.d Invalidate by dependency/version/policy/consent/recall impact
        without deleting evidence needed for historical reconstruction.
        Evidence: invalidation graph test.
  - [ ] A6.8.e Quarantine corruption or suspicious entries, prevent further
        hits, and recompute safely. Evidence: poison/corruption test.
- [ ] A6.9 Implement validation and gate APIs with stale-policy semantics.
      Evidence: old pass cannot release.
  - [ ] A6.9.a Expose immutable raw measurements, evaluator provenance, verdict
        profile/version, freshness, non-pass state, and exact input revision.
        Evidence: response contracts.
  - [ ] A6.9.b Authorize measurement access, validation requests, manual review,
        waiver, and profile administration separately. Evidence: permission
        tests.
  - [ ] A6.9.c Detect stale inputs, algorithms/models, calibration, thresholds,
        profile, consent, rights, or target and return typed reasons. Evidence:
        staleness matrix.
  - [ ] A6.9.d Require recomputation/review under the current release profile;
        never translate an old pass into current pass. Evidence: release race
        test.
- [ ] A6.10 Implement package/export APIs with idempotency and current consent
      lock. Evidence: revocation race test.
  - [ ] A6.10.a Define package/export request, preview, eligibility, target,
        mapping, watermark, dependency lock, idempotency, and status contracts.
        Evidence: OpenAPI fixtures.
  - [ ] A6.10.b Preview exact included/skipped assets, versions, gates, consent/
        rights, losses, cost, and target limitations without mutation. Evidence:
        preflight tests.
  - [ ] A6.10.c Acquire fenced locks on exact revisions and current consent/
        rights/gate state immediately before producing output. Evidence:
        concurrency tests.
  - [ ] A6.10.d Produce one immutable package/result per idempotency key and
        reject mismatched retries. Evidence: duplicate-request test.
  - [ ] A6.10.e Verify target read-back, sign manifest/provenance, publish
        atomically, and release locks on every outcome. Evidence: export
        journey.
  - [ ] A6.10.f Abort or recall when consent changes during assembly/publication
        and prove no eligible-looking partial output remains. Evidence:
        revocation race test.
- [ ] A6.11 Implement canonical embodied-instruction adapter projections.
      Evidence: capability, demonstration, overlay, and handoff contract tests.
  - [ ] A6.11.a Implement the canonical health/capability projection with
        supported motion/profile/input/output/version/latency/limit detail.
        Evidence: capability contract.
  - [ ] A6.11.b Implement demonstration lookup/request projection with current
        consent, rights, quality, compatibility, and disclosure eligibility.
        Evidence: demonstration tests.
  - [ ] A6.11.c Implement coaching-overlay projection with bounded semantic
        measures/annotations and no protected raw media or unsupported claim.
        Evidence: overlay contract.
  - [ ] A6.11.d Implement asynchronous handoff/status/result/cancel projection
        with stable correlation, idempotency, degraded health, and provenance.
        Evidence: handoff journey.
  - [ ] A6.11.e Map internal evolution behind versioned adapter contracts and
        fail closed on unsupported negotiation. Evidence: compatibility tests.
- [ ] A6.12 Implement permission-aware search and analytics. Evidence:
      biometric/protected metadata exclusion.
  - [ ] A6.12.a Define approved searchable/aggregatable fields and prohibited
        raw media, biometric, subject identity, consent detail, and protected
        metadata per role/purpose. Evidence: field policy.
  - [ ] A6.12.b Build tenant/project/consent-compartment filtered projections
        from authoritative events with deletion/recall reconciliation. Evidence:
        index contract.
  - [ ] A6.12.c Enforce authorization before query, filtering, facets, counts,
        suggestions, snippets, exports, and analytics dimensions. Evidence:
        inference tests.
  - [ ] A6.12.d Apply minimum cohorts, suppression/noise where required, bounded
        cardinality, retention, and audit to analytics. Evidence: privacy suite.
- [ ] A6.13 Implement two-tenant/project/consent-compartment isolation across
      API, blob, search, jobs, cache, preview, and export. Evidence: integration
      suite.
  - [ ] A6.13.a Create fixtures with colliding IDs, names, hashes, tags, models,
        and resources across two tenants/projects/consent compartments.
        Evidence: isolation corpus.
  - [ ] A6.13.b Prove CRUD, list, filter, expansion, error, count, and audit
        isolation in every API. Evidence: API suite.
  - [ ] A6.13.c Prove blob/ref, signed URL, preview, search/index, cache
        key/hit, queue/worker, log/trace, and webhook isolation. Evidence:
        service suite.
  - [ ] A6.13.d Prove package/export, recall, backup/restore, and administrative
        operations cannot cross boundaries without explicit governed authority.
        Evidence: adversarial integration test.
- [ ] A6.14 Generate OpenAPI and worker/adapter consumer contracts. Evidence:
      drift gate.
  - [ ] A6.14.a Generate OpenAPI from the authoritative API definitions with
        stable operation IDs, schemas, errors, auth, pagination, idempotency,
        and examples. Evidence: spec validation.
  - [ ] A6.14.b Generate worker protocol and canonical adapter contracts with
        versions, capabilities, events, commands, and compatibility rules.
        Evidence: protocol artifacts.
  - [ ] A6.14.c Generate client/server fixtures or SDK types and compile
        representative producers/consumers. Evidence: contract build.
  - [ ] A6.14.d Diff checked-in/generated artifacts and block breaking or
        undocumented drift. Evidence: CI gate.
- [ ] A6.15 Implement webhook subscription, signed delivery, filtering, retries,
      replay protection, dead-letter, disable, and audit for job, stage, output,
      consent, quality, package, and recall events. Evidence: external
      subscriber conformance and secret-rotation tests.
  - [ ] A6.15.a Create authorized subscriptions with tenant/project scope,
        event/filter selection, HTTPS endpoint policy, secret ownership, rate
        limits, and verification handshake. Evidence: subscription tests.
  - [ ] A6.15.b Emit versioned minimal event envelopes from the transactional
        outbox with stable event/resource IDs, revisions, time, trace, and no
        protected payload. Evidence: event contract.
  - [ ] A6.15.c Sign timestamped deliveries and verify SSRF protections, DNS/IP
        policy, TLS, body checksum, secret version, and bounded response.
        Evidence: security suite.
  - [ ] A6.15.d Apply ordered/unordered semantics explicitly, retry with bounded
        backoff, prevent replay, and dead-letter exhausted delivery. Evidence:
        fault tests.
  - [ ] A6.15.e Support secret overlap/rotation, pause/disable, replay from an
        authorized range, and immutable delivery audit. Evidence: lifecycle
        journey.
  - [ ] A6.15.f Prove an external subscriber handles job, stage, output,
        consent, quality, package, and recall events compatibly. Evidence:
        conformance suite.
- [ ] A6.16 Complete TypeScript/Python SDK and CLI parity for capability/format
      discovery, ingest/live-session handoff, job operations, batch, preview
      refs, annotations, rigs, validation, export, webhooks, and audit-safe
      inspection. Evidence: generated public-contract matrix and end-to-end
      external-client tests.
  - [ ] A6.16.a Generate or implement shared authentication, endpoint,
        pagination, retries, timeouts, idempotency, errors, version negotiation,
        and observability consistently in both SDKs and CLI. Evidence: core
        parity tests.
  - [ ] A6.16.b Implement capability/format/profile discovery and ingest/live-
        session handoff in TypeScript, Python, and CLI. Evidence: discovery/
        intake client tests.
  - [ ] A6.16.c Implement job/stage status, batch, retry/cancel, wait/stream,
        preview refs, and result retrieval in all clients. Evidence: job parity
        tests.
  - [ ] A6.16.d Implement annotation, rig/mapping, validation/review, export/
        package, webhook, and audit-safe inspection operations in all clients.
        Evidence: operation matrix.
  - [ ] A6.16.e Keep protected content out of default logs/output, expose
        machine-readable CLI modes, and handle partial/degraded states.
        Evidence: privacy and UX tests.
  - [ ] A6.16.f Run end-to-end external TypeScript, Python, and CLI journeys
        against the same server contract and compare observable behavior.
        Evidence: parity report.
- [ ] A6.17 Implement video chunking, multi-GPU/stage pipeline parallelism,
      model compilation/optimization, distributed result aggregation,
      conflict/quality selection, and final assembly with deterministic
      boundaries or explicit nondeterminism. Evidence: chunk seam, ordering,
      duplicate, worker loss, heterogeneous model/version, and aggregation
      tests.
  - [ ] A6.17.a Plan chunks from immutable media/time base with overlap,
        keyframe/contact/shot boundaries, context, order, checksums, and
        deterministic plan identity. Evidence: chunk-plan tests.
  - [ ] A6.17.b Register compiled/optimized model artifacts by source model,
        compiler/runtime/hardware/profile versions, checksum, validation, and
        compatibility. Evidence: model registry.
  - [ ] A6.17.c Reserve compatible multi-GPU or stage-pipeline resources and
        dispatch chunks/stages with fenced leases, bounded concurrency, and
        backpressure. Evidence: scheduler tests.
  - [ ] A6.17.d Persist idempotent chunk outputs with input/worker/model/tool/
        seed provenance, overlap context, raw quality, and attempt lineage.
        Evidence: duplicate/worker-loss tests.
  - [ ] A6.17.e Aggregate in canonical order, reconcile overlap/conflicts with
        versioned selection policy, and mark heterogeneous/incomparable results.
        Evidence: aggregation goldens.
  - [ ] A6.17.f Assemble final timing, tracks, metadata, and provenance;
        validate seams/completeness/quality and disclose any nondeterminism.
        Evidence: final-assembly tests.
  - [ ] A6.17.g Recover from chunk/stage/worker/GPU loss without gaps,
        duplicates, stale commits, or hidden partial success. Evidence:
        distributed fault suite.

### A7 — Console, batch operations, export, and scheduling

- [ ] A7.1 Build operator hub around intake, queue, stage failures, QA, consent,
      worker health, cost, and export readiness. Evidence: utility-first design
      review.
  - [ ] A7.1.a Define operator questions, critical states, next actions, role
        visibility, and drill-down routes before layout. Evidence: task map.
  - [ ] A7.1.b Project live intake, queue, failed-stage, QA, consent, worker,
        cost, and export-readiness summaries from authoritative APIs. Evidence:
        data contract tests.
  - [ ] A7.1.c Compose a restrained task-led workspace with one clear queue/
        incident focus and no decorative hero or undifferentiated card mosaic.
        Evidence: frontend review.
  - [ ] A7.1.d Preserve filters/time window/tenant/project context through
        drill-down and back navigation. Evidence: navigation journey.
  - [ ] A7.1.e Render loading, empty, stale, partial, unavailable, and failed
        summaries honestly. Evidence: state matrix.
- [ ] A7.2 Build intake, job, pipeline, QA, annotation, library, rigs, consent,
      workers, batch, schedule, and export routes. Evidence: route integrity.
  - [ ] A7.2.a Define route IDs, hierarchy, parameters, loaders, permissions,
        breadcrumbs, titles, and canonical deep links. Evidence: route registry.
  - [ ] A7.2.b Implement intake/job/pipeline/QA/annotation routes with exact
        object and revision context. Evidence: route tests.
  - [ ] A7.2.c Implement library/rig/consent routes with protected-data and
        eligibility boundaries. Evidence: authorization tests.
  - [ ] A7.2.d Implement worker/batch/schedule/export routes with operation and
        step-up controls. Evidence: workflow tests.
  - [ ] A7.2.e Verify direct load, refresh, forward/back, not-found, forbidden,
        signed-link, and permission-change behavior for every route. Evidence:
        Playwright route suite.
- [ ] A7.3 Build live per-job DAG with exact stage state, attempts, metrics,
      cache, resource, cost, logs, and output. Evidence: live fault journey.
  - [ ] A7.3.a Load immutable job/run definition and subscribe to ordered,
        resumable state updates with snapshot reconciliation. Evidence:
        live-data tests.
  - [ ] A7.3.b Render stage/dependency state, attempt, worker/resource, cache,
        progress, cost, timing, and output without inventing missing links.
        Evidence: DAG fixture.
  - [ ] A7.3.c Inspect a selected stage's exact configuration, inputs, logs,
        measurements, attempts, outputs, failure, and lineage. Evidence:
        inspector journey.
  - [ ] A7.3.d Provide an equivalent ordered/dependency semantic table and
        keyboard selection instead of requiring graph interaction. Evidence:
        accessibility parity.
  - [ ] A7.3.e Reconcile disconnect, duplicate/out-of-order events, retry,
        cancellation, worker loss, and late output. Evidence: live fault
        journey.
- [ ] A7.4 Build stage configuration and rerun-from-stage with impact preview.
      Evidence: downstream invalidation.
  - [ ] A7.4.a Load the versioned stage schema, defaults, current configuration,
        allowed overrides, and permissions. Evidence: form-schema tests.
  - [ ] A7.4.b Validate model/tool/profile/adapter/resource compatibility and
        show changed versus inherited values before save. Evidence: validation
        tests.
  - [ ] A7.4.c Preview rerun start point, reusable upstream outputs, invalidated
        downstream stages/cache/reviews/packages, cost, capacity, and consent
        impact. Evidence: impact fixture.
  - [ ] A7.4.d Require explicit authorization of the exact job/run/config/
        impact revisions and enqueue idempotently. Evidence: stale-preview and
        duplicate tests.
  - [ ] A7.4.e Track the new run separately and preserve comparison/lineage to
        every prior attempt. Evidence: rerun journey.
- [ ] A7.5 Build queue and worker console with drain, retry, quarantine, pause,
      backfill, and kill controls. Evidence: step-up authorization.
  - [ ] A7.5.a Project queue depth/age/class/tenant/priority/backpressure and
        worker identity/capability/version/resource/load/health/lease state.
        Evidence: console contract.
  - [ ] A7.5.b Inspect exact queued/running work, requirements, attempts,
        reservations, checkpoints, and dispatch eligibility. Evidence:
        inspection journey.
  - [ ] A7.5.c Preview scope and impact for drain, retry, quarantine, pause,
        resume, reprioritize, backfill, and kill operations. Evidence: impact
        tests.
  - [ ] A7.5.d Enforce operation-specific step-up, reason, concurrency,
        idempotency, audit, and protected-tenant boundaries. Evidence:
        authorization suite.
  - [ ] A7.5.e Display partial execution, late results, compensation, and
        read-back rather than optimistic success. Evidence: fault journey.
- [ ] A7.6 Build batch preview with eligible/skipped items, cost, capacity,
      gates, consent, and schedule. Evidence: mixed-result journey.
  - [ ] A7.6.a Resolve a stable item set/query snapshot, requested operation,
        configuration, target, and caller authority. Evidence: batch request
        contract.
  - [ ] A7.6.b Evaluate per-item compatibility, gate freshness, consent/rights,
        lifecycle, dependency, quota, and schedule eligibility. Evidence:
        eligibility fixtures.
  - [ ] A7.6.c Show included/skipped/blocked items with exact reasons plus cost,
        resources, capacity window, duration, and downstream impact. Evidence:
        preview tests.
  - [ ] A7.6.d Authorize the exact preview revision and execute idempotently
        with per-item progress, retry/cancel, partial result, and audit.
        Evidence: mixed-result journey.
- [ ] A7.7 Build committed GPU/worker/capture/reviewer windows in the calendar.
      Evidence: capacity conflict tests.
  - [ ] A7.7.a Model resource pools, skills/capabilities, locations, operating
        windows, maintenance, quotas, and time zones. Evidence: capacity schema.
  - [ ] A7.7.b Create tentative holds and committed reservations for GPU,
        worker, capture, and reviewer resources with exact demand and owner.
        Evidence: reservation tests.
  - [ ] A7.7.c Detect overlap, capability, quota, travel/setup, dependency, and
        downstream schedule conflicts transactionally. Evidence: conflict
        fixtures.
  - [ ] A7.7.d Reschedule, cancel, expire, or compensate reservations and
        propagate impact to queued/batch/live work. Evidence: lifecycle journey.
  - [ ] A7.7.e Provide list/agenda and keyboard alternatives to drag-based
        calendar manipulation. Evidence: accessibility tests.
- [ ] A7.8 Build export target selection, mapping, validation, package,
      watermark, provenance, and read-back verification. Evidence: golden target
      journeys.
  - [ ] A7.8.a Select an exact artifact revision, consumer/target/profile/
        version, mapping, package options, watermark, and delivery destination.
        Evidence: target-selection tests.
  - [ ] A7.8.b Explain compatibility, loss, current quality/consent/rights/
        release gates, dependencies, costs, and target availability. Evidence:
        preflight journey.
  - [ ] A7.8.c Preview package files/manifest, mappings, generated regions,
        watermark placement, provenance, and excluded items. Evidence: package
        preview.
  - [ ] A7.8.d Authorize and monitor immutable export assembly under revision/
        consent locks with idempotent retry and explicit partial/failure state.
        Evidence: operation tests.
  - [ ] A7.8.e Independently read back the target, compare expected contents,
        signatures, motion/metadata, and record verified delivery. Evidence:
        golden target journey.
- [ ] A7.9 Build supersession, revoke, unpublish, quarantine, and recall.
      Evidence: downstream library/consumer state.
  - [ ] A7.9.a Define distinct eligibility, visibility, execution, distribution,
        and remediation effects for each lifecycle action. Evidence: transition
        specification.
  - [ ] A7.9.b Compute the exact asset/revision, derivatives, packages,
        libraries, projects, consumers, cached copies, and active jobs affected.
        Evidence: dependency preview.
  - [ ] A7.9.c Require action-specific authority, step-up, reason, effective
        time, replacement where applicable, and exact impact revision. Evidence:
        authorization tests.
  - [ ] A7.9.d Execute the propagation saga with checkpoints, per-consumer
        results, retry/compensation, and no hidden partial completion. Evidence:
        recall drill.
  - [ ] A7.9.e Verify downstream state by read-back and keep unresolved
        recipients visible until closure. Evidence: reconciliation report.
- [ ] A7.10 Build signed deep links from Metis/Studio consumers to authorized
      Aja job/asset context. Evidence: expiry, tenant, permission, and replay.
  - [ ] A7.10.a Define allowed destination routes/context fields and omit
        protected source, subject, biometric, secret, or authority-bearing data.
        Evidence: deep-link contract.
  - [ ] A7.10.b Mint bounded audience/tenant/object/revision/purpose/expiry/
        nonce links with key version and return location. Evidence: signing
        tests.
  - [ ] A7.10.c Validate signature, audience, current authorization, tenant,
        expiry, nonce/replay, object existence/revision, and lifecycle on open.
        Evidence: negative suite.
  - [ ] A7.10.d Land on an honest forbidden/expired/missing/stale state without
        leaking object existence and preserve a safe return path. Evidence:
        Playwright journey.
- [ ] A7.11 Build responsive tablet/mobile incident, review, consent, and
      approve mode. Evidence: no full 3D edit requirement.
  - [ ] A7.11.a Prioritize incident triage, assignment, evidence review,
        annotation, consent decision, approval/rejection, and emergency control
        for compact viewports. Evidence: mobile task map.
  - [ ] A7.11.b Provide semantic thumbnails, measurements, tables, source-safe
        media, and deep links so no full 3D editor is required. Evidence:
        compact review journey.
  - [ ] A7.11.c Implement touch/keyboard targets, reflow, zoom, orientation,
        reduced motion, safe-area, offline/reconnect, and interruption
        resilience. Evidence: responsive accessibility suite.
  - [ ] A7.11.d Enforce step-up, exact revision, reason, confirmation, and
        read-back for consent, approve/reject, terminate, or recall actions.
        Evidence: mobile authorization tests.
- [ ] A7.12 Build semantic alternatives and non-drag controls for DAG, timeline,
      mapping, preview, and graphs. Evidence: WCAG 2.2 journeys.
  - [ ] A7.12.a Inventory every visual datum, relationship, selection, and
        operation in DAG, timeline, mapping, preview, and graph surfaces.
        Evidence: parity inventory.
  - [ ] A7.12.b Provide structured tables/lists/trees with labels, ordering,
        dependencies, exact values, units, statuses, and diagnostics. Evidence:
        semantic snapshots.
  - [ ] A7.12.c Provide keyboard/button/menu inputs for pan/zoom/select,
        connect/map, range edit, reorder, resize, and other drag interactions.
        Evidence: non-drag tests.
  - [ ] A7.12.d Synchronize focus, selection, filters, and actions between
        visual and semantic views. Evidence: parity journey.
  - [ ] A7.12.e Test screen-reader announcement, focus recovery, zoom/reflow,
        color independence, reduced motion, and error recovery. Evidence: WCAG
        suite.
- [ ] A7.13 Build honest fixture, missing-GPU/model/rig/host, offline, stale,
      partial, degraded, and failed states. Evidence: state matrix.
  - [ ] A7.13.a Define each state, source signal, reason code, precedence,
        permitted action, retryability, owner, and release effect. Evidence:
        state contract.
  - [ ] A7.13.b Render fixture/demo data with persistent labeling and prohibit
        production-affecting actions. Evidence: fixture tests.
  - [ ] A7.13.c Render each missing dependency and offline/degraded condition
        with last-known time, affected scope, and safe next step. Evidence:
        outage matrix.
  - [ ] A7.13.d Distinguish stale and partial data from empty/success and expose
        mixed per-item/per-stage outcomes. Evidence: partial-state tests.
  - [ ] A7.13.e Preserve diagnostics and retry/recovery state for failures
        without showing optimistic completion. Evidence: fault journey.
- [ ] A7.14 Register permission-filtered Studio/control-room navigation and
      command palette. Evidence: route/role parity.
  - [ ] A7.14.a Register canonical route and command IDs, labels, keywords,
        hierarchy, required capabilities, target resolver, and feature
        availability. Evidence: navigation registry.
  - [ ] A7.14.b Filter server- and client-side navigation, search, recent,
        shortcuts, and command results by current authorization and context.
        Evidence: role tests.
  - [ ] A7.14.c Reauthorize and validate the resolved object/revision at route
        load or command execution rather than trusting visibility. Evidence:
        stale-permission test.
  - [ ] A7.14.d Prove every authorized route/action is reachable and every
        unauthorized route/action is absent and non-executable. Evidence:
        route/role parity matrix.
- [ ] A7.15 Build searchable job/result operations with filters, saved views,
      queue statistics, bulk retry/cancel/reprioritize, webhook status, and
      export diagnostics. Evidence: bulk impact preview, partial-result,
      authorization, and undo/compensation behavior.
  - [ ] A7.15.a Define permission-aware job/result fields, full-text terms,
        filters/facets, sort, pagination, and bounded queue statistics.
        Evidence: query contract.
  - [ ] A7.15.b Build shareable saved views with owner/scope, versioned query,
        columns, defaults, permissions, and safe deletion. Evidence: saved-view
        tests.
  - [ ] A7.15.c Expose webhook delivery and export diagnostic projections linked
        to exact job/result revisions without leaking secrets or protected
        payloads. Evidence: diagnostic tests.
  - [ ] A7.15.d Resolve bulk selection as a stable snapshot and preview per-item
        retry/cancel/reprioritize eligibility and downstream impact. Evidence:
        bulk preview.
  - [ ] A7.15.e Execute authorized operations idempotently with per-item
        progress/result, partial failures, retry, and compensation/undo where
        valid. Evidence: mixed-result journey.
- [ ] A7.16 Build live-capture supervision for device/session health, subject/
      consent, calibration, preview, buffer/upload, tracking modalities, quality
      warnings, handoff, and emergency stop. Evidence: desktop and mobile
      session fault journeys.
  - [ ] A7.16.a Load live device/session identity, operator, location, subject-
        safe consent state, selected protocol/modalities, and immutable
        configuration. Evidence: supervision contract.
  - [ ] A7.16.b Monitor device/stream/clock/network/buffer/upload/worker health
        and calibration validity with bounded live updates. Evidence: telemetry
        tests.
  - [ ] A7.16.c Show privacy-governed preview, tracking modality/confidence,
        dropped data, quality warnings, and unavailable states. Evidence: live
        preview fixtures.
  - [ ] A7.16.d Gate record/start/resume/handoff on current subject consent,
        calibration, storage/capacity, protocol, and operator authority.
        Evidence: precondition tests.
  - [ ] A7.16.e Implement step-up emergency stop that halts capture/upload/
        processing, preserves admissible evidence, and reports late data.
        Evidence: stop drill.
  - [ ] A7.16.f Recover from device/network/app interruption and reconcile
        buffers, uploads, stages, and handoff without duplicate or hidden gaps.
        Evidence: desktop/mobile fault journeys.
- [ ] A7.17 Build profile-specific review projections for all ratified vertical
      pipelines, including intended-use warning, protocol, reference, measures,
      annotations, qualified reviewer, and consumer handoff. Evidence: role and
      profile boundaries prevent unsupported medical/performance claims.
  - [ ] A7.17.a Define a common projection contract while keeping profile-
        specific vocabulary, measures, thresholds, warnings, and decisions in
        their owned versions. Evidence: projection schema.
  - [ ] A7.17.b Render the exact profile/protocol/intended and prohibited use,
        input/reference provenance, measurements, uncertainty, annotations, and
        validation freshness. Evidence: profile fixtures.
  - [ ] A7.17.c Enforce profile-specific qualified-reviewer roles, conflicts,
        required evidence, decision authority, and escalation. Evidence: role
        matrix.
  - [ ] A7.17.d Produce only the approved consumer handoff fields, disclosures,
        limitations, and claim boundaries for the exact profile. Evidence:
        handoff contract tests.
  - [ ] A7.17.e Prove yoga, fitness, dance, martial arts, sports, medical/
        rehabilitation, film/VFX, and gaming projections cannot inherit one
        another's claims, review, or approval. Evidence: cross-profile negative
        suite.

### A8 — Aja observability, verification, and launch

- [ ] A8.1 Instrument capture, upload, queue, stage, cache, validation, review,
      export, adapter, consent, and recall traces/metrics. Evidence: bounded
      schema.
  - [ ] A8.1.a Define bounded event/span/metric names, attributes, units,
        cardinality limits, sampling, retention, and ownership per subsystem.
        Evidence: telemetry registry.
  - [ ] A8.1.b Propagate tenant-safe trace/run/job/stage/artifact correlation
        across capture, services, queues, workers, adapters, and consumers.
        Evidence: traced fixture.
  - [ ] A8.1.c Instrument success, latency, load, retries, backpressure, partial
        results, typed failures, validation/review, consent blocks, and recall
        closure. Evidence: signal tests.
  - [ ] A8.1.d Redact raw media, biometric/subject data, secrets, signed URLs,
        free text, and unbounded identifiers from telemetry. Evidence: privacy
        scan.
- [ ] A8.2 Define SLOs for ingest, queue age, stage runtime, worker health,
      validation, preview, export, adapter health, and consent enforcement.
      Evidence: error-budget policy.
  - [ ] A8.2.a Define user-visible service indicators, populations, exclusions,
        windows, data sources, and measurement quality for each named surface.
        Evidence: SLI catalog.
  - [ ] A8.2.b Ratify latency, availability, freshness, correctness, durability,
        and enforcement objectives by profile/tier where genuinely distinct.
        Evidence: approved targets.
  - [ ] A8.2.c Create dashboards and multi-window burn alerts with actionable
        ownership and protected-data-safe drill-down. Evidence: alert tests.
  - [ ] A8.2.d Define error-budget consumption, release freeze, exception,
        recovery, and retrospective policy. Evidence: policy exercise.
- [ ] A8.3 Run contract and migration compatibility suites. Evidence: input,
      rig, motion, consent, package, and adapter fixtures.
  - [ ] A8.3.a Test supported prior/current API, event, worker, adapter, file,
        and package contract versions against consumer fixtures. Evidence:
        compatibility matrix.
  - [ ] A8.3.b Test input, rig, motion, consent, validation, lineage, and
        package schema round-trips including unknown/optional fields. Evidence:
        contract goldens.
  - [ ] A8.3.c Test migration upgrade, backfill, mixed-version operation,
        rollback/roll-forward, and cleanup at representative scale. Evidence:
        migration report.
  - [ ] A8.3.d Block undocumented breaking drift and expired compatibility
        windows. Evidence: release gate.
- [ ] A8.4 Run known-value calibration, sync, skeleton, frame, contact, slide,
      jitter, anatomy, ground-truth, retarget, loop, and format tests. Evidence:
      gate registry coverage.
  - [ ] A8.4.a Register independent analytical or externally verified fixtures
        with source, generator, expected values/tolerances, rights, and
        checksum. Evidence: fixture registry.
  - [ ] A8.4.b Cover calibration/sync/skeleton/frame and contact/slide/jitter
        measures with positive, boundary, and broken cases. Evidence: known-
        value report.
  - [ ] A8.4.c Cover anatomy/physics, ground-truth fidelity, retarget, loop/
        blend, facial where consented, and format round-trip separately.
        Evidence: gate matrix.
  - [ ] A8.4.d Prove every production gate/profile/version maps to current
        known-value evidence and unavailable gates cannot pass. Evidence:
        registry audit.
- [ ] A8.5 Run calibrated perceptual/model gate evaluations. Evidence:
      threshold, subgroup, uncertainty, and drift report.
  - [ ] A8.5.a Freeze evaluator/model/config, consented evaluation corpus,
        labels/adjudication, splits, cohorts, and baselines. Evidence: eval
        manifest.
  - [ ] A8.5.b Measure task performance, calibration, uncertainty, abstention,
        subgroup error, out-of-distribution behavior, and human disagreement.
        Evidence: evaluation report.
  - [ ] A8.5.c Select pass/fail/review thresholds from declared costs and prove
        them on held-out data without tuning leakage. Evidence: threshold
        rationale.
  - [ ] A8.5.d Compare candidate to current model, replay affected historical
        cases, monitor drift, and document limitations/expiry. Evidence: model
        release gate.
- [ ] A8.6 Run real-database, blob, queue, worker, cache, search, preview, and
      export integration tests. Evidence: durable round-trip.
  - [ ] A8.6.a Stand up production-like database, object store, queue, workers,
        cache, search, and target adapters with isolated fixtures. Evidence:
        integration environment.
  - [ ] A8.6.b Exercise capture/ingest through persisted job/stages/artifacts,
        cache/index, preview, validation, package/export, and target read-back.
        Evidence: durable round-trip.
  - [ ] A8.6.c Exercise retries, duplicate delivery, concurrency, stale cache/
        index, restart, partial result, and cleanup. Evidence: fault cases.
  - [ ] A8.6.d Verify tenant/consent isolation, checksums, lineage, audit, and
        reconciliation at every storage boundary. Evidence: invariant report.
- [ ] A8.7 Run full-journey Playwright: ingest/consent through validated export.
      Evidence: deterministic fixture clip and read-back.
  - [ ] A8.7.a Create a deterministic rights-cleared clip, capture metadata,
        subject consent, rig/profile, known gate values, and target fixture.
        Evidence: fixture manifest.
  - [ ] A8.7.b Ingest, validate admissibility, configure/run stages, and inspect
        synchronized preview/lineage through the browser. Evidence: Playwright
        trace.
  - [ ] A8.7.c Review raw measurements, annotations, consent, and current gates;
        approve the exact eligible revision. Evidence: decision assertions.
  - [ ] A8.7.d Export/package and independently read back target bytes/metadata/
        motion/provenance, asserting UI and API final state. Evidence: journey
        report.
- [ ] A8.8 Run full-journey Playwright: failed stage, rerun, compare, annotate,
      approve, package. Evidence: exact state.
  - [ ] A8.8.a Inject a deterministic stage failure and assert job DAG, attempt,
        diagnostics, downstream block, and non-success announcement. Evidence:
        failure trace.
  - [ ] A8.8.b Change a permitted configuration, preview invalidation/cost, and
        rerun from the failed stage. Evidence: rerun assertions.
  - [ ] A8.8.c Compare exact old/new revisions, create durable frame/joint
        annotations, and resolve review. Evidence: comparison trace.
  - [ ] A8.8.d Approve/package only the new eligible revision and assert the
        failed/stale output cannot release. Evidence: exact-state report.
- [ ] A8.9 Run full-journey adapter tests for Metis demonstration, coaching
      overlay, and handoff. Evidence: health/degraded behavior.
  - [ ] A8.9.a Negotiate adapter version/capabilities and test healthy,
        degraded, unavailable, incompatible, and stale health projections.
        Evidence: health matrix.
  - [ ] A8.9.b Request and consume an eligible demonstration with consent,
        rights, quality, compatibility, provenance, and revocation assertions.
        Evidence: demonstration journey.
  - [ ] A8.9.c Request a coaching overlay and assert bounded semantic measures,
        uncertainty, claim limitations, and protected-data exclusion. Evidence:
        overlay journey.
  - [ ] A8.9.d Submit/status/cancel/complete an asynchronous handoff with
        idempotency, correlation, retry, late-result, and failure behavior.
        Evidence: handoff journey.
- [ ] A8.10 Run WCAG 2.2 AA, keyboard, announcement, non-drag, and semantic
      preview parity suites. Evidence: report matrix.
  - [ ] A8.10.a Run automated WCAG checks across authenticated routes, roles,
        themes, locales, and wide/compact/single-column viewports. Evidence:
        accessibility report.
  - [ ] A8.10.b Complete keyboard-only intake, job/DAG, compare, mapping,
        annotation, review, batch, schedule, and export journeys. Evidence:
        keyboard traces.
  - [ ] A8.10.c Verify names/roles/values, focus order/restoration, live
        announcements, errors, loading, stale/partial/failure, and step-up
        dialogs. Evidence: assistive-technology matrix.
  - [ ] A8.10.d Verify non-drag controls, 200–400% zoom/reflow, touch targets,
        color independence, reduced motion, and semantic 3D/graph parity.
        Evidence: manual/automated results.
- [ ] A8.11 Run upload, queue, batch, worker, preview, large motion, search, and
      export performance/load tests. Evidence: backpressure and budgets.
  - [ ] A8.11.a Define representative and worst-supported datasets, concurrency,
        resource limits, warm/cold state, budgets, and success/failure criteria.
        Evidence: load plan.
  - [ ] A8.11.b Measure upload/ingest, queue fairness/age, dispatch, stage/
        worker, cache, and batch behavior under sustained and burst load.
        Evidence: service report.
  - [ ] A8.11.c Measure first/useful preview, timeline/skeleton/large-motion
        interaction, search, and export/package/read-back performance. Evidence:
        user-performance report.
  - [ ] A8.11.d Verify quotas, backpressure, degradation, cancellation,
        recovery, data correctness, cost, and no tenant starvation. Evidence:
        overload report.
- [ ] A8.12 Run malicious media/archive/rig, consent bypass, biometric leak,
      model attack, worker spoof, cache poison, auth, tenant, and export tamper
      tests. Evidence: zero blockers.
  - [ ] A8.12.a Test malformed/oversized/polyglot media, archives, rigs, paths,
        metadata, parsers, decompression, and dependency resolution in
        quarantine. Evidence: content security suite.
  - [ ] A8.12.b Test consent/rights expiry, revocation races, purpose confusion,
        protected preview/search/log leaks, and biometric
        inference/exfiltration. Evidence: privacy suite.
  - [ ] A8.12.c Test adversarial model inputs/outputs, prompt/reference abuse,
        unsafe deserialization, model substitution, and generated-content
        disclosure bypass. Evidence: model security report.
  - [ ] A8.12.d Test worker identity/capability spoof, lease theft, stale
        commit, cache poison, artifact tamper, and supply-chain compromise.
        Evidence: compute security suite.
  - [ ] A8.12.e Test authentication, object/property authorization, tenant/
        project isolation, step-up, export/package/signature, and recall bypass.
        Evidence: adversarial matrix with zero release blockers.
- [ ] A8.13 Run worker loss, GPU loss, queue replay, cache loss, backup/restore,
      consent revocation, and recall drills. Evidence: RPO/RTO.
  - [ ] A8.13.a Inject worker process/host/network and GPU reset/exhaustion loss
        during queued, running, checkpointing, and committing states. Evidence:
        compute recovery report.
  - [ ] A8.13.b Replay, duplicate, reorder, pause, and partially lose queue/
        event delivery; verify fencing, idempotency, and reconciliation.
        Evidence: messaging drill.
  - [ ] A8.13.c Lose/corrupt cache entries and prove safe miss, quarantine,
        recomputation, and no invalid release. Evidence: cache drill.
  - [ ] A8.13.d Restore database/blobs/config/keys/indexes from backup and
        reconcile checksums, queues, jobs, search, audit, and targets. Evidence:
        restore report.
  - [ ] A8.13.e Revoke consent and recall during processing/export/distribution;
        verify bounded stop and downstream closure. Evidence: recall drill.
  - [ ] A8.13.f Measure and compare achieved RPO/RTO/data loss/duplicate work to
        ratified objectives. Evidence: recovery scorecard.
- [ ] A8.14 Publish capture, consent, pipeline, worker, QA, export, incident,
      privacy, and recovery runbooks. Evidence: game-day.
  - [ ] A8.14.a Assign owner, audience, prerequisites, triggers, authority,
        commands/UI paths, diagnostics, safety checks, escalation, and rollback
        to each runbook. Evidence: runbook template audit.
  - [ ] A8.14.b Write capture/consent and pipeline/worker operational runbooks
        with protected-data and emergency-stop handling. Evidence: reviewed
        docs.
  - [ ] A8.14.c Write QA/export and privacy/security incident runbooks with
        gate, quarantine, revocation, and recall procedures. Evidence: reviewed
        docs.
  - [ ] A8.14.d Write backup/restore/disaster recovery and dependency-outage
        runbooks with RPO/RTO validation. Evidence: reviewed docs.
  - [ ] A8.14.e Execute representative game-days, record deviations, remediate,
        and revalidate runbook commands/links. Evidence: exercise report.
- [ ] A8.15 Produce engineering-readiness evidence. Evidence: all requirements
      map to proof.
  - [ ] A8.15.a Build a bidirectional requirement-to-design/code/test/operation/
        owner/evidence index with immutable evidence refs. Evidence: zero-
        orphan audit.
  - [ ] A8.15.b Record contract/migration, quality/calibration,
        security/privacy, accessibility, performance/SLO, recovery, and runbook
        results with exact versions. Evidence: readiness packet.
  - [ ] A8.15.c Surface failed, stale, waived, unavailable, or missing evidence
        explicitly and block readiness on unresolved mandatory proof. Evidence:
        readiness gate.
  - [ ] A8.15.d Reproduce the packet from repository and approved external
        evidence and verify links/checksums. Evidence: clean-room generation.
- [ ] A8.16 Obtain named motion/animation, privacy/consent, safety,
      accessibility, security, engineering, and product approvals. Evidence:
      human signoff.
  - [ ] A8.16.a Define each approver's scope, competence, independence/conflict
        rules, required evidence, decision options, and expiry. Evidence:
        approval policy.
  - [ ] A8.16.b Present the exact release candidate, readiness packet,
        limitations, risks, waivers, and unresolved items. Evidence: review
        bundle.
  - [ ] A8.16.c Capture named approve/reject/conditional decisions with role,
        time, revision, rationale, conditions, and signature/audit. Evidence:
        signoff records.
  - [ ] A8.16.d Invalidate or renew approvals when candidate, evidence, policy,
        model/profile, consent posture, or material risk changes. Evidence:
        staleness test.
- [ ] A8.17 Run distinct full journeys for desktop live capture, mobile live
      capture, multi-person/body/hand/face tracking, video analysis, generative
      cleanup/blending, format conversion, SDK/CLI, and webhooks. Evidence: no
      generic batch-ingest journey substitutes for live or modality-specific
      proof.
  - [ ] A8.17.a Run separate desktop and mobile live-capture journeys through
        device enrollment, calibration, consent, interruption, handoff, and
        emergency stop. Evidence: live traces.
  - [ ] A8.17.b Run separate multi-person, body, hand, and consented face
        tracking journeys with identity, occlusion, confidence, modality loss,
        and protected-data assertions. Evidence: modality matrix.
  - [ ] A8.17.c Run video-analysis and generative cleanup/blending journeys with
        exact edited/generated ranges, alternatives, validation, and human
        acceptance. Evidence: provenance traces.
  - [ ] A8.17.d Run every supported format-conversion pair through loss preview,
        conversion, validation, package, and read-back. Evidence: conversion
        matrix.
  - [ ] A8.17.e Run external TypeScript SDK, Python SDK, CLI, and webhook
        journeys including auth, version negotiation, retry, partial failure,
        secret rotation, and recall. Evidence: client matrix.
- [ ] A8.18 Run independent contract, known-value, calibrated-quality,
      accessibility, privacy/consent, performance, and consumer-handoff gates
      for yoga, fitness, dance, martial arts, sports, medical/rehabilitation,
      film/VFX, and gaming profiles. Evidence: no vertical inherits another
      vertical's release evidence.
  - [ ] A8.18.a Register one release-evidence bundle per exact vertical profile/
        version with owners, intended/prohibited use, fixtures, consumers, and
        expiry. Evidence: profile registry.
  - [ ] A8.18.b Run profile-specific contract and independent known-value/
        calibrated-quality gates against its own protocol, measures, thresholds,
        and reviewer population. Evidence: quality matrix.
  - [ ] A8.18.c Run profile-specific accessibility, privacy/consent, safety/
        claim-boundary, performance, and consumer-handoff tests. Evidence:
        assurance matrix.
  - [ ] A8.18.d Cover yoga, fitness, dance, martial arts, each ratified sport,
        medical/rehabilitation, film/VFX, and gaming with declared unavailable
        cells rather than inherited proof. Evidence: coverage audit.
  - [ ] A8.18.e Block profile release when any required bundle is missing,
        stale, failed, or belongs to another profile/version. Evidence:
        independence test.
- [ ] A8.19 Run full journeys for CG replacement, text/reference-to-motion,
      avatar customization/marketplace, chunked/parallel inference, optimized
      model rollout, distributed aggregation, and final package assembly.
      Evidence: specialized lineage, consent, compatibility, failure, and
      quality assertions.
  - [ ] A8.19.a Run video-to-CG per-shot segmentation/tracking/transfer/render/
        composite/review/delivery with consent, generated-region, temporal
        quality, seam, and recall assertions. Evidence: CG journey.
  - [ ] A8.19.b Run text and protected-reference-to-motion through constraints,
        candidate generation, physics/quality validation, human acceptance,
        export, and disclosure. Evidence: generation journeys.
  - [ ] A8.19.c Run avatar customization and marketplace submit/scan/review/
        promote/use/update/quarantine/takedown with compatibility and rights
        propagation. Evidence: marketplace journey.
  - [ ] A8.19.d Run chunked multi-GPU/stage-parallel inference through overlap,
        duplicate, ordering, worker/GPU loss, retry, and deterministic or
        disclosed-nondeterministic aggregation. Evidence: distributed trace.
  - [ ] A8.19.e Roll out a compiled/optimized model through compatibility,
        known-value/calibrated comparison, canary, rollback, and historical
        result provenance. Evidence: model rollout journey.
  - [ ] A8.19.f Assemble, validate, sign, publish, and independently read back
        the final package while preserving all specialized lineage, consent,
        rights, quality, compatibility, and failure evidence. Evidence: package
        journey.
- [ ] A8.20 EVE-SEAM-EXIT: require "required Eve reads and writes admitted with
      source and behavior evidence, or an exact task-0.8 explicit non-goal
      proves zero operations; a named defer preserves ownership but never passes
      completion" before this phase exits.
      Evidence: source-derived totality record and passing verifier.
  - [ ] A8.20.a Re-run the route, bridge, state-signal, and complete domain-code
        envelope census; review every changed candidate instead of carrying a
        stale phase disposition forward. Evidence: current ratchets.
  - [ ] A8.20.b Bind the exact ratified task-0.8 workflow and requirement set;
        classify it as a required seam or an exact source-backed non-goal.
        Evidence: charter-bound disposition set.
  - [ ] A8.20.c For a required seam, admit both read and write operations with
        byte-pinned source and behavior evidence. A named defer retains its owner
        and unblock condition but fails completion. Evidence: operation proofs.
  - [ ] A8.20.d Permit a zero-operation boundary only for an exact ratified
        task-0.8 explicit non-goal; absence or unavailability is not a non-goal.
        Evidence: source-bound non-goal proof.
  - [ ] A8.20.e Run the totality verifier, every retained gate-case CLI, and the
        injected durable-candidate control; retain red and green receipts.
        Evidence: release gate logs.

---

## Phase B — Bellona DCC bridge and interchange console

Bellona is a Studio-only substrate with an existing `apps/bellona/control-room`.
The work is to unify that control room with the bridge hosts, build/render
services, OpenUSD interchange, consistency checks, artifact store, release
evidence, and governed Studio entry point without creating a duplicate console.

### B0 — Host, bridge matrix, ownership, and threat model

- [ ] B0.1 Ratify `apps/bellona/control-room` as the canonical operator console
      or record the replacement and migration. Evidence: host ADR.
  - [ ] B0.1.a Inventory current Bellona control-room ownership, users, routes,
        mutations, deployments, data sources, and operational dependencies.
        Evidence: current-state map.
  - [ ] B0.1.b Compare retain/refactor and replacement options against
        authority, migration, accessibility, operations, cost, and duplication
        criteria. Evidence: option record.
  - [ ] B0.1.c Ratify one canonical console and its governed Studio projection,
        with named owner and prohibited duplicate surfaces. Evidence: approved
        ADR.
  - [ ] B0.1.d If replaced, define route/data/permission/operation/test rollout,
        coexistence, rollback, and retirement milestones. Evidence: migration
        plan.
- [ ] B0.2 Inventory control-room routes/components/tests, static Studio routes,
      bridge apps, gateways, hosts, build/render services, CLI, and libraries.
      Evidence: route-capability matrix.
      <!-- partial: UI (a) + runtime (b) + code (c) inventories done in
                                                                                                                                                                                                                                                                      evidence/v1-workbenches/bellona-inventory.json. d (capability/disposition
                                                                                                                                                                                                                                                                      mapping) is design/human and stays unchecked. -->
  - [x] B0.2.a Enumerate control-room and Studio routes, components, loaders,
        actions, tests, flags, permissions, and data dependencies. Evidence: UI
        inventory.
        <!-- evidence: .ui — the control-room React-Router routes (4) + module/
                                                                                                                                                                                                                                                                        component counts + 82 /studio/bellona/* Studio pages. generate-bellona-
                                                                                                                                                                                                                                                                        inventory.mjs + 5 tests. -->
  - [x] B0.2.b Enumerate bridge/remote/gateway/host/build/render apps and their
        protocols, commands, state, deployment, and owner. Evidence: runtime
        inventory.
        <!-- evidence: .runtimeApps — all 10 runtime apps classified by kind
                                                                                                                                                                                                                                                                        (dcc-bridge×4, remote×3, build×2, render×1) AND transport protocol
                                                                                                                                                                                                                                                                        (websocket/grpc/http/ipc): the DCC bridges (blender/godot/unity/unreal) speak
                                                                                                                                                                                                                                                                        WebSocket, not HTTP routes. deployment/owner are the semantic half. -->
  - [x] B0.2.c Enumerate CLI commands and Bellona libraries with public
        contracts, consumers, persistence, jobs/events, and test coverage.
        Evidence: code inventory.
        <!-- evidence: .code — 6 CLI commands + 41 libs/bellona/* packages. Per-lib
                                                                                                                                                                                                                                                                        contracts/consumers already in the BASE.3 library graph; this is the Bellona
                                                                                                                                                                                                                                                                        code-surface count. -->
  - [ ] B0.2.d Map every discovered item to one capability/disposition and flag
        duplicates, orphans, stubs, fixtures, and dead paths. Evidence:
        route-capability matrix.
- [ ] B0.3 Map all `libs/bellona/*` packages to console capabilities, bridge
      runtime, SDK, consumer integration, or out-of-scope. Evidence: ledger.
  - [x] B0.3.a Enumerate each package's entry points, exports, schemas, runtime
        side effects, dependencies, consumers, owners, and tests. Evidence:
        <!-- evidence: .domains.bellona.packages — 41 libs/bellona/* packages with entry points/exports/dependencies/consumers/tests/disposition. runtime side effects + owners are B0.3.b classification (semantic). evidence/v1-workbenches/domain-package-inventories.json (254 pkgs; per-package exports/consumers/deps/disposition/tests + capability catalog with exact source locations). -->
        package census.
  - [ ] B0.3.b Classify every public symbol by canonical console, bridge
        runtime, SDK, consumer seam, shared substrate, migration-only, or
        out-of-scope ownership. Evidence: symbol ledger.
  - [ ] B0.3.c Resolve overlapping implementations and undocumented cross-
        domain authority. Evidence: disposition decisions.
  - [ ] B0.3.d Verify build/dependency graph and consumer imports agree with
        approved boundaries. Evidence: boundary tests.
- [ ] B0.4 Define the governed Oshun Studio entry/facade and deep-link boundary.
      Evidence: Bellona never becomes a customer-domain route.
  - [ ] B0.4.a Define the Studio audience, permitted summary fields/actions,
        canonical Bellona destinations, and prohibited bridge/admin detail.
        Evidence: facade contract.
  - [ ] B0.4.b Define signed link context, audience, tenant/project, object/
        revision, expiry, nonce, and return behavior. Evidence: link schema.
  - [ ] B0.4.c Reauthorize at Bellona route load and prevent the Studio facade
        from proxying canonical mutation authority. Evidence: boundary tests.
  - [ ] B0.4.d Verify navigation, route ownership, analytics, support docs, and
        command palette never expose Bellona as a customer-domain route.
        Evidence: route audit.
- [ ] B0.5 Define operator, DCC artist, technical artist, pipeline engineer,
      build engineer, reviewer, release manager, security operator, and
      administrator roles. Evidence: permission matrix.
  - [ ] B0.5.a Define job duties, least-privilege capabilities, tenant/project/
        host scope, separation-of-duties, and prohibited actions for each role.
        Evidence: role definitions.
  - [ ] B0.5.b Map view, command, transfer, configure, approve, waive, release,
        revoke, quarantine, and emergency actions to object/property authority.
        Evidence: permission matrix.
  - [ ] B0.5.c Define step-up, dual control, break-glass, conflict, expiry, and
        delegation rules for privileged operations. Evidence: control policy.
  - [ ] B0.5.d Test every role across console, API, bridge, CLI/SDK, Studio
        facade, and in-host UI. Evidence: cross-surface authorization suite.
- [ ] B0.6 Define supported DCC/engine hosts and versions for Blender, Maya,
      Houdini, 3ds Max, Unreal, Unity, Godot, and ratified extensions. Evidence:
      compatibility matrix.
  - [ ] B0.6.a Define host/plugin/OS/architecture/server/protocol version tuple,
        support tier, lifecycle, owner, and test environment. Evidence: host
        registry.
  - [ ] B0.6.b Ratify Blender, Maya, Houdini, 3ds Max, Unreal, Unity, and Godot
        tuples independently with install/update and vendor-support constraints.
        Evidence: approved rows.
  - [ ] B0.6.c Register extensions only with capability, trust, packaging,
        compatibility, security, and release proof separate from the base host.
        Evidence: extension records.
  - [ ] B0.6.d Define compatibility windows, deprecation/expiry, unavailable
        cells, and upgrade/downgrade behavior. Evidence: lifecycle matrix.
- [ ] B0.7 Define source and target formats, OpenUSD profiles, schemas, resolver
      rules, units, axes, color, material, animation, rig, and package
      expectations. Evidence: interchange profile.
  - [ ] B0.7.a Register each source/target format/profile/version, reader/
        writer, host support, limits, and unsupported constructs. Evidence:
        format registry.
  - [ ] B0.7.b Ratify OpenUSD core/extensions, schema/API versions, layer/
        composition policy, resolver contexts, package layout, and validation.
        Evidence: USD profile.
  - [ ] B0.7.c Define geometry/topology, transforms/units/axes, color, material/
        texture, animation/time, rig/skinning, camera/light, metadata, and
        dependency mappings. Evidence: semantic mapping spec.
  - [ ] B0.7.d Define deterministic handling and disclosure for unknown, lossy,
        approximated, baked, remapped, or synthesized features. Evidence: loss
        policy.
- [ ] B0.8 Define local, remote, cloud, and on-box UE host trust boundaries.
      Evidence: network/auth architecture.
  - [ ] B0.8.a Diagram actors, processes, devices, networks, stores, secrets,
        data/command flows, and tenancy for each deployment mode. Evidence:
        trust diagrams.
  - [ ] B0.8.b Define host/device identity, enrollment, attestation, mutual
        authentication, authorization grants, key storage/rotation, and revoke.
        Evidence: identity architecture.
  - [ ] B0.8.c Define inbound/outbound network policy, discovery, relays,
        firewall/proxy/NAT, SSRF controls, and offline behavior. Evidence:
        network architecture.
  - [ ] B0.8.d Define additional isolation and command/file safeguards for
        remote/cloud and on-box Unreal execution. Evidence: deployment threat
        review.
- [ ] B0.9 Define artifact retention, cache, legal hold, licensing, provenance,
      release evidence, and deletion policy. Evidence: data lifecycle.
  - [ ] B0.9.a Classify source, intermediate, cache, log, preview, build,
        package, release, and evidence artifacts by sensitivity and authority.
        Evidence: data catalog.
  - [ ] B0.9.b Define retention, expiry, cache eviction, archive, deletion,
        tombstone, backup, and restoration per class. Evidence: lifecycle
        schedule.
  - [ ] B0.9.c Define legal hold and licensing/redistribution constraints,
        conflicts, and authorized release/deletion behavior. Evidence: policy
        matrix.
  - [ ] B0.9.d Define immutable source/transformation/dependency/tool/host/
        operator provenance and evidence retention beyond artifact deletion.
        Evidence: provenance contract.
  - [ ] B0.9.e Prove expiry, deletion, hold, quarantine, recall, and read-back
        reconciliation across stores/caches/targets. Evidence: lifecycle tests.
- [ ] B0.10 Threat-model remote command injection, bridge impersonation,
      compromised plugins/hosts, malicious assets/USD, path traversal, SSRF,
      credential theft, artifact tamper, supply chain, and build exfiltration.
      Evidence: mitigation/test map.
  - [ ] B0.10.a Enumerate assets, actors, entry points, trust transitions,
        attacker capabilities, abuse cases, and consequences per deployment/
        host profile. Evidence: threat inventory.
  - [ ] B0.10.b Model command injection/confusion, bridge/host/plugin identity
        spoofing, stale grants, and compromised execution. Evidence: command
        threat scenarios.
  - [ ] B0.10.c Model malicious files/USD/archives/dependencies, parser abuse,
        path/resolver traversal, SSRF, and artifact tamper. Evidence: content
        threat scenarios.
  - [ ] B0.10.d Model credential/secret theft, supply-chain substitution,
        worker/build compromise, cross-tenant access, and build exfiltration.
        Evidence: platform threat scenarios.
  - [ ] B0.10.e Map each threat to preventive/detective/recovery control, owner,
        residual risk, adversarial test, and release gate. Evidence: approved
        mitigation matrix.
- [ ] B0.11 Define emergency controls for host revoke, session terminate,
      command stop, artifact quarantine, template revoke, and release recall.
      Evidence: authority/blast-radius matrix.
  - [ ] B0.11.a Define trigger, authority, step-up/dual control, scope selector,
        confirmation, and immutable audit for each emergency action. Evidence:
        control contracts.
  - [ ] B0.11.b Compute active sessions/commands/transfers/jobs/builds,
        artifacts, templates, releases, targets, and consumers in blast radius.
        Evidence: impact preview.
  - [ ] B0.11.c Specify immediate stop/fencing behavior, late-result handling,
        quarantine, downstream propagation, and degraded operation. Evidence:
        response design.
  - [ ] B0.11.d Define recovery/re-enrollment/unquarantine/re-release authority
        and proof requirements. Evidence: recovery policy.
  - [ ] B0.11.e Exercise each control and verify target/consumer read-back to
        closure. Evidence: emergency drill.
- [ ] B0.12 Define success measures for connection, transfer, conversion,
      fidelity, queue, build, cache, host availability, cost, and release
      verification. Evidence: metric spec.
  - [ ] B0.12.a Define each measure's user outcome, numerator/denominator,
        population, unit, source, window, exclusions, and owner. Evidence:
        metric catalog.
  - [ ] B0.12.b Define latency/reliability/correctness/fidelity/capacity/cost
        targets by supported host/profile/tier where warranted. Evidence: target
        matrix.
  - [ ] B0.12.c Define bounded privacy-safe dimensions and prohibit secret,
        asset-content, path, free-text, and high-cardinality labels. Evidence:
        telemetry policy.
  - [ ] B0.12.d Link measures to dashboards, alerts, error budgets, release
        gates, and corrective decisions. Evidence: operational trace.
- [ ] B0.13 Author and ratify `V1/BELLONA_DCC_CONSOLE_DESIGN_2026-07-23.md` with
      current-state, host, bridge trust, OpenUSD spine, contracts, data, APIs,
      fleet, UI, release, migration, rollout, and decision records. Evidence:
      approved doc and resolved review comments.
  - [ ] B0.13.a Draft current-state findings, scope/boundaries, personas/
        permissions, host/version matrix, and trust/threat architecture.
        Evidence: reviewed design sections.
  - [ ] B0.13.b Draft OpenUSD/interchange, canonical data, protocol/API/events,
        persistence/artifact, queue/fleet, and consumer contracts. Evidence:
        reviewed technical sections.
  - [ ] B0.13.c Draft task-led console/in-host/Studio experiences,
        accessibility, honest states, operations, verification, and release.
        Evidence: reviewed experience sections.
  - [ ] B0.13.d Draft migration/compatibility/rollout/rollback/retirement,
        risks, open decisions, ownership, and milestones. Evidence: reviewed
        delivery sections.
  - [ ] B0.13.e Obtain named cross-functional review, resolve every comment or
        record a decision, and ratify an immutable version. Evidence: approval
        log.
- [ ] B0.14 Reconcile every Bellona control-room/Studio route, bridge/remote/
      build/render app, CLI/SDK command, `libs/bellona/*` package, protocol,
      host plugin, workflow, test, and documented feature at atomic-feature
      level. Evidence: explicit dispositions cover bridges/interchange/builds,
      render, gameplay, virtual production, XR, mocap, MetaHuman, Resolve/
      Houdini, audio/video, text-to-3D/creative flows, agent/MCP control,
      cross-domain ingestion, events, and release evidence.
  - [x] B0.14.a Build a normalized inventory of every named code, route,
        contract, command, feature claim, workflow, test, and release artifact
        with stable IDs and owners. Evidence: atomic inventory.
        <!-- bellona-code-census.json: 13 artifact classes, each with a stable ID
                                                                                                                                                                                                                                                                        (bellona.pkg/app/code/test/route.*/runtime-app/protocol/cli/contract/
                                                                                                                                                                                                                                                                        workflow/doc/deploy) + package-derived owner. 41 libs packages + 23 app
                                                                                                                                                                                                                                                                        projects, 952 code files + 630 tests, 4 control-room + 82 studio routes,
                                                                                                                                                                                                                                                                        10 runtime apps (dcc-bridge 4/remote 3/build 2/render 1) over grpc/http/
                                                                                                                                                                                                                                                                        ipc/websocket, 6 CLI commands (bellona/export/list/query/start/submit),
                                                                                                                                                                                                                                                                        8 contract/schema files, 86 workflow/plugin/bridge files, 51 docs, and
                                                                                                                                                                                                                                                                        domain-specific deploy targets (unreal/blender/remote-mac/cloud-agent
                                                                                                                                                                                                                                                                        smokes). Consolidated from fresh git counts + committed B0.2 inventory.
                                                                                                                                                                                                                                                                        generate-bellona-code-census.mjs + 6 tests, wired into inventory/verify +
                                                                                                                                                                                                                                                                        CI. NAMED human owners, atomic-feature decomposition (B0.14.b),
                                                                                                                                                                                                                                                                        dispositions (B0.14.c), zero-orphan reconciliation (B0.14.d), and signed
                                                                                                                                                                                                                                                                        owner review (B0.14.e) are semantic/human-governance and stay unchecked;
                                                                                                                                                                                                                                                                        parent B0.14 stays [ ]. -->
  - [ ] B0.14.b Decompose compound claims into independently classifiable host,
        interchange, build, render, runtime, VP, XR, mocap, character,
        editorial/procedural, media, generative, agent, integration, event, and
        evidence capabilities. Evidence: feature ledger.
  - [ ] B0.14.c Assign each item retain/refactor/migrate/replace/merge/remove/
        external-owner/defer decisions with rationale, destination, dependency,
        proof, and target milestone. Evidence: disposition audit.
  - [ ] B0.14.d Map inventory to ledger and ledger back to authoritative or
        explicitly net-new scope; resolve duplicates/orphans. Evidence:
        bidirectional zero-orphan check.
  - [ ] B0.14.e Obtain domain-owner review of every unresolved, conflicting, or
        high-risk disposition. Evidence: signed reconciliation.
- [ ] B0.15 Define the boundary among control-room operations, in-host artist
      plugin UI, governed Studio facade, remote/cloud administration,
      build/release automation, virtual-production stage control, gameplay/XR
      validation, and developer SDK/CLI. Evidence: every capability has one
      authoritative mutation surface and consistent projections elsewhere.
  - [ ] B0.15.a Catalog read and mutation capabilities for each surface and
        identify duplicated or missing authority. Evidence: surface matrix.
  - [ ] B0.15.b Assign one authoritative mutation surface/service per capability
        with role, context, audit, and consistency contract. Evidence: authority
        registry.
  - [ ] B0.15.c Define read-only projections, signed deep links, request/
        approval seams, and event reconciliation for non-authoritative surfaces.
        Evidence: projection contracts.
  - [ ] B0.15.d Test conflicting concurrent actions, stale projections,
        permission changes, offline hosts, and partial propagation across
        surfaces. Evidence: boundary suite.
- [ ] B0.16 Ratify specialized compatibility profiles for asset interchange,
      engine project/runtime, render, virtual production, XR device, mocap,
      MetaHuman, Resolve editorial, Houdini procedural, and cross-domain
      compilation. Evidence: each profile names hosts/versions, commands,
      assets, measurements, gates, safety controls, and release targets.
  - [ ] B0.16.a Define the common profile schema and lifecycle for exact hosts/
        versions, commands, assets, mappings, measurements, validators/gates,
        safety controls, targets, owners, evidence, and limitations. Evidence:
        schema tests.
  - [ ] B0.16.b Ratify separate asset-interchange, engine-runtime, and render
        profiles with no inherited target proof. Evidence: profile records.
  - [ ] B0.16.c Ratify separate virtual-production, XR-device, mocap, and
        MetaHuman profiles with device/stage/performer safety boundaries.
        Evidence: profile records.
  - [ ] B0.16.d Ratify separate Resolve editorial, Houdini procedural, and
        cross-domain compilation profiles with source/target authority and
        deterministic evidence rules. Evidence: profile records.
  - [ ] B0.16.e Reject execution or release under missing, stale, incompatible,
        or cross-profile evidence. Evidence: independence suite.

### B1 — Session, command, artifact, interchange, and release contracts

- [ ] B1.1 Define host registration with DCC/engine, version, plugin, platform,
      capabilities, identity, attestation, health, load, and maintenance.
      Evidence: schema.
  - [ ] B1.1.a Define immutable host/device identity, owner/tenant, enrollment,
        certificate/key version, attestation, and revocation fields. Evidence:
        identity schema.
  - [ ] B1.1.b Define host/DCC/engine/plugin/OS/architecture/protocol versions
        and typed capability/limit declarations. Evidence: compatibility schema.
  - [ ] B1.1.c Define heartbeat time, health components, resource/load/license
        state, network locality, and freshness semantics. Evidence: liveness
        schema.
  - [ ] B1.1.d Define maintenance/drain/quarantine/disabled lifecycle,
        concurrency rules, and audit invariants. Evidence: state tests.
- [ ] B1.2 Define bridge session with host, actor, project, workspace, purpose,
      grants, expiry, heartbeat, status, and termination. Evidence: invariants.
  - [ ] B1.2.a Define session identity, enrolled host revision, authenticated
        actor, tenant/project/workspace, client, purpose, and trace. Evidence:
        session schema.
  - [ ] B1.2.b Define least-privilege typed grants with resource/path scope,
        command capabilities, limits, issue/expiry/revoke times, and approver.
        Evidence: grant schema.
  - [ ] B1.2.c Define connect/active/degraded/draining/expired/terminating/
        terminated states, heartbeat, reconnect, and concurrent-session rules.
        Evidence: state machine.
  - [ ] B1.2.d Define user, host, administrator, timeout, revoke, and emergency
        termination semantics with fenced late commands. Evidence: invariants.
- [ ] B1.3 Define command envelope with version, capability, target, arguments,
      idempotency, deadline, expected revision, authorization, and correlation.
      Evidence: contract tests.
  - [ ] B1.3.a Define command/protocol version, registry capability/action,
        session/host/project target, and typed argument schema reference.
        Evidence: envelope schema.
  - [ ] B1.3.b Define command/idempotency/correlation/causation IDs, requested
        time, deadline, priority, and retry budget. Evidence: identity tests.
  - [ ] B1.3.c Define expected object/scene/file revisions, preconditions,
        resource/path scopes, dry-run/impact-preview reference, and actor grant.
        Evidence: concurrency contract.
  - [ ] B1.3.d Sign/authenticate the immutable envelope and reject unknown,
        malformed, expired, replayed, stale, or unauthorized commands. Evidence:
        negative contract suite.
- [ ] B1.4 Define command result with status, output refs, diagnostics, logs,
      measurements, partial state, and retry semantics. Evidence: schema.
  - [ ] B1.4.a Define accepted/running/succeeded/failed/cancelled/expired/
        rejected/partial/unknown statuses and legal transitions. Evidence:
        result state machine.
  - [ ] B1.4.b Define attempt/host/tool versions, start/end, progress,
        checkpoints, output artifact refs/checksums, changed-object revisions,
        and target read-back. Evidence: result schema.
  - [ ] B1.4.c Define typed diagnostics, bounded redacted logs, raw
        measurements, warnings, limitations, and unsupported-feature detail.
        Evidence: diagnostic fixtures.
  - [ ] B1.4.d Define retryability, safe retry point, compensation/undo,
        irreversible side effects, partial outputs, and late-result semantics.
        Evidence: fault contract tests.
- [ ] B1.5 Define interchange job and stage DAG from source ingest through
      canonical USD and targets. Evidence: DAG validation.
  - [ ] B1.5.a Define immutable job identity, source artifact/revision/profile,
        requested targets, configuration, priority, deadline, and actor.
        Evidence: job schema.
  - [ ] B1.5.b Define typed
        ingest/inspect/normalize/USD-author/resolve/validate/
        convert/package/read-back stages with inputs, outputs, requirements, and
        failure policy. Evidence: stage registry.
  - [ ] B1.5.c Validate dependency acyclicity, input/output compatibility,
        required stages, target/profile coverage, and terminal gates. Evidence:
        DAG validator.
  - [ ] B1.5.d Define attempts, checkpoints, cache keys, rerun/invalidation,
        cancellation, partial target results, and immutable lineage. Evidence:
        execution invariants.
- [ ] B1.6 Define artifact, representation, version, layer, dependency, resolver
      context, checksum, size, rights, provenance, and retention. Evidence:
      schema.
  - [ ] B1.6.a Define logical artifact identity, immutable version, asset type,
        lifecycle, owner/tenant/project, rights/license, sensitivity, and
        retention class. Evidence: artifact schema.
  - [ ] B1.6.b Define each representation's format/profile, blob ref, checksum,
        size, media type, tool/host, coordinate/color/time basis, and
        validation. Evidence: representation schema.
  - [ ] B1.6.c Define USD layer identity/role/order/muting, composition arcs,
        resolver context, variants, payloads, and package membership. Evidence:
        layer fixtures.
  - [ ] B1.6.d Define typed dependency edges with exact version/checksum,
        optionality, resolver rule, license/rights propagation, and cycle
        policy. Evidence: dependency tests.
  - [ ] B1.6.e Define source/command/transformation/operator/tool provenance,
        supersession/quarantine/recall, and retention/legal-hold invariants.
        Evidence: lifecycle fixtures.
- [ ] B1.7 Define geometry, topology, transform, unit, axis, material, texture,
      rig, animation, camera, light, metadata, and scene measurements. Evidence:
      typed measure registry.
  - [ ] B1.7.a Define common measurement identity, subject/path, component,
        algorithm/version/config, unit, coordinate/time basis, raw value/range,
        uncertainty, and source revision. Evidence: measure schema.
  - [ ] B1.7.b Register geometry/topology and transform/unit/axis measures with
        applicability and independent known values. Evidence: registry rows.
  - [ ] B1.7.c Register material/texture/color and rig/skinning/animation
        measures with applicability and independent known values. Evidence:
        registry rows.
  - [ ] B1.7.d Register camera/light/timecode and metadata/scene/dependency
        measures with applicability and independent known values. Evidence:
        registry rows.
  - [ ] B1.7.e Define missing, unavailable, unsupported, invalid-input, and
        evaluator-failure states distinct from numeric results. Evidence: state
        tests.
- [ ] B1.8 Define consistency result separately from raw measurements. Evidence:
      policy recomputation.
  - [ ] B1.8.a Define immutable raw measurement sets independent of thresholds
        and release decision. Evidence: measurement persistence schema.
  - [ ] B1.8.b Define verdict identity, profile/version, exact measurements,
        thresholds/rules, pass/fail/review/non-pass status, and reasons.
        Evidence: consistency schema.
  - [ ] B1.8.c Define human review/waiver identity, authority, scope, rationale,
        expiry, and prohibited waivers. Evidence: review schema.
  - [ ] B1.8.d Recompute a new verdict under new policy without mutating prior
        measurements or verdicts. Evidence: policy-recompute fixture.
- [ ] B1.9 Define build template, parameter schema, target, toolchain,
      dependency lock, environment, resources, gates, and lifecycle. Evidence:
      schema.
  - [ ] B1.9.a Define template identity/version/owner/purpose, supported
        profiles/targets, typed parameters/defaults/constraints, and secrets
        references. Evidence: template schema.
  - [ ] B1.9.b Define exact toolchain/container/host SDK/runtime/compiler/
        plugin versions, dependency lock/checksums, and environment policy.
        Evidence: reproducibility schema.
  - [ ] B1.9.c Define CPU/GPU/memory/storage/license/device/network/resource
        requirements, capacity class, timeout, checkpoint, and cost model.
        Evidence: execution schema.
  - [ ] B1.9.d Define required input/intermediate/output gates, approvals,
        target validation/read-back, package/signing, and evidence outputs.
        Evidence: gate contract.
  - [ ] B1.9.e Define draft/review/approved/deprecated/revoked lifecycle,
        compatibility windows, impact preview, and immutable historical use.
        Evidence: lifecycle tests.
- [ ] B1.10 Define release candidate and evidence with source revisions,
      commands, artifacts, checksums, validators, gates, approvals, toolchain,
      target, and read-back. Evidence: integrity invariants.
  - [ ] B1.10.a Lock candidate identity to exact source/dependency/template/
        configuration/command/artifact revisions and checksums. Evidence:
        candidate schema.
  - [ ] B1.10.b Record toolchain/host/worker/environment, validator/profile/
        calibration/threshold versions, raw measures, verdicts, and limitations.
        Evidence: technical evidence schema.
  - [ ] B1.10.c Record named approvals/waivers, roles, scopes, conditions,
        expiry, signatures, and staleness dependencies. Evidence: governance
        schema.
  - [ ] B1.10.d Record target/package/deployment identity, manifest/signatures,
        delivery result, and independent read-back measurements/checksums.
        Evidence: target evidence schema.
  - [ ] B1.10.e Verify no release evidence can be reused after any locked
        revision, gate, approval, rights, or target state changes. Evidence:
        integrity tests.
- [ ] B1.11 Define immutable revisions/diffs for layers, dependencies, settings,
      templates, mappings, and evidence. Evidence: golden fixtures.
  - [ ] B1.11.a Define canonical serialization, stable identity, parent/base
        revision, author/time/reason, checksum, and optimistic-concurrency
        invariants. Evidence: revision schema.
  - [ ] B1.11.b Define semantic layer and dependency diffs including add/
        remove/change/reorder/mute/resolver/version/license effects. Evidence:
        diff goldens.
  - [ ] B1.11.c Define settings/template/mapping diffs with defaults,
        normalization, secret redaction, compatibility, and downstream impact.
        Evidence: diff goldens.
  - [ ] B1.11.d Define evidence diffs for measurements, policies, verdicts,
        approvals, limitations, and target read-back without rewriting history.
        Evidence: diff goldens.
- [ ] B1.12 Define protocol and contract version negotiation. Evidence: version
      skew and unsupported-client tests.
  - [ ] B1.12.a Advertise supported protocol/contract/capability versions and
        constraints for server, gateway, host/plugin, SDK, and consumer.
        Evidence: negotiation schema.
  - [ ] B1.12.b Select the highest mutually approved version deterministically
        and bind it to the session/command/job. Evidence: selection tests.
  - [ ] B1.12.c Define backward/forward compatibility, optional/unknown fields,
        capability downgrade, deprecation, and expiry semantics. Evidence:
        compatibility policy.
  - [ ] B1.12.d Fail closed with actionable unsupported/incompatible states
        rather than guessing or silently coercing. Evidence: version-skew suite.
- [ ] B1.13 Define session/command audit projections with sensitive argument
      redaction. Evidence: traceability without secret leakage.
  - [ ] B1.13.a Define immutable audit events for enrollment, grant, session,
        command, result, artifact, approval, release, revoke, and emergency
        action. Evidence: audit schema.
  - [ ] B1.13.b Record actor/host/tenant/project, action, object/revision,
        decision, time, correlation, and evidence refs without raw protected
        payloads. Evidence: trace fixture.
  - [ ] B1.13.c Apply typed allowlist/redaction/hashing/tokenization to command
        arguments, paths, URLs, secrets, content, and DCC logs. Evidence:
        redaction tests.
  - [ ] B1.13.d Provide role-filtered investigations that reconstruct sequence
        and causation while preserving compartment boundaries. Evidence: audit
        journey.
- [ ] B1.14 Define SDK and remote-extension consumer compatibility. Evidence:
      public-contract fixtures.
  - [ ] B1.14.a Define supported SDK language/version/runtime and remote-
        extension platform/version matrices. Evidence: consumer registry.
  - [ ] B1.14.b Define public discovery, enrollment/session, typed command,
        artifact/job/result, event, error, auth, retry, and cancellation
        contracts. Evidence: public API spec.
  - [ ] B1.14.c Define compatibility windows, negotiation, feature detection,
        generated clients/types, and deprecation behavior. Evidence: version
        fixtures.
  - [ ] B1.14.d Compile and run representative external consumers against
        current and supported prior server versions. Evidence: consumer suite.
- [ ] B1.15 Define render job, scene/camera/layer, frame/range, renderer,
      settings, color/OCIO, output/pass, resource, license, checkpoint,
      artifact, measurement, and review contracts. Evidence: still/sequence and
      multi-pass fixtures.
  - [ ] B1.15.a Define immutable render source scene/USD, camera, layer/variant,
        frame/range/handles, resolution/aspect, renderer/version, and settings.
        Evidence: render request schema.
  - [ ] B1.15.b Define OCIO/config/display/view/look, input/output color spaces,
        tone/exposure, and color-provenance lock. Evidence: color contract.
  - [ ] B1.15.c Define beauty/AOV/pass/output naming, format/profile, channels,
        compression, destination, checksum, and package relationships. Evidence:
        output schema.
  - [ ] B1.15.d Define compute/storage/license requirements, chunking,
        checkpoint/resume, retries, partial frames/passes, cancellation, and
        cost. Evidence: execution contract.
  - [ ] B1.15.e Define per-frame/sequence measurements, visual review,
        approvals, artifact lineage, and read-back for still, sequence, and
        multi-pass outputs. Evidence: render fixtures.
- [ ] B1.16 Define virtual-production stage/session, camera tracking,
      calibration, lens, LED wall, frustum, genlock, timecode, ICVFX layer,
      playback, recording, safety, and incident contracts. Evidence:
      device/clock/fail-safe invariants.
  - [ ] B1.16.a Define stage/session identity, location, schedule, personnel/
        roles, project/shot, device inventory, topology, and operating mode.
        Evidence: stage schema.
  - [ ] B1.16.b Define camera tracker/calibration/lens/distortion/focus/zoom,
        coordinate transforms, uncertainty, validity window, and drift.
        Evidence: camera fixtures.
  - [ ] B1.16.c Define LED wall/processors/panels, color/calibration, frustum,
        ICVFX layers, content versions, playback, and target state. Evidence:
        display contract.
  - [ ] B1.16.d Define genlock/timecode/reference clock, sync quality,
        discontinuity, recording/media, take identity, and read-back. Evidence:
        clock/recording fixtures.
  - [ ] B1.16.e Define device-safe command limits, interlocks, emergency stop,
        degraded/fallback states, incident capture, and restart authority.
        Evidence: fail-safe invariants.
- [ ] B1.17 Define XR target/device, runtime, input, interaction, comfort,
      performance, spatial asset, entitlement, package, deployment, and
      validation contracts; define mocap stream/skeleton/retarget/recording and
      MetaHuman identity/rig/Live Link contracts separately. Evidence:
      platform-specific fixtures and consent/privacy mapping.
  - [ ] B1.17.a Define XR target/device/OS/runtime versions, capabilities,
        entitlement, input/interaction mappings, spatial permissions/assets, and
        deployment/package identity. Evidence: XR platform schema.
  - [ ] B1.17.b Define XR frame timing/latency, rendering, memory/thermal,
        tracking, locomotion/comfort, accessibility, safety, and validation
        measures. Evidence: XR quality contract.
  - [ ] B1.17.c Define mocap device/stream protocol, clock, subjects/consent,
        skeleton/channels, calibration, confidence, retarget mapping, recording,
        dropout, and lineage. Evidence: mocap schema.
  - [ ] B1.17.d Define MetaHuman identity/rights, asset/rig/DNA versions,
        animation/face channels, Live Link source/timecode, Unreal
        compatibility, and protected-data handling. Evidence: MetaHuman schema.
  - [ ] B1.17.e Keep XR, mocap, and MetaHuman execution, permissions, quality,
        release, and evidence independently versioned. Evidence: boundary
        fixtures.
- [ ] B1.18 Define gameplay input/action, save schema, inventory, combat,
      behavior/AI, navigation, state, test scenario, telemetry, migration, and
      build contracts. Evidence: versioned runtime/save compatibility and no
      unsafe arbitrary-script command.
  - [ ] B1.18.a Define versioned input devices/actions/bindings/contexts,
        accessibility remapping, network authority, and replayable input
        fixtures. Evidence: input contract.
  - [ ] B1.18.b Define save/profile/checkpoint/world-state schema, version,
        integrity, privacy, migration, rollback, and forward/backward behavior.
        Evidence: save fixtures.
  - [ ] B1.18.c Define inventory/economy, combat/damage/abilities, behavior/AI,
        navigation, quest/state-machine, and deterministic scenario contracts.
        Evidence: gameplay schemas.
  - [ ] B1.18.d Define typed test setup/action/assertion/cleanup commands from
        an allowlist; prohibit arbitrary scripts or console execution. Evidence:
        command security tests.
  - [ ] B1.18.e Define bounded gameplay/performance telemetry, expected
        outcomes, build/package versions, target deployment, and result
        evidence. Evidence: scenario fixtures.

### B2 — Host enrollment, liveness, sessions, and remote security

- [ ] B2.1 Build host enrollment with operator approval and cryptographic
      identity. Evidence: unapproved host cannot connect.
  - [ ] B2.1.a Issue a one-time bounded enrollment challenge tied to tenant,
        environment, expected host, requester, expiry, and nonce. Evidence:
        challenge tests.
  - [ ] B2.1.b Collect host/DCC/plugin/platform identity, key proof,
        attestation, capabilities, owner, and requested scopes without trusting
        claims. Evidence: enrollment request.
  - [ ] B2.1.c Verify policy, compatibility, attestation/signatures, duplicate
        identity, and operator authority before approval. Evidence: review
        tests.
  - [ ] B2.1.d Issue environment-bound cryptographic identity and persist
        approval/rejection/audit; deny every unapproved or replayed request.
        Evidence: connection negative suite.
- [ ] B2.2 Build short-lived mutual authentication and key rotation. Evidence:
      expired, revoked, replayed, and wrong-environment credentials fail.
  - [ ] B2.2.a Establish mutual TLS or equivalent proof from enrolled host and
        environment-specific server identities. Evidence: handshake tests.
  - [ ] B2.2.b Mint short-lived session credentials with audience, tenant, host,
        capability scope, issue/expiry, nonce, and key version. Evidence: token
        inspection.
  - [ ] B2.2.c Rotate host/server keys with bounded overlap, proof of
        possession, atomic activation, rollback, and audit. Evidence: rotation
        journey.
  - [ ] B2.2.d Revoke credentials/keys immediately and reject expired, replayed,
        wrong-host/audience/environment, or downgraded authentication. Evidence:
        adversarial suite.
- [ ] B2.3 Build capability/version negotiation. Evidence: unsupported command
      is rejected before session creation.
  - [ ] B2.3.a Exchange server/host/plugin protocol and typed
        capability/version/ limit declarations after authentication. Evidence:
        negotiation trace.
  - [ ] B2.3.b Intersect declarations with the approved host compatibility
        profile and current policy. Evidence: selection tests.
  - [ ] B2.3.c Bind the negotiated versions/capabilities to the session grant
        and expose explicit downgraded/unavailable reasons. Evidence: session
        contract.
  - [ ] B2.3.d Reject session creation when required versions/capabilities are
        absent and reject later unnegotiated commands. Evidence: negative tests.
- [ ] B2.4 Build liveness, latency, load, DCC state, project state, and
      maintenance probes. Evidence: health rollup fixtures.
  - [ ] B2.4.a Collect signed heartbeat sequence/time, round-trip latency, clock
        skew, process/plugin connectivity, and protocol health. Evidence:
        liveness fixtures.
  - [ ] B2.4.b Collect bounded CPU/GPU/memory/storage/license/queue load and
        capacity without secret/path/content leakage. Evidence: load fixtures.
  - [ ] B2.4.c Collect typed DCC busy/modal/render/dirty-scene state and active
        project/workspace/revision compatibility. Evidence: host-state fixtures.
  - [ ] B2.4.d Collect maintenance/drain/quarantine/update state and freshness.
        Evidence: maintenance fixtures.
  - [ ] B2.4.e Compute healthy/degraded/unavailable/incompatible/stale rollups
        with component reasons and no false healthy state. Evidence: health
        matrix.
- [ ] B2.5 Build session open with scoped project, commands, paths, expiry,
      resources, and actor. Evidence: least-privilege tests.
  - [ ] B2.5.a Authenticate the actor and enrolled host, resolve tenant/project/
        workspace/purpose, and load current compatibility/health. Evidence:
        precondition tests.
  - [ ] B2.5.b Intersect requested commands, virtual paths, objects, targets,
        resources, duration, and limits with role/policy/host capabilities.
        Evidence: scope tests.
  - [ ] B2.5.c Require step-up or approval for sensitive scopes and record exact
        grant rationale/approver. Evidence: grant journey.
  - [ ] B2.5.d Open an immutable expiring session with negotiated versions,
        fencing epoch, actor, scopes, resource reservations, and audit.
        Evidence: session fixture.
  - [ ] B2.5.e Prove ungranted property, path, command, project, resource, and
        time use fails server- and host-side. Evidence: least-privilege suite.
- [ ] B2.6 Build heartbeat, reconnect, suspend, resume, drain, expiry, and
      close. Evidence: clock-controlled session tests.
  - [ ] B2.6.a Heartbeat with monotonic sequence, observed session epoch, host/
        project state, in-flight command refs, and bounded health data.
        Evidence: heartbeat tests.
  - [ ] B2.6.b Mark degraded/disconnected after controlled thresholds and fence
        commands after lease/session expiry. Evidence: clock tests.
  - [ ] B2.6.c Reconnect through reauthentication, state reconciliation, and
        same-or-new epoch without duplicate command execution. Evidence:
        reconnect tests.
  - [ ] B2.6.d Suspend/resume or drain with explicit admission behavior,
        in-flight completion/cancellation policy, and deadline. Evidence:
        lifecycle tests.
  - [ ] B2.6.e Close or expire idempotently, release reservations/grants, retain
        audit, and classify late host results. Evidence: terminal-state tests.
- [ ] B2.7 Build remote command allowlist with typed schemas and property
      authorization. Evidence: arbitrary command/argument tests fail.
  - [ ] B2.7.a Register each command with stable ID/version, supported hosts,
        typed arguments/results, defaults/bounds, side effects, prerequisites,
        permissions, and compensation. Evidence: command registry.
  - [ ] B2.7.b Validate envelope/schema/unknown fields, negotiated capability,
        host/project/object/path scope, expected revision, and deadline before
        dispatch. Evidence: validation suite.
  - [ ] B2.7.c Authorize sensitive argument properties and resulting object/
        file mutations independently of command visibility. Evidence: property
        tests.
  - [ ] B2.7.d Translate only to a fixed host API/plugin operation; prohibit
        arbitrary shell, script, eval, console, path, or network execution.
        Evidence: injection tests.
  - [ ] B2.7.e Audit normalized redacted arguments, authorization decision, host
        operation/version, result, and changed revisions. Evidence: audit
        fixture.
- [ ] B2.8 Build safe path mapping and virtual roots. Evidence: traversal,
      symlink, case collision, UNC/device path, and Unicode tests.
  - [ ] B2.8.a Configure named virtual roots by tenant/project/host/purpose with
        local canonical root, read/write policy, formats, quotas, and lifetime.
        Evidence: root registry.
  - [ ] B2.8.b Parse and normalize separators, dot segments, percent/Unicode
        encoding, normalization form, case semantics, drive/UNC/device prefixes,
        and reserved names. Evidence: path corpus.
  - [ ] B2.8.c Resolve components without following unauthorized symlinks/
        junctions/mounts and verify the final canonical target remains within
        its root. Evidence: traversal tests.
  - [ ] B2.8.d Open/create with race-safe primitives, file type/size/permission
        checks, collision policy, and atomic write/rename. Evidence: TOCTOU
        tests.
  - [ ] B2.8.e Return only virtual paths outside the host and redact local user/
        secret path components from logs/errors. Evidence: disclosure tests.
- [ ] B2.9 Build network destination allowlists for host-side fetches. Evidence:
      SSRF/DNS-rebinding/redirect tests.
  - [ ] B2.9.a Define destination profiles by scheme, exact domain, port, path,
        method, purpose, size/type, TLS policy, credentials, and tenant.
        Evidence: allowlist schema.
  - [ ] B2.9.b Resolve DNS and reject loopback, link-local, private, metadata,
        multicast, reserved, unexpected IP families, and disallowed address
        changes. Evidence: SSRF fixtures.
  - [ ] B2.9.c Validate every redirect and connection against the profile,
        pinning/rechecking resolution to resist DNS rebinding. Evidence:
        redirect/rebinding tests.
  - [ ] B2.9.d Bound time, bytes, decompression, responses, retries, and
        credentials; quarantine fetched content before host use. Evidence: fetch
        safety suite.
- [ ] B2.10 Build command deadlines, cancellation, idempotency, and late-result
      handling. Evidence: race tests.
  - [ ] B2.10.a Enforce admission and execution deadlines using synchronized or
        duration-based clocks and typed expiry states. Evidence: clock tests.
  - [ ] B2.10.b Deduplicate command IDs/idempotency keys and reject the same key
        with a different normalized request. Evidence: duplicate tests.
  - [ ] B2.10.c Propagate cooperative cancellation, host abort, checkpoint/
        cleanup, and compensation according to command contract. Evidence:
        cancellation tests.
  - [ ] B2.10.d Fence result commit by session/command epoch and classify late
        output without publishing it as success. Evidence: race suite.
  - [ ] B2.10.e Reconcile timeout/cancel/result uncertainty through host state
        and changed-object read-back. Evidence: unknown-outcome journey.
- [ ] B2.11 Build bounded output/log streaming with backpressure and redaction.
      Evidence: oversize and secret-leak tests.
  - [ ] B2.11.a Define typed progress/log/diagnostic/output chunk envelopes with
        sequence, command/attempt, time, level, media type, size, and terminal
        semantics. Evidence: stream schema.
  - [ ] B2.11.b Enforce per-event/rate/total/buffer/retention/cardinality bounds
        and spill authorized artifacts rather than unbounded inline data.
        Evidence: limit tests.
  - [ ] B2.11.c Apply structured allowlisting and secret/token/path/content/PII
        redaction at the host before transport and again on ingest. Evidence:
        redaction suite.
  - [ ] B2.11.d Implement acknowledgement/backpressure, resumable cursor,
        duplicate/out-of-order handling, disconnect, and dropped-event markers.
        Evidence: streaming fault tests.
- [ ] B2.12 Build host quarantine and emergency session termination. Evidence:
      in-flight work enters explicit safe state.
  - [ ] B2.12.a Authorize and record host quarantine with reason, evidence,
        scope, effective time, step-up, and optional fleet profile impact.
        Evidence: control tests.
  - [ ] B2.12.b Immediately block new authentication/sessions/commands,
        invalidate credentials/grants, and fence host results. Evidence:
        quarantine tests.
  - [ ] B2.12.c Terminate active sessions/commands according to safe-stop
        contracts and mark jobs/artifacts/releases explicit partial/unknown/
        quarantined states. Evidence: in-flight fault test.
  - [ ] B2.12.d Trace downstream artifacts/builds/releases made by the host and
        launch impact review/recall where required. Evidence: blast-radius test.
  - [ ] B2.12.e Require remediation, re-attestation, approval, key rotation, and
        verification before unquarantine. Evidence: recovery journey.
- [ ] B2.13 Build plugin/extension version, signature, and update policy.
      Evidence: unsigned/downgrade tests.
  - [ ] B2.13.a Register approved package identity/version, publisher/signing
        keys, host/platform ranges, files/checksums, permissions, dependencies,
        and lifecycle. Evidence: package registry.
  - [ ] B2.13.b Verify signatures, provenance/attestation, checksums, archive/
        path safety, malware, dependency lock, and policy before install.
        Evidence: supply-chain suite.
  - [ ] B2.13.c Stage, install, activate, health-check, rollback, and report
        updates atomically without corrupting the host plugin. Evidence: update
        journey.
  - [ ] B2.13.d Reject unsigned, tampered, revoked, wrong-host/environment,
        unapproved downgrade, dependency-confused, or expired packages.
        Evidence: negative tests.
  - [ ] B2.13.e Reconcile fleet versions and quarantine vulnerable/incompatible
        hosts with controlled compatibility windows. Evidence: fleet policy.
- [ ] B2.14 Build audited break-glass access. Evidence: step-up, time limit,
      reason, notification, and review.
  - [ ] B2.14.a Define eligible incidents, permitted emergency capabilities,
        prohibited actions, maximum duration, approver/reviewer, and automatic
        expiry. Evidence: break-glass policy.
  - [ ] B2.14.b Require strong step-up, incident/ticket, justification, exact
        host/project/resource scope, and dual approval where required. Evidence:
        activation tests.
  - [ ] B2.14.c Mint isolated short-lived grants, visibly label their use,
        notify security/owners, and record every view/mutation. Evidence: access
        journey.
  - [ ] B2.14.d Auto-expire/revoke, terminate dependent sessions, rotate exposed
        secrets if needed, and require independent post-use review. Evidence:
        closure tests.
- [ ] B2.15 Build allowlisted screen/frame/viewport capture and host-state
      observation for preview, agent planning, diagnostics, and support with
      explicit user/session grant, visible capture state, window/region scope,
      frame/rate/size limits, protected-region redaction, retention, and revoke.
      Evidence: no hidden desktop surveillance or capture outside the granted
      host/project context.
  - [ ] B2.15.a Define distinct preview, planning, diagnostic, and support
        capture purposes with allowed host windows/viewports, fields, viewers,
        processing, retention, and prohibited use. Evidence: purpose policy.
  - [ ] B2.15.b Require an explicit current user/session grant for exact
        application/window/viewport/region, duration, frame/rate/size, and
        destination. Evidence: consent tests.
  - [ ] B2.15.c Display persistent host-side capture indicator, scope, consumer,
        and stop control that the remote operator cannot hide. Evidence: host UI
        test.
  - [ ] B2.15.d Capture only allowlisted pixels/state; mask protected windows/
        regions, notifications, credentials, other projects, and desktop
        surroundings before egress. Evidence: redaction fixtures.
  - [ ] B2.15.e Enforce bandwidth/frame/rate/resolution/time bounds, secure
        transport/storage, viewer authorization, watermark, and audit. Evidence:
        limit/access suite.
  - [ ] B2.15.f Revoke or expire immediately, stop collection/streaming,
        reconcile buffered frames, apply retention/deletion, and prove no
        capture outside scope. Evidence: surveillance negative test.

### B3 — Ingest, OpenUSD interchange, conversion, and dependency handling

- [ ] B3.1 Build asset intake from upload, artifact store, DCC pull, project
      reference, and previous release. Evidence: provenance per path.
  - [ ] B3.1.a Define a common intake manifest for actor, tenant/project,
        purpose, source kind, source identity/revision, expected format/profile,
        rights/license, and idempotency. Evidence: manifest schema.
  - [ ] B3.1.b Implement bounded resumable upload and existing artifact-store
        reference with checksum/authorization validation. Evidence: intake
        tests.
  - [ ] B3.1.c Implement DCC pull through an enrolled scoped session and project
        reference through an approved resolver context. Evidence: host/ resolver
        tests.
  - [ ] B3.1.d Implement previous-release selection by immutable manifest/
        checksum without mutating or treating it as source authority. Evidence:
        release-input test.
  - [ ] B3.1.e Normalize all paths into one quarantined source artifact while
        retaining path-specific actor/command/source provenance. Evidence:
        lineage matrix.
- [ ] B3.2 Quarantine and scan files, archives, USD packages, scripts, plugins,
      and metadata. Evidence: malicious corpus.
  - [ ] B3.2.a Store intake bytes in an isolated non-executable quarantine with
        immutable checksum, type claim, size limits, and no resolver/network
        access. Evidence: quarantine tests.
  - [ ] B3.2.b Safely enumerate archives/packages with entry/count/depth/ratio/
        total-size bounds and reject traversal, links, devices, collisions, and
        nested bombs. Evidence: archive corpus.
  - [ ] B3.2.c Detect actual media/file types and scan content, scripts,
        plugins/binaries, macros, metadata, and known malware/suspicious
        constructs. Evidence: scanner matrix.
  - [ ] B3.2.d Parse USD in a sandbox with resolver disabled or constrained and
        inspect layers, assets, schemas, paths, code-bearing fields, and
        external references. Evidence: malicious USD corpus.
  - [ ] B3.2.e Emit signed per-file findings and promote only policy-approved
        bytes; preserve rejected/quarantined state and reviewer workflow.
        Evidence: promotion tests.
- [ ] B3.3 Detect format, version, units, axes, color, dependencies, and
      encryption. Evidence: fixture matrix.
  - [ ] B3.3.a Determine format/profile/version from bytes and structure,
        compare extensions/MIME/claims, and report ambiguity or unsupported
        variants. Evidence: detection fixtures.
  - [ ] B3.3.b Detect scene/asset units, scale, up/forward axes, handedness,
        coordinate basis, time codes, and frame rates with source locations.
        Evidence: transform metadata fixtures.
  - [ ] B3.3.c Detect embedded/declared color spaces, OCIO context, texture
        interpretations, and missing/ambiguous color metadata. Evidence: color
        fixtures.
  - [ ] B3.3.d Enumerate embedded/external dependencies and resolver/path
        requirements without fetching them. Evidence: dependency fixtures.
  - [ ] B3.3.e Detect container/file encryption, compression, password/key
        requirements, and unsupported opaque regions; never guess secrets.
        Evidence: encrypted fixtures.
- [ ] B3.4 Build canonical OpenUSD stage creation with pinned profile and schema
      set. Evidence: deterministic golden.
  - [ ] B3.4.a Select exact OpenUSD runtime/profile/schema/plugin versions,
        resolver policy, meters-per-unit, axes, time codes, and layer strategy.
        Evidence: stage plan.
  - [ ] B3.4.b Create root/session/sublayers with deterministic identifiers,
        ordering, default prim, metadata, namespaces, and canonical formatting.
        Evidence: stage golden.
  - [ ] B3.4.c Author only allowed schemas/APIs and record source-to-prim/
        property mapping plus unsupported data. Evidence: schema validation.
  - [ ] B3.4.d Serialize deterministically where promised and attribute
        unavoidable nondeterministic fields explicitly. Evidence: repeated-
        build checksum test.
  - [ ] B3.4.e Reopen with an independent/pinned reader and verify composition,
        metadata, checksums, and provenance. Evidence: read-back test.
- [ ] B3.5 Build layer, variant set, reference, payload, inherit, specialize,
      and composition inspection. Evidence: composition fixtures.
  - [ ] B3.5.a Enumerate layer stack, identifiers, roles, permissions, offsets,
        muting, sublayers, edit target, and resolver context. Evidence: layer
        fixture.
  - [ ] B3.5.b Enumerate variant sets/selections/opinions and compare declared,
        authored, and composed selections. Evidence: variant fixtures.
  - [ ] B3.5.c Enumerate references/payloads with target prim, layer offsets,
        load state, resolution, and dependency identity. Evidence: arc fixtures.
  - [ ] B3.5.d Enumerate inherits/specializes and composition strength/order,
        cycles, conflicts, blocked values, and provenance. Evidence: composition
        fixtures.
  - [ ] B3.5.e Provide composed-value-to-authoring-layer/source tracing and a
        semantic non-graph representation. Evidence: inspection journey.
- [ ] B3.6 Build resolver context and dependency capture without environment-
      implicit paths. Evidence: portable package test.
  - [ ] B3.6.a Define versioned resolver-context inputs, virtual roots, search/
        mapping rules, credentials references, case semantics, and network
        policy. Evidence: resolver schema.
  - [ ] B3.6.b Resolve every dependency under the explicit context and record
        requested path, canonical identity/version/checksum, selected rule, and
        resolution diagnostics. Evidence: resolver trace.
  - [ ] B3.6.c Reject undeclared environment variables, current directories,
        user-home paths, ambient search paths, or network fallback. Evidence:
        hermeticity tests.
  - [ ] B3.6.d Capture or package permitted dependencies with rewritten virtual/
        relative refs and license/rights/provenance. Evidence: dependency
        manifest.
  - [ ] B3.6.e Resolve and open the package on a clean environment/OS fixture
        with no original path context. Evidence: portable package test.
- [ ] B3.7 Build missing, cyclic, duplicate, ambiguous, and external dependency
      detection. Evidence: broken-package corpus.
  - [ ] B3.7.a Build a typed dependency graph from authored and discovered refs,
        including layer/prim/property source and optional/load behavior.
        Evidence: graph fixture.
  - [ ] B3.7.b Detect missing/unreadable/version-mismatched/checksum-mismatched
        dependencies with exact referrers. Evidence: missing fixtures.
  - [ ] B3.7.c Detect cycles and composition/resolver recursion with cycle path
        and affected objects. Evidence: cyclic fixtures.
  - [ ] B3.7.d Detect duplicate logical identities/content, case/Unicode path
        collisions, and ambiguous multi-candidate resolution. Evidence:
        ambiguity fixtures.
  - [ ] B3.7.e Classify external network/local absolute/out-of-root dependencies
        and enforce capture, exception, or rejection policy. Evidence: external
        dependency tests.
- [ ] B3.8 Build geometry/topology conversion and preservation. Evidence: known
      mesh round-trips.
  - [ ] B3.8.a Inventory source primitives, points, faces/indices, orientation,
        normals/tangents, subdivision, creases, holes, UV/color/primvars, and
        instances. Evidence: source measure report.
  - [ ] B3.8.b Map each supported geometry/topology construct to canonical USD
        with explicit interpolation, indexing, precision, and naming. Evidence:
        mapping registry.
  - [ ] B3.8.c Triangulate/tessellate/bake/approximate only when target profile
        requires it and record source-to-output correspondence/loss. Evidence:
        conversion fixtures.
  - [ ] B3.8.d Measure counts, bounds, topology equivalence, orientation,
        normals, UVs/primvars, subdivision, instances, and geometric deviation.
        Evidence: independently known values.
  - [ ] B3.8.e Round-trip representative and adversarial meshes through every
        supported target adapter. Evidence: mesh compatibility matrix.
- [ ] B3.9 Build transform, unit, axis, pivot, hierarchy, and instancing
      conversion. Evidence: independently derived fixtures.
  - [ ] B3.9.a Parse local/world transforms, operation order, pivots, inherit/
        reset behavior, hierarchy, units, axes/handedness, and instance
        prototypes. Evidence: source fixtures.
  - [ ] B3.9.b Compute explicit basis/unit conversions and operation mappings
        with precision, decomposition, negative scale, shear, and rotation-order
        policy. Evidence: analytical goldens.
  - [ ] B3.9.c Preserve hierarchy and instancing where supported or disclose
        flattening/baking/prototype splitting and dependency impact. Evidence:
        loss fixtures.
  - [ ] B3.9.d Compare local/world transforms, pivots, bounds, hierarchy, and
        instance identity after target read-back. Evidence: round-trip matrix.
- [ ] B3.10 Build material, texture, shader, color-space, UDIM, and fallback
      mapping, including MaterialX where supported. Evidence: per-target
      compatibility fixtures.
  - [ ] B3.10.a Inventory material graphs, shader nodes/versions/connections/
        parameters, assignments/subsets, textures/samplers/UDIMs, and color
        metadata. Evidence: material manifest.
  - [ ] B3.10.b Map supported native constructs to pinned USD Shade/MaterialX
        profiles with explicit units, color transforms, and texture resolver
        paths. Evidence: mapping fixtures.
  - [ ] B3.10.c Detect unsupported nodes/features and preview substitute,
        approximate, bake, fallback, or fail choices with visible semantic/
        visual loss. Evidence: loss decisions.
  - [ ] B3.10.d Package textures/UDIM tiles with checksums, dedupe, rights/
        license, path portability, and missing-tile diagnostics. Evidence:
        texture tests.
  - [ ] B3.10.e Read back in each target and compare graph semantics,
        assignments, parameters, texture/color interpretation, and reference
        renders where calibrated. Evidence: compatibility matrix.
- [ ] B3.11 Build skeleton, skinning, blend-shape, animation, constraint, and
      camera conversion. Evidence: round-trip fixtures.
  - [ ] B3.11.a Convert skeleton hierarchy/names/rest/bind transforms and
        skinning influences/weights with unit/axis/precision and limit policy.
        Evidence: rig fixtures.
  - [ ] B3.11.b Convert blend-shape targets/in-betweens/normals/weights and
        animation channels, interpolation, layers, clips, events, and time base.
        Evidence: deformation/animation fixtures.
  - [ ] B3.11.c Map constraints/controllers/drivers to supported target
        constructs or preview explicit bake/approximation/unsupported behavior.
        Evidence: constraint fixtures.
  - [ ] B3.11.d Convert camera projection, aperture/sensor, focal length, lens/
        clipping/focus, transforms, and animation. Evidence: camera goldens.
  - [ ] B3.11.e Read back and measure hierarchy, rest/bind, deformation,
        weights, curves/timing, constraints/bakes, and camera framing per
        target. Evidence: round-trip matrix.
- [ ] B3.12 Build metadata, naming, custom property, and namespace policy.
      Evidence: loss/rename diagnostics.
  - [ ] B3.12.a Define canonical identifier, display-name, path, namespace,
        casing, Unicode, reserved word, length, collision, and stability rules.
        Evidence: naming spec.
  - [ ] B3.12.b Classify standard, custom, sensitive, internal, target-specific,
        code-bearing, and unsupported metadata/properties with type and
        retention rules. Evidence: metadata registry.
  - [ ] B3.12.c Map names/properties deterministically with source/output
        lookup, collision resolution, type/range conversion, and reverse
        mapping. Evidence: mapping tests.
  - [ ] B3.12.d Strip/redact/quarantine prohibited metadata and report every
        rename, drop, coercion, or namespace change. Evidence: diagnostics.
  - [ ] B3.12.e Verify target read-back and mapping reversibility within the
        declared profile. Evidence: metadata round-trip.
- [ ] B3.13 Build target adapters for ratified DCCs/engines through public
      bridge interfaces. Evidence: no target-specific logic in generic core.
  - [ ] B3.13.a Define the public adapter interface for capability discovery,
        plan, import/export, inspect, validate, cancel, result, and read-back.
        Evidence: adapter contract.
  - [ ] B3.13.b Implement per-host adapters against typed bridge commands and
        exact host/plugin/version profiles. Evidence: adapter fixtures.
  - [ ] B3.13.c Keep host-specific formats, defaults, paths, command mappings,
        limitations, and workarounds inside the adapter package. Evidence:
        dependency boundary test.
  - [ ] B3.13.d Run a shared conformance suite plus host-specific known-value,
        unsupported, authorization, cancellation, and failure cases. Evidence:
        adapter matrix.
  - [ ] B3.13.e Reject incompatible/unavailable adapter selection rather than
        falling through generic assumptions. Evidence: selection tests.
- [ ] B3.14 Run `usdchecker` plus pinned custom validators. Evidence: exact
      report and validator versions.
  - [ ] B3.14.a Pin `usdchecker`/OpenUSD and every custom validator code/config/
        dependency version with checksum and supported profile. Evidence:
        validator registry.
  - [ ] B3.14.b Run validators in a sandbox against exact stage/package bytes
        and explicit resolver context. Evidence: execution manifest.
  - [ ] B3.14.c Parse raw output into typed findings while retaining the
        unmodified bounded report artifact. Evidence: parser goldens.
  - [ ] B3.14.d Distinguish tool unavailable/crash/timeout/invalid input from a
        valid validation failure. Evidence: state tests.
  - [ ] B3.14.e Persist validator versions/config/checksums/findings and link
        them to policy verdict/release evidence. Evidence: provenance query.
- [ ] B3.15 Build incremental conversion and cache eligibility. Evidence: cache
      key covers source, dependencies, adapter, toolchain, policy, and target.
  - [ ] B3.15.a Compute semantic change sets from source layer/property/
        dependency revisions and identify affected conversion stages/targets.
        Evidence: invalidation fixtures.
  - [ ] B3.15.b Define cache keys from exact source/dependency bytes, resolver
        context, adapter/toolchain/profile/config/policy/target/environment
        versions. Evidence: key coverage tests.
  - [ ] B3.15.c Reuse only checksum-valid, compatible, authorized, non-
        quarantined entries whose stage supports incremental execution.
        Evidence: eligibility tests.
  - [ ] B3.15.d Recompute affected outputs, preserve unaffected immutable refs,
        and rebuild aggregate manifests/validation. Evidence: incremental
        conversion tests.
  - [ ] B3.15.e Quarantine corrupt or conflicting entries and prove a full
        rebuild matches declared deterministic output. Evidence: cache fault
        test.
- [ ] B3.16 Build conversion plan preview with expected losses, substitutions,
      unsupported features, cost, host, and duration. Evidence: user must accept
      material losses.
  - [ ] B3.16.a Resolve exact source/dependencies, target/profile/adapter/
        toolchain, compatible host/resources, policy, and current measurements.
        Evidence: plan inputs.
  - [ ] B3.16.b Produce ordered stages with per-stage inputs/outputs/cache,
        host/resource/license, estimated duration/cost, and failure/cancel
        semantics. Evidence: plan schema.
  - [ ] B3.16.c Enumerate preserved, renamed, converted, substituted,
        approximated, baked, dropped, and unsupported features with affected
        objects/properties. Evidence: semantic loss report.
  - [ ] B3.16.d Preview validation/review/release consequences and safer target/
        configuration alternatives. Evidence: decision view.
  - [ ] B3.16.e Require explicit authorized acceptance of the exact material
        losses and stale the acceptance when inputs/plan/policy change.
        Evidence: acceptance tests.
- [ ] B3.17 Build durable interchange jobs with checkpoint, retry, cancel, and
      resume. Evidence: host/worker loss test.
  - [ ] B3.17.a Persist job/DAG/stage/attempt state, exact revisions/plan,
        requirements, reservations, idempotency, deadlines, and lineage.
        Evidence: job persistence tests.
  - [ ] B3.17.b Lease stages to compatible hosts/workers with fencing,
        heartbeat, bounded progress, and duplicate-delivery tolerance. Evidence:
        lease tests.
  - [ ] B3.17.c Checkpoint resumable inputs/outputs/tool state atomically and
        validate before reuse. Evidence: checkpoint fixtures.
  - [ ] B3.17.d Retry only classified safe stages under budget/backoff; retain
        attempts and dead-letter permanent/exhausted failures. Evidence: retry
        suite.
  - [ ] B3.17.e Cancel cooperatively and reconcile partial outputs/host side
        effects/late results explicitly. Evidence: cancel tests.
  - [ ] B3.17.f Resume after service/worker/host loss from the last valid fence
        without duplicate mutations or hidden gaps. Evidence: recovery journey.
- [ ] B3.18 Build deterministic or explicitly non-deterministic replay
      classification. Evidence: environment dependencies recorded.
  - [ ] B3.18.a Classify each stage deterministic, conditionally deterministic,
        or nondeterministic with sources of time/randomness/threading/host/
        renderer/external/environment variation. Evidence: stage registry.
  - [ ] B3.18.b Record all reproducibility inputs: bytes/checksums, versions,
        configs, seeds, locale/time zone, hardware/runtime, environment, and
        external dependency snapshots. Evidence: replay manifest.
  - [ ] B3.18.c Re-execute deterministic stages and compare canonical bytes/
        semantic measurements to zero or declared tolerance. Evidence: replay
        tests.
  - [ ] B3.18.d For nondeterministic stages, disclose variation, retain
        candidates/selection, and compare bounded quality/integrity invariants.
        Evidence: nondeterministic replay report.

### B4 — Cross-DCC fidelity, validation, preview, and diff

- [ ] B4.1 Define versioned fidelity profiles by source, target, asset type, and
      release purpose. Evidence: complete profile registry.
  - [ ] B4.1.a Define profile schema for source/target formats, host/tool
        versions, asset type, release purpose, applicable measures, tolerances,
        validators, review/waiver rules, and limitations. Evidence: schema.
  - [ ] B4.1.b Register every supported source-target-asset-purpose combination
        with independently owned thresholds and evidence. Evidence: coverage
        matrix.
  - [ ] B4.1.c Version, approve, activate, deprecate, retire, and migrate
        profiles without rewriting historical results. Evidence: lifecycle
        tests.
  - [ ] B4.1.d Reject validation/release when no exact current compatible
        profile exists. Evidence: missing-profile tests.
- [ ] B4.2 Implement geometry position, normal, topology, UV, and bounds
      measurements. Evidence: known fixtures.
  - [ ] B4.2.a Align source/target geometry by stable mapping, instance,
        coordinate/unit basis, precision, and comparable primitive scope.
        Evidence: alignment fixtures.
  - [ ] B4.2.b Measure point/surface position distance, normal/tangent angular
        deviation, and object/world bounds error. Evidence: analytical goldens.
  - [ ] B4.2.c Compare topology counts/connectivity/orientation/subdivision/
        holes and distinguish declared tessellation or remeshing. Evidence:
        topology fixtures.
  - [ ] B4.2.d Compare UV/primvar sets, interpolation/indexing, seams,
        orientation, range, and per-element deviation. Evidence: UV fixtures.
  - [ ] B4.2.e Report exact objects/elements and raw values before profile
        verdict. Evidence: diagnostic tests.
- [ ] B4.3 Implement transform, unit, axis, pivot, hierarchy, and instance
      measurements. Evidence: known fixtures.
  - [ ] B4.3.a Resolve source/target mapping and normalize declared unit/axis/
        handedness/time basis without hiding conversion errors. Evidence:
        normalization tests.
  - [ ] B4.3.b Measure local/world translation, rotation, scale, shear,
        operation order, and pivot deviations. Evidence: analytical fixtures.
  - [ ] B4.3.c Compare hierarchy parentage/order, reset/inherit behavior,
        visibility, purpose, and instance/prototype identity/count. Evidence:
        scene fixtures.
  - [ ] B4.3.d Detect unmapped, duplicated, flattened, expanded, or ambiguous
        nodes/instances and report raw impact. Evidence: structure tests.
- [ ] B4.4 Implement material parameter, texture, color-space, shader, and
      render-difference measurements. Evidence: target fixture corpus.
  - [ ] B4.4.a Compare material assignments, shader/node types/versions,
        connections, parameter types/values/units, and unsupported mappings.
        Evidence: graph fixtures.
  - [ ] B4.4.b Compare texture identity/checksum, channels, resolution,
        sampler/wrap/filter, UDIM tiles, paths, and missing/substituted assets.
        Evidence: texture fixtures.
  - [ ] B4.4.c Compare declared/decoded color spaces, OCIO transforms, linear/
        display interpretation, alpha, and precision. Evidence: color goldens.
  - [ ] B4.4.d Render under pinned scene/camera/light/renderer/OCIO/settings and
        compute calibrated pixel/perceptual differences with masks. Evidence:
        target corpus.
  - [ ] B4.4.e Separate structural/material loss from renderer nondeterminism
        and threshold verdict. Evidence: diagnostic report.
- [ ] B4.5 Implement skeleton hierarchy, skin weight, bind pose, blend shape,
      and deformation measurements. Evidence: rig pairs.
  - [ ] B4.5.a Map source/target joints and compare hierarchy, names/aliases,
        rest/bind transforms, inverse binds, units/axes, and required bones.
        Evidence: skeleton fixtures.
  - [ ] B4.5.b Compare per-vertex joint influences/weights, normalization,
        truncation/remapping, and weighted deformation impact. Evidence:
        skinning goldens.
  - [ ] B4.5.c Compare blend-shape targets/in-betweens/deltas/normals,
        assignments, weights, ranges, and combination behavior. Evidence:
        blend-shape fixtures.
  - [ ] B4.5.d Evaluate deformed geometry under a pinned representative pose/
        expression corpus and report per-vertex/surface error. Evidence: rig
        pair report.
- [ ] B4.6 Implement animation curve, key, timing, interpolation, root motion,
      and event measurements. Evidence: animation pairs.
  - [ ] B4.6.a Align clips/tracks/channels by mapping, time codes, rates,
        ranges, offsets, loops, layers, and coordinate basis. Evidence:
        alignment fixtures.
  - [ ] B4.6.b Compare key times/values/tangents/weights/interpolation,
        resampling, infinity/extrapolation, and baked versus authored behavior.
        Evidence: curve goldens.
  - [ ] B4.6.c Measure sampled position/rotation/scale and derivative error over
        time with discontinuity and precision handling. Evidence: sample
        fixtures.
  - [ ] B4.6.d Compare root trajectory/orientation, contacts, markers/events,
        clip metadata, duration, and loop seams. Evidence: motion pairs.
  - [ ] B4.6.e Report exact channels/ranges and declared bake/loss separately
        from verdict. Evidence: animation diagnostics.
- [ ] B4.7 Implement camera, light, scene, variant, metadata, and dependency
      preservation measurements. Evidence: scene fixtures.
  - [ ] B4.7.a Compare camera projection, filmback/aperture, focal/lens/
        clipping/focus, transform, framing, and animation. Evidence: camera
        fixtures.
  - [ ] B4.7.b Compare light type, transform, intensity/exposure, color/
        temperature, shape, shadow, linking, and unsupported attributes.
        Evidence: light fixtures.
  - [ ] B4.7.c Compare scene hierarchy, purpose/visibility, collections,
        variants/selections, layer composition, and default state. Evidence:
        scene fixtures.
  - [ ] B4.7.d Compare governed metadata/custom properties/namespaces with
        rename/coercion/drop diagnostics. Evidence: metadata fixtures.
  - [ ] B4.7.e Compare dependency identities/versions/checksums/resolution,
        optionality, packaging, and license/rights propagation. Evidence:
        dependency fixtures.
- [ ] B4.8 Implement target build/import/runtime smoke validation. Evidence:
      asset opens and loads in supported target version.
  - [ ] B4.8.a Provision the exact supported target host/runtime/toolchain/
        plugin version and isolated project fixture. Evidence: environment
        manifest.
  - [ ] B4.8.b Import/open the exact artifact/package through the public adapter
        and capture warnings/errors, duration, and created object refs.
        Evidence: import trace.
  - [ ] B4.8.c Build/cook/package where required and launch/load in the target
        runtime with bounded health/crash/log checks. Evidence: runtime trace.
  - [ ] B4.8.d Inspect expected objects, dependencies, materials, animation,
        scripts/assets, and entry scene/state after load. Evidence: smoke
        assertions.
  - [ ] B4.8.e Classify unavailable/incompatible versus asset/build/runtime
        failure and clean the fixture. Evidence: state matrix.
- [ ] B4.9 Implement round-trip comparison through source→USD→target→USD where
      supported. Evidence: target matrix.
  - [ ] B4.9.a Lock source bytes/revision, source adapter, USD profile, target
        adapter/host, mappings, configuration, and declared loss. Evidence:
        round-trip manifest.
  - [ ] B4.9.b Convert source to canonical USD, import/write target state, and
        export/read back to a separately identified canonical USD stage.
        Evidence: execution trace.
  - [ ] B4.9.c Compare source, first USD, target inspection, and returned USD
        across every applicable typed measure. Evidence: four-way report.
  - [ ] B4.9.d Attribute deviations to source conversion, target import, target
        export, declared loss, or nondeterminism where possible. Evidence:
        diagnostic attribution.
  - [ ] B4.9.e Publish supported/unavailable/unsupported pair cells and prevent
        one target's evidence from satisfying another. Evidence: target matrix.
- [ ] B4.10 Persist raw measures separately from threshold verdicts. Evidence:
      policy recomputation.
  - [ ] B4.10.a Persist immutable measure set with exact source/target
        revisions, algorithm/version/config, units/basis, raw values, ranges,
        uncertainty, and artifact report. Evidence: measure schema.
  - [ ] B4.10.b Persist verdict with exact profile/version, thresholds/rules,
        measurement refs, status/reasons, freshness, and reviewer/waiver refs.
        Evidence: verdict schema.
  - [ ] B4.10.c Re-evaluate unchanged measures under new policy and retain both
        historical and current verdicts. Evidence: recomputation test.
- [ ] B4.11 Version adapters, DCC/engine versions, validators, profiles,
      thresholds, render settings, and corpus. Evidence: result provenance.
  - [ ] B4.11.a Register immutable version/checksum/compatibility records for
        every adapter, host/plugin, validator, profile, threshold set, render
        configuration, and fixture corpus. Evidence: registries.
  - [ ] B4.11.b Persist the complete version tuple, runtime/environment, and
        selected fixture/ground-truth revisions on results. Evidence: provenance
        query.
  - [ ] B4.11.c Detect revoked, stale, expired, unsupported, or mutually
        incompatible dependencies and enumerate affected evidence/releases.
        Evidence: impact tests.
- [ ] B4.12 Add unavailable-host, unsupported-feature, low-confidence,
      insufficient-comparison, and validator-failure states. Evidence: none
      renders as pass.
  - [ ] B4.12.a Define typed non-pass statuses, reason codes, diagnostic fields,
        retryability, owner, permitted action, and release effect. Evidence:
        state contract.
  - [ ] B4.12.b Emit unavailable-host and validator-failure for infrastructure/
        tool inability rather than asset quality failure. Evidence: outage
        tests.
  - [ ] B4.12.c Emit unsupported-feature, insufficient-comparison, and low-
        confidence with exact affected features/measurements. Evidence:
        applicability tests.
  - [ ] B4.12.d Prove API, UI, automation, waiver, and release paths never
        coerce a non-pass state to pass. Evidence: fail-closed suite.
- [ ] B4.13 Build representation preview with synchronized source/USD/target
      camera and selection. Evidence: accessible comparison.
  - [ ] B4.13.a Load authorized immutable source/USD/target representations and
        resolve comparable object mappings, cameras, units/axes, and time.
        Evidence: preview contract.
  - [ ] B4.13.b Synchronize camera, framing, transport/time, variant/layer,
        object/component selection, isolation, and overlays across views.
        Evidence: interaction tests.
  - [ ] B4.13.c Support side-by-side, overlay/difference, A/B, and target
        switching while retaining exact revision/profile identity. Evidence:
        comparison journey.
  - [ ] B4.13.d Provide semantic hierarchy/property/measurement/difference
        tables and keyboard controls equivalent to visual selection. Evidence:
        accessibility parity.
  - [ ] B4.13.e Surface unavailable renderer/host, loading, stale mapping,
        partial assets, and unsupported previews honestly. Evidence: state
        matrix.
- [ ] B4.14 Build layer/dependency/metadata/measurement diffs with semantic
      tables. Evidence: non-visual parity.
  - [ ] B4.14.a Select exact comparable revisions and compute stable path/
        identity mappings with ambiguity surfaced. Evidence: diff setup tests.
  - [ ] B4.14.b Diff layer stack/opinions/composition/variants and dependency
        add/remove/change/resolve/license state. Evidence: structural fixtures.
  - [ ] B4.14.c Diff names/namespaces/metadata/custom properties and typed raw
        measurement sets/verdicts. Evidence: property fixtures.
  - [ ] B4.14.d Present filterable add/remove/change/move/rename/conflict/
        unavailable rows with before/after, source, impact, and exact location.
        Evidence: semantic table.
  - [ ] B4.14.e Synchronize table focus with visual object/property/layer and
        prove every visual difference is represented non-visually. Evidence:
        parity journey.
- [ ] B4.15 Build image/render diff with calibrated settings and explicit
      nondeterminism. Evidence: known render corpus.
  - [ ] B4.15.a Lock scene/artifact/camera/frame, renderer/version, hardware,
        OCIO, resolution, sampling, lights, random seeds, and output transforms.
        Evidence: render manifest.
  - [ ] B4.15.b Render source/reference and target with deterministic settings
        where supported and record stochastic/runtime variation otherwise.
        Evidence: repeated-render study.
  - [ ] B4.15.c Align images and compute raw absolute/relative/color/perceptual/
        edge/region metrics with masks and uncertainty. Evidence: analytical
        image fixtures.
  - [ ] B4.15.d Calibrate tolerances against the known corpus and distinguish
        structural conversion loss from renderer noise. Evidence: calibration
        report.
  - [ ] B4.15.e Provide accessible image descriptions/region tables and exact
        settings/limitations with verdict. Evidence: comparison report.
- [ ] B4.16 Build exception/waiver with exact loss, scope, target, expiry, and
      approver. Evidence: incompatible release cannot reuse waiver.
  - [ ] B4.16.a Define waivable and prohibited gates/losses plus required role,
        independent approver, rationale, mitigation, expiry, and evidence.
        Evidence: waiver policy.
  - [ ] B4.16.b Bind a request to exact source/target/artifact/profile/measure/
        verdict/loss/target-purpose revisions and bounded objects/features.
        Evidence: waiver schema.
  - [ ] B4.16.c Review, approve/reject/condition, sign, audit, notify, and
        surface the exception in preview/package/release evidence. Evidence:
        workflow test.
  - [ ] B4.16.d Invalidate on any material input, target/version, measurement,
        policy, scope, purpose, or expiry change. Evidence: reuse negative test.

### B5 — Artifact library, builds, evidence, and release

- [ ] B5.1 Build artifact library search by asset type, representation, version,
      source/target, rights, quality, lineage, status, project, and owner.
      Evidence: permission-aware query tests.
  - [ ] B5.1.a Define the searchable projection and approved fields from
        authoritative artifact/representation/rights/quality/lineage/lifecycle
        records. Evidence: index contract.
  - [ ] B5.1.b Enforce tenant/project/object/property/rights authorization
        before query, facets, counts, suggestions, snippets, and previews.
        Evidence: inference tests.
  - [ ] B5.1.c Implement exact/full-text search, filters, facets, sort,
        pagination, and version/source-target/dependency queries. Evidence:
        query fixtures.
  - [ ] B5.1.d Reconcile index state after new version, supersession,
        quarantine, revoke, retirement, restore, and deletion. Evidence:
        lifecycle tests.
- [ ] B5.2 Build exact and structural dedupe with non-destructive merge
      decisions. Evidence: retained aliases and lineage.
  - [ ] B5.2.a Detect exact duplicates from normalized bytes/checksums while
        preserving distinct ownership, rights, and source records. Evidence:
        exact fixtures.
  - [ ] B5.2.b Detect structural candidates from versioned hierarchy/geometry/
        material/rig/dependency signatures with compatibility and uncertainty.
        Evidence: structural fixtures.
  - [ ] B5.2.c Compare representations, versions, rights/licenses, quality,
        lineage, dependencies, releases, and exact differences. Evidence: review
        journey.
  - [ ] B5.2.d Record keep-both, relate, alias, or consolidate-reference
        decisions without deleting immutable artifacts or provenance. Evidence:
        non-destructive tests.
- [ ] B5.3 Build artifact version, branch, supersession, deprecation,
      quarantine, retirement, and restore. Evidence: lifecycle tests.
  - [ ] B5.3.a Define immutable versions, mutable branch pointers, parent/
        ancestry, merge provenance, semantic diff, and optimistic concurrency.
        Evidence: version fixtures.
  - [ ] B5.3.b Define distinct superseded/deprecated/quarantined/retired/
        deleted/restored effects on visibility, selection, execution, release,
        dependencies, and retention. Evidence: state machine.
  - [ ] B5.3.c Authorize transitions with reason, effective time, replacement,
        impact preview, audit, and downstream propagation. Evidence: transition
        tests.
  - [ ] B5.3.d Restore only eligible retained bytes/metadata into a new current
        revision/state and revalidate dependencies/rights/quality. Evidence:
        restore journey.
- [ ] B5.4 Build collections, kits, dependencies, related assets, and release
      sets through versioned curation. Evidence: bundle golden.
  - [ ] B5.4.a Define typed versioned curation operations for membership,
        ordering, roles, kits, dependency pins, related edges, metadata, and
        release-set state. Evidence: bundle schema.
  - [ ] B5.4.b Validate refs/versions, cycles, compatibility, rights/license,
        quality, lifecycle, permissions, and release completeness. Evidence:
        preflight tests.
  - [ ] B5.4.c Review and apply an exact bundle atomically/idempotently with
        concurrency, per-operation result, audit, and compensation. Evidence:
        bundle golden.
  - [ ] B5.4.d Read back collection/kit/dependency/release projections and
        prohibit direct production writes outside the applier. Evidence:
        authority tests.
- [ ] B5.5 Build build-template registry, typed parameters, lifecycle, and
      compatibility. Evidence: template tests.
  - [ ] B5.5.a Register immutable template versions with owner/purpose, target/
        profile ranges, toolchain/dependency locks, typed parameters, resources,
        stages, gates, and output contracts. Evidence: registry tests.
  - [ ] B5.5.b Validate defaults/bounds/conditional parameters, secret refs,
        command allowlists, host/target compatibility, and reproducibility.
        Evidence: schema fixtures.
  - [ ] B5.5.c Review, approve, activate, deprecate, supersede, revoke, and
        migrate templates with immutable historical use. Evidence: lifecycle
        journey.
  - [ ] B5.5.d Compute affected builds/releases and block selection of
        incompatible, revoked, or stale templates. Evidence: impact tests.
- [ ] B5.6 Build batch/build scheduling with host, worker, license, storage,
      render, and reviewer capacity. Evidence: reservation conflict tests.
  - [ ] B5.6.a Model resource pools/capabilities/locality, calendars/
        maintenance, quotas, licenses/tokens, storage/egress, reviewers, and
        time zones. Evidence: capacity schema.
  - [ ] B5.6.b Estimate exact per-stage demand, duration, dependencies,
        affinity, deadlines, cost, and alternative compatible resources.
        Evidence: scheduling inputs.
  - [ ] B5.6.c Create tentative holds and committed fenced reservations
        transactionally across all required resource types. Evidence:
        reservation tests.
  - [ ] B5.6.d Detect overlap, version/capability, quota, license, storage,
        dependency, setup, and reviewer conflicts. Evidence: conflict matrix.
  - [ ] B5.6.e Reschedule/cancel/expire/release reservations and reconcile
        queued/running work without leaks or double allocation. Evidence:
        lifecycle tests.
- [ ] B5.7 Build batch preview with eligibility, losses, cost, capacity,
      dependencies, and gates. Evidence: mixed-result journey.
  - [ ] B5.7.a Resolve a stable artifact/query snapshot, requested template/
        parameters/targets, caller authority, and current policy. Evidence:
        batch contract.
  - [ ] B5.7.b Evaluate per-item version/rights/lifecycle/dependency/format/
        host/profile/gate compatibility and expected semantic loss. Evidence:
        eligibility fixtures.
  - [ ] B5.7.c Estimate per-item and aggregate stage/resource/license/storage/
        duration/cost/capacity with uncertainty. Evidence: estimate tests.
  - [ ] B5.7.d Present included/skipped/blocked/warning items with exact
        reasons, dependencies, losses, gates, and schedule options. Evidence:
        preview journey.
  - [ ] B5.7.e Execute only an explicitly approved exact preview and report
        per-item progress/partial/failure/compensation. Evidence: mixed-result
        test.
- [ ] B5.8 Build release candidate locked to exact artifact, layer, dependency,
      template, toolchain, host, adapter, validator, gate, and approval
      versions. Evidence: mutation invalidation.
  - [ ] B5.8.a Resolve and lock the full transitive artifact/layer/dependency/
        mapping/template/parameter graph with versions/checksums. Evidence:
        candidate manifest.
  - [ ] B5.8.b Lock toolchain/environment/host/plugin/adapter/validator/profile/
        threshold/render/build target versions. Evidence: execution lock.
  - [ ] B5.8.c Lock raw measurements/verdicts, rights/licenses, security/SBOM,
        approvals/waivers, limitations, and target plan. Evidence: assurance
        lock.
  - [ ] B5.8.d Detect any changed, stale, revoked, expired, missing,
        quarantined, or incompatible lock dependency and invalidate readiness.
        Evidence: mutation tests.
  - [ ] B5.8.e Rebuild a new candidate rather than updating a locked one.
        Evidence: immutability test.
- [ ] B5.9 Build tamper-evident release-evidence assembly. Evidence:
      deterministic hash and signature validation.
  - [ ] B5.9.a Define canonical manifest ordering/serialization and include
        candidate locks, artifacts, commands, builds, gates, approvals,
        limitations, targets, and read-back. Evidence: manifest schema.
  - [ ] B5.9.b Resolve every evidence ref to immutable bytes/checksum or signed
        external record and reject missing/ambiguous refs. Evidence: assembly
        tests.
  - [ ] B5.9.c Build deterministically, compute manifest/Merkle or equivalent
        integrity hashes, sign with authorized release key, and timestamp.
        Evidence: repeated-build hash test.
  - [ ] B5.9.d Independently verify schema, refs/checksums, signature/key
        status, authorization, and candidate identity. Evidence: verifier suite.
  - [ ] B5.9.e Preserve superseded/revoked evidence and publish status without
        allowing artifact replacement under the same ref. Evidence: tamper
        tests.
- [ ] B5.10 Build target export/deploy with plan, execute, read-back, and
      compensate. Evidence: partial failure recovery.
  - [ ] B5.10.a Build a read-only target plan with exact candidate, destination,
        files/objects/actions, prerequisites, permissions, conflicts, cost,
        downtime, and rollback/compensation. Evidence: plan fixture.
  - [ ] B5.10.b Revalidate candidate/target/credentials/capacity immediately
        before execution and authorize the exact plan revision. Evidence:
        stale-plan tests.
  - [ ] B5.10.c Execute typed idempotent steps with checkpoints, target
        revisions, bounded logs, progress, cancel, and immutable attempt audit.
        Evidence: deployment tests.
  - [ ] B5.10.d Read back target objects/files/versions/checksums/state through
        an independent inspection path. Evidence: verification report.
  - [ ] B5.10.e On partial failure, stop safely and retry/roll back/compensate
        per step while exposing unresolved target state. Evidence: recovery
        journey.
- [ ] B5.11 Build target-specific post-deploy smoke and fidelity checks.
      Evidence: transport success alone cannot release.
  - [ ] B5.11.a Select the exact target/version/profile and its required import/
        load/build/runtime/scenario/fidelity check registry. Evidence: target
        test plan.
  - [ ] B5.11.b Execute target-native open/import/load and dependency/
        configuration/entry-state smoke checks. Evidence: smoke results.
  - [ ] B5.11.c Execute target-specific structural, semantic, visual/runtime,
        performance, and security checks with raw measurements. Evidence:
        fidelity results.
  - [ ] B5.11.d Distinguish unavailable infrastructure from deployment or
        artifact failure and prevent transport-only success from release.
        Evidence: state tests.
  - [ ] B5.11.e Attach exact target read-back and current verdicts to release
        evidence. Evidence: evidence linkage.
- [ ] B5.12 Build rollback, supersession, revoke, quarantine, and recall.
      Evidence: dependent projects and targets reconcile.
  - [ ] B5.12.a Define distinct source/library/build/release/target/consumer
        effects, authorities, prerequisites, and terminal criteria for each
        action. Evidence: lifecycle policy.
  - [ ] B5.12.b Traverse exact artifacts/versions/dependencies/builds/packages/
        deployments/projects/targets/consumers and preview blast radius.
        Evidence: impact graph.
  - [ ] B5.12.c Execute the authorized saga with fencing, checkpoints, per-
        dependency result, retry/compensation, notifications, and audit.
        Evidence: propagation tests.
  - [ ] B5.12.d Verify target and dependent-project state by read-back and keep
        partial/unreachable recipients unresolved. Evidence: reconciliation
        report.
  - [ ] B5.12.e Restore or supersede only through a new validated candidate and
        approval path. Evidence: recovery tests.
- [ ] B5.13 Build artifact retention and purge with dependency/legal-hold
      preview. Evidence: referenced artifacts remain.
  - [ ] B5.13.a Calculate retention/expiry eligibility from artifact class,
        project/rights/license, release/evidence policy, deletion request, and
        legal hold. Evidence: policy tests.
  - [ ] B5.13.b Traverse versions, branches, dependencies, packages, releases,
        targets, caches, backups, and audit/evidence refs before purge.
        Evidence: dependency preview.
  - [ ] B5.13.c Classify deletable, retained, tombstoned, anonymized, blocked,
        and separately approved objects with exact reasons. Evidence: purge
        plan.
  - [ ] B5.13.d Execute authorized bounded purge with checkpoints, per-store
        confirmation, immutable tombstone/evidence, and retry. Evidence: purge
        journey.
  - [ ] B5.13.e Prove referenced or legally held bytes remain and unreferenced
        eligible bytes become inaccessible across active stores/caches.
        Evidence: read-back tests.
- [ ] B5.14 Build release notifications and audit export with safe redaction.
      Evidence: authorization tests.
  - [ ] B5.14.a Define notification event types, minimal payloads, recipients/
        subscriptions, severity, locale, dedupe, and escalation. Evidence:
        notification contract.
  - [ ] B5.14.b Deliver release/failed/revoked/recalled/partial state through
        governed channels with retries, preference/policy handling, and audit.
        Evidence: delivery tests.
  - [ ] B5.14.c Define audit export scope, query filters, exact time/object
        refs, schema/version, pagination/chunking, signature, retention, and
        purpose. Evidence: export contract.
  - [ ] B5.14.d Authorize object/property visibility and redact secrets,
        credentials, local paths, content, sensitive metadata, and unauthorized
        existence. Evidence: redaction matrix.
  - [ ] B5.14.e Verify recipient/download identity, expiry, checksums, and
        reproducible query manifest. Evidence: authorization tests.
- [ ] B5.15 Build UI-governed asset, representation, schema, material, rig,
      animation, target, build, fidelity, and catalog taxonomies over code-owned
      floors. Evidence: versioned review, alias, deprecation, and migration
      journey.
  - [ ] B5.15.a Define independent taxonomy schemas, owners, code-owned
        invariants/floors, allowed relations, and consumers. Evidence: registry.
  - [ ] B5.15.b Author draft terms with stable IDs, localized labels/
        definitions, aliases, mappings, constraints, and migration notes.
        Evidence: authoring tests.
  - [ ] B5.15.c Review, approve, publish, deprecate, supersede, retire, and
        restore versions without reinterpreting historical records. Evidence:
        lifecycle journey.
  - [ ] B5.15.d Reject collisions, cycles, incompatible mappings, floor
        violations, and referenced-term deletion. Evidence: validation suite.
  - [ ] B5.15.e Migrate refs only through previewed, authorized, resumable jobs
        with per-record result and rollback/compensation. Evidence: migration
        tests.
- [ ] B5.16 Build taxonomy/profile impact over templates, sessions, artifacts,
      validators, search, builds, targets, and releases. Evidence: read-only
      preview precedes an authorized migration job.
  - [ ] B5.16.a Build a complete versioned reference index from taxonomy/profile
        concepts to templates, active sessions, artifacts, validators, search,
        builds, targets, releases, docs, and clients. Evidence: reference audit.
  - [ ] B5.16.b Preview changed eligibility/mapping/validation/search/runtime/
        release behavior, incompatible refs, active-operation risk, and cost
        without mutation. Evidence: impact report.
  - [ ] B5.16.c Require authorized approval of exact source/destination versions
        and preview revision before scheduling. Evidence: stale-preview test.
  - [ ] B5.16.d Execute/checkpoint/resume/retry/partially fail/compensate the
        migration and read back all projections. Evidence: migration journey.

### B5A — Specialized production, runtime, and automation operations

- [ ] B5A.1 Complete per-host typed command registries for Blender, Maya,
      Houdini, 3ds Max, Unreal, Unity, Godot, and DaVinci, including selection,
      scene/entity, asset/import/export, material, rig/animation, camera/light,
      render/build, undo/transaction, diagnostics, capability, and version
      behavior. Evidence: no generic "bridge works" proof substitutes for
      command-by-command host conformance.
  - [ ] B5A.1.a Define the shared command-definition/conformance schema with
        exact host/plugin versions, typed inputs/results, prerequisites,
        permissions, side effects, undo/compensation, limits, and diagnostics.
        Evidence: registry schema.
  - [ ] B5A.1.b Complete and validate Blender and Maya command registries across
        every named command category. Evidence: independent host matrices.
  - [ ] B5A.1.c Complete and validate Houdini and 3ds Max command registries
        across every named command category. Evidence: independent host
        matrices.
  - [ ] B5A.1.d Complete and validate Unreal and Unity command registries across
        every named command category. Evidence: independent host matrices.
  - [ ] B5A.1.e Complete and validate Godot and DaVinci command registries
        across applicable command categories with explicit unsupported cells.
        Evidence: independent host matrices.
  - [ ] B5A.1.f Test each host-command-version row for success, invalid input,
        unsupported capability, authorization, stale revision, idempotency,
        cancellation, compensation, crash/reconnect, and audit. Evidence:
        command-by-command conformance.
  - [ ] B5A.1.g Prohibit generic bridge health from satisfying any untested
        command row. Evidence: release coverage gate.
- [ ] B5A.2 Build GPU render-queue operations for scene/version intake,
      renderer/profile, frame ranges, passes, dependencies, color, resources,
      licenses, priority/fairness, preview, cost, progress, pause/cancel/retry,
      checkpoint, output QC, review, and publish. Evidence: partial-frame and
      worker/host-loss recovery.
  - [ ] B5A.2.a Lock scene/camera/layer/variant/dependency revisions and select
        renderer/version/profile, frame/range/handles, passes, outputs, OCIO,
        and destination. Evidence: render manifest.
  - [ ] B5A.2.b Validate dependencies, rights, renderer/plugin compatibility,
        target/output rules, and pre-render scene/color gates. Evidence:
        preflight suite.
  - [ ] B5A.2.c Estimate and preview frames/tasks, GPU/CPU/memory/storage/
        bandwidth/licenses, priority/fairness, schedule, duration, and cost.
        Evidence: queue plan.
  - [ ] B5A.2.d Reserve capacity and dispatch idempotent frame/pass tasks with
        fenced leases, checkpoint/resume, bounded retries, and backpressure.
        Evidence: scheduler tests.
  - [ ] B5A.2.e Stream bounded progress/thumbnails/logs and support authorized
        pause/resume/cancel/retry/reprioritize without hidden late output.
        Evidence: operations journey.
  - [ ] B5A.2.f Aggregate ordered frames/passes, detect
        missing/duplicate/corrupt output, verify color/naming/format/checksums,
        and run calibrated QC. Evidence: output-quality suite.
  - [ ] B5A.2.g Review/approve/publish exact outputs with artifact lineage and
        target read-back. Evidence: release journey.
  - [ ] B5A.2.h Recover worker/host/GPU/license/storage loss and partial frames/
        passes without false completion. Evidence: fault drill.
- [ ] B5A.3 Build gameplay-system operations for input/action maps, save/load
      schemas and migrations, inventory/items, combat/damage, AI behavior/
      navigation, validation scenarios, target builds, runtime telemetry, and
      rollback. Evidence: deterministic or explicitly stochastic engine tests
      for every supported target/version.
  - [ ] B5A.3.a Register versioned gameplay schemas/configuration for input/
        actions, saves, inventory/items, combat/damage, behavior/AI, navigation,
        and state with one authoritative source. Evidence: gameplay registry.
  - [ ] B5A.3.b Author changes through typed commands with exact project/object
        revisions, semantic diff, validation, authorization, and undo/
        compensation. Evidence: mutation tests.
  - [ ] B5A.3.c Implement save upgrade/downgrade/rollback fixtures and validate
        integrity, compatibility, data preservation, and failure recovery.
        Evidence: save migration matrix.
  - [ ] B5A.3.d Define allowlisted deterministic test scenarios with seeded or
        explicitly stochastic setup, actions, timing, assertions, cleanup, and
        telemetry. Evidence: scenario schema.
  - [ ] B5A.3.e Build/package/deploy every supported engine target/version and
        execute input, save, inventory, combat, AI, navigation, and state
        scenarios independently. Evidence: target matrix.
  - [ ] B5A.3.f Capture bounded runtime correctness/performance telemetry and
        target read-back tied to source/build revisions. Evidence: result
        manifests.
  - [ ] B5A.3.g Roll back build/config/save migrations and prove prior target
        state remains viable. Evidence: rollback drill.
- [ ] B5A.4 Build virtual-production operations for camera/lens tracking,
      calibration, LED-wall configuration, ICVFX layers/frusta, genlock/
      timecode, playback, take/recording, health, latency, color, failover,
      emergency stop, and dailies handoff. Evidence: stage-safe simulation and
      supported-device/host matrix.
  - [ ] B5A.4.a Enroll and map stage, camera/tracker/lens, wall/processor,
        render nodes, clocks, recording, and safety devices to supported
        profiles. Evidence: device inventory.
  - [ ] B5A.4.b Calibrate camera/tracker/lens/world/wall/color systems with
        versioned measurements, uncertainty, validity, drift, and approval.
        Evidence: calibration suite.
  - [ ] B5A.4.c Configure and preview exact content/layer/variant, camera,
        frustum, wall/color, genlock/timecode, playback, recording, and resource
        plan. Evidence: session plan.
  - [ ] B5A.4.d Start/supervise/stop playback and takes with synchronized state,
        health/latency/color/sync warnings, bounded controls, and immutable take
        identity. Evidence: supervision journey.
  - [ ] B5A.4.e Enforce device-safe interlocks, failover/fallback, emergency
        stop, late command fencing, and incident evidence. Evidence: stage-safe
        simulation.
  - [ ] B5A.4.f Package recorded media/metadata/calibration/timecode/content
        lineage and hand off dailies through governed editorial seams. Evidence:
        dailies read-back.
  - [ ] B5A.4.g Validate each supported device/host/version combination
        independently with unavailable cells explicit. Evidence: matrix.
- [ ] B5A.5 Build XR operations for shared capabilities plus visionOS, Meta
      Quest, and every ratified platform: project settings, spatial assets,
      interactions/input, comfort/accessibility, device/runtime compatibility,
      performance budgets, package/sign, install, launch, smoke, telemetry, and
      rollback. Evidence: platform-specific validation; no inherited desktop
      build proof.
  - [ ] B5A.5.a Register each XR platform/device/OS/runtime/SDK profile,
        capabilities, entitlements, project/package/signing requirements,
        performance/comfort/accessibility budgets, and test devices. Evidence:
        XR registry.
  - [ ] B5A.5.b Validate and configure project settings, render path,
        permissions, spatial assets/anchors, input/actions,
        interaction/locomotion, and accessibility per exact profile. Evidence:
        project tests.
  - [ ] B5A.5.c Preview device/runtime compatibility, unsupported features,
        entitlements, assets, build settings, secrets/signing, cost, and target
        limitations. Evidence: deployment plan.
  - [ ] B5A.5.d Build/sign/package, install, launch, and inspect exact versions
        on visionOS, Meta Quest, and every other ratified platform separately.
        Evidence: platform journeys.
  - [ ] B5A.5.e Run platform-native input/tracking/spatial/interaction, comfort,
        accessibility, frame-time/memory/thermal, lifecycle, and offline smoke
        scenarios. Evidence: validation matrix.
  - [ ] B5A.5.f Collect bounded telemetry/read-back and roll back/uninstall/
        restore prior target state. Evidence: rollback tests.
  - [ ] B5A.5.g Prohibit desktop or another XR platform's evidence from
        satisfying an untested profile. Evidence: independence gate.
- [ ] B5A.6 Build motion-capture operations for vendor streams, clocks, skeleton
      mapping, subject/consent refs, retargeting, recording/playback, format
      conversion, animation export, health, dropouts, and Aja handoff. Evidence:
      consent/provenance and live network fault journeys.
  - [ ] B5A.6.a Register vendor/device/firmware/protocol/plugin profiles,
        modalities, skeleton/channel schemas, clock/sync, capability, and
        network/security requirements. Evidence: mocap registry.
  - [ ] B5A.6.b Enroll session/devices/subjects with current consent/purpose,
        operator, location, calibration, skeleton mapping, target rig, and Aja
        handoff scope. Evidence: admissibility tests.
  - [ ] B5A.6.c Connect and monitor signed streams, clock skew, packet order/
        loss, latency, confidence, health, dropouts, reconnect, and modality
        degradation. Evidence: live supervision suite.
  - [ ] B5A.6.d Record immutable raw and normalized streams with timecode,
        subject-safe refs, configuration, mappings, checksums, and interruption
        markers; support synchronized playback. Evidence: recording tests.
  - [ ] B5A.6.e Retarget/convert/export through versioned mappings and formats
        with loss preview, quality/consent gates, provenance, and read-back.
        Evidence: conversion journey.
  - [ ] B5A.6.f Handoff to Aja only through its canonical contract with
        idempotency, health/degraded states, ownership, and recall propagation.
        Evidence: seam tests.
  - [ ] B5A.6.g Inject network/device/clock/host loss, consent revocation, and
        late packets; prove explicit partial state and safe recovery. Evidence:
        fault journeys.
- [ ] B5A.7 Build MetaHuman operations for mesh/identity import, face rig,
      body/customization, skeleton/material compatibility, Live Link, animation,
      rights/likeness, versioning, performance, package, and Unreal validation.
      Evidence: identity/consent and supported-Unreal-version gates.
  - [ ] B5A.7.a Register supported Unreal/MetaHuman/plugin/DNA/rig/Live Link
        versions and exact host/tool compatibility. Evidence: profile registry.
  - [ ] B5A.7.b Admit mesh/identity/reference sources only with current rights,
        likeness/subject consent, intended use, retention, and protected-data
        controls. Evidence: identity gate.
  - [ ] B5A.7.c Import/create immutable MetaHuman identity, mesh, DNA, face/body
        rig, skeleton, materials, clothing/customization, and dependency
        versions with lineage. Evidence: asset manifest.
  - [ ] B5A.7.d Validate geometry/deformation, skeleton/animation/blend shapes,
        material/clothing, target platform, and current Unreal compatibility.
        Evidence: compatibility suite.
  - [ ] B5A.7.e Configure/supervise Live Link identity/source/subjects,
        channels/timecode, confidence, recording, animation transfer, dropouts,
        and revoke. Evidence: live journey.
  - [ ] B5A.7.f Measure editor/runtime frame time, memory, LOD, animation/
        rendering quality, and target budgets. Evidence: performance report.
  - [ ] B5A.7.g Package/build/open/read back in every supported Unreal version
        and propagate rights/consent/version quarantine or recall. Evidence:
        release gates.
- [ ] B5A.8 Build DaVinci operations for project/timeline exchange, media/
      proxy, edit/conform, color/OCIO, markers, audio, render/deliver, relink,
      version, and round-trip diagnostics. Evidence: locked timeline and
      media-hash fixtures.
  - [ ] B5A.8.a Register supported Resolve/version/platform/project/timeline/
        interchange/color/audio/render profiles and plugin capabilities.
        Evidence: compatibility registry.
  - [ ] B5A.8.b Lock project/timeline/bin/clip/media revisions, hashes,
        timecode/rates, proxies, edit decisions, markers, audio, and OCIO/color
        context before exchange. Evidence: timeline manifest.
  - [ ] B5A.8.c Import/export/conform through typed commands with mapping,
        handles, transitions/effects, speed changes, multicam, grades, audio,
        and unsupported-loss preview. Evidence: conform fixtures.
  - [ ] B5A.8.d Relink originals/proxies by approved identity/hash/path mapping
        and reject ambiguous, missing, or changed media. Evidence: relink tests.
  - [ ] B5A.8.e Render/deliver exact timeline/ranges/presets with color/audio/
        subtitle/data-level metadata, checksums, review, and read-back.
        Evidence: delivery journey.
  - [ ] B5A.8.f Round-trip and compare timeline structure, timing, media hashes,
        markers, color/OCIO, audio, and declared loss. Evidence: diagnostic
        matrix.
- [ ] B5A.9 Build Houdini operations for HDA definition/version/parameter,
      dependency, cook/cache, geometry/USD, simulation, output, farm execution,
      diagnostics, and round-trip. Evidence: deterministic seed/toolchain or
      explicit nondeterminism and license/capacity handling.
  - [ ] B5A.9.a Register Houdini/Engine/plugin/renderer/HDA versions, platforms,
        licenses, farm/runtime compatibility, and supported node/format
        capabilities. Evidence: profile registry.
  - [ ] B5A.9.b Ingest and verify HDA definition/library/version/signature,
        typed parameters/defaults/expressions, dependencies, permissions, and
        source provenance. Evidence: HDA contract.
  - [ ] B5A.9.c Plan cook/simulation with exact input geometry/USD, parameter
        revision, frames, seeds, caches, environment/toolchain, resources,
        licenses, outputs, cost, and nondeterminism. Evidence: cook plan.
  - [ ] B5A.9.d Execute locally or on farm with fenced work items, checkpoint/
        cache, progress, pause/cancel/retry, worker loss, and license
        backpressure. Evidence: execution tests.
  - [ ] B5A.9.e Validate output geometry/USD/simulation fields, dependencies,
        frames, checksums, known values, diagnostics, and declared variation.
        Evidence: output QC.
  - [ ] B5A.9.f Round-trip supported inputs/outputs and prove deterministic
        replay by checksum/measure or document bounded nondeterminism. Evidence:
        replay report.
- [ ] B5A.10 Build audio/video asset operations for ingest, transcode, proxy,
      color/audio metadata, sync/timecode, renditions, engine/DCC attachment,
      review, package, and target validation. Evidence: A/V fidelity and rights
      lineage survive conversions.
  - [ ] B5A.10.a Ingest immutable A/V bytes with type/profile, checksum, source,
        rights/license/consent, streams, timecode, language, and provenance.
        Evidence: intake tests.
  - [ ] B5A.10.b Inspect codec/container/profile, resolution/rate/color/HDR,
        audio channels/rate/loudness/layout, captions/metadata, duration, and
        corruption. Evidence: media report.
  - [ ] B5A.10.c Plan and execute versioned transcode/proxy/rendition operations
        with exact tools/config, sync/timecode policy, color/audio transforms,
        loss, resources, and lineage. Evidence: conversion fixtures.
  - [ ] B5A.10.d Attach exact renditions to engine/DCC scene/timeline/assets
        through portable dependencies and typed commands. Evidence: attachment
        tests.
  - [ ] B5A.10.e Review synchronized picture/audio/captions/timecode and raw
        fidelity measurements with accessible alternatives. Evidence: review
        journey.
  - [ ] B5A.10.f Package and read back in each target, verifying bytes/streams/
        metadata/sync/color/audio and rights lineage. Evidence: target matrix.
- [ ] B5A.11 Build automatic cross-domain ingestion and lore-to-engine/
      text-to-3D compilation for Yemaya, Isis, Aja, Hathor, and other ratified
      producers through public contracts. Evidence: source entities, generated
      ingredients, consent/rights, compiler/toolchain, mapping decisions,
      artifacts, and target read-back remain traceable.
  - [ ] B5A.11.a Register each producer contract/version/capability,
        authoritative source entities/revisions, artifact types,
        rights/consent/canon fields, events, and ownership boundary. Evidence:
        producer matrix.
  - [ ] B5A.11.b Admit signed authorized immutable manifests idempotently and
        reject stale, revoked, unsupported, cross-tenant, or incomplete inputs.
        Evidence: intake contract tests.
  - [ ] B5A.11.c Normalize source entities and generated ingredients into
        versioned intermediate/target mappings with explicit alternatives,
        omissions, defaults, and human decisions. Evidence: mapping records.
  - [ ] B5A.11.d Compile through pinned compiler/toolchain/templates/adapters/
        models with seeds/config/environment, staged outputs, validation, and
        deterministic or disclosed-nondeterministic lineage. Evidence:
        compilation trace.
  - [ ] B5A.11.e Package/deploy and read back target entities/assets/state while
        retaining source IDs/revisions, canon, rights/consent, generated
        regions, mappings, and artifact checksums. Evidence: target journey.
  - [ ] B5A.11.f Propagate producer correction/revocation/recall and reconcile
        every compiled derivative/target. Evidence: downstream closure test.
- [ ] B5A.12 Build governed agent/MCP remote-control planning with
      natural-language intent translated into typed allowlisted commands, exact
      target/revision, dry-run diff, risk/cost/blast radius, required approval,
      least-privilege session, step execution, stop conditions, and immutable
      tool-call evidence. Evidence: there is no raw shell or unreviewed
      autonomous destructive path.
  - [ ] B5A.12.a Define supported agent/MCP identities, clients, model/tool
        versions, scopes, tenants, budgets, data policy, and prohibited
        capabilities. Evidence: agent policy.
  - [ ] B5A.12.b Parse intent into an immutable structured goal, assumptions,
        exact target/object/revision, constraints, success criteria, and
        uncertainty; require clarification on material ambiguity. Evidence:
        intent tests.
  - [ ] B5A.12.c Compile only to versioned typed allowlisted commands with
        explicit dependencies, expected revisions, permissions, idempotency,
        deadlines, compensation, and no shell/eval escape. Evidence: planner
        contract.
  - [ ] B5A.12.d Dry-run every step and present semantic diff, affected objects/
        paths, fidelity/security/release risk, cost/spend, capacity, blast
        radius, irreversible effects, and alternatives. Evidence: plan preview.
  - [ ] B5A.12.e Determine required human approvals/step-up/dual control and
        bind them to the exact immutable plan revision. Evidence: approval
        tests.
  - [ ] B5A.12.f Open a time/resource/command/object/path-bounded least-
        privilege host session and revalidate preconditions before each step.
        Evidence: grant tests.
  - [ ] B5A.12.g Execute sequential/parallel steps under declared dependencies,
        budgets, stop conditions, live status, cancellation, fencing, and human
        redirect checkpoints. Evidence: execution journey.
  - [ ] B5A.12.h Persist intent, plan/revisions, model/tool versions, approvals,
        normalized tool calls/results, diffs, costs, decisions, and final read-
        back with redaction. Evidence: immutable audit.
- [ ] B5A.13 Build agent pause, redirect, cancel, budget/spend kill,
      organization policy, host quarantine, replay, and partial-result
      reconciliation. Evidence: agents cannot waive fidelity/security/release
      gates or disguise a failed/partial command as complete.
  - [ ] B5A.13.a Define control authority and semantics for pause/resume,
        redirect/replan, cancel, spend kill, policy stop, and host quarantine.
        Evidence: control state machine.
  - [ ] B5A.13.b Enforce organization/tenant/project/model/tool/host/data/
        network/time/token/cost policies on plan admission and before each step.
        Evidence: policy tests.
  - [ ] B5A.13.c Pause at safe boundaries, redirect only through a new diff/
        risk/approval plan revision, and cancel with typed compensation/unknown
        outcomes. Evidence: intervention tests.
  - [ ] B5A.13.d Stop dispatch immediately on budget/spend limit or quarantine,
        fence late results, release reservations, and surface incurred cost/
        side effects. Evidence: kill-switch tests.
  - [ ] B5A.13.e Replay only eligible idempotent steps from locked evidence and
        require new approval when inputs/policy/risk changed. Evidence: replay
        tests.
  - [ ] B5A.13.f Reconcile every step/target by read-back and report succeeded,
        failed, cancelled, skipped, compensated, partial, or unknown separately.
        Evidence: partial-result journey.
  - [ ] B5A.13.g Enforce fidelity/security/rights/release gates outside agent
        authority and reject attempts to waive or relabel them. Evidence:
        adversarial suite.
- [ ] B5A.14 Complete package-builder and export operations for engine/DCC/XR/
      VP targets, native dependencies, plugins, content, configuration,
      licenses, SBOM, provenance, signatures, install/upgrade/uninstall, target
      smoke, and rollback. Evidence: reproducible release-evidence bundle per
      supported target.
  - [ ] B5A.14.a Register target-specific package formats/layouts, toolchains,
        native/runtime dependencies, plugins, content/config, permissions,
        signing/notarization, install lifecycle, and validators. Evidence:
        target profiles.
  - [ ] B5A.14.b Resolve and lock exact source artifacts, generated/native
        dependencies, plugins, configs, licenses, rights, target, toolchain, and
        build environment. Evidence: dependency manifest.
  - [ ] B5A.14.c Scan/validate components and generate normalized licenses/
        notices, SBOM, vulnerability results, provenance attestations, and
        policy verdicts. Evidence: supply-chain bundle.
  - [ ] B5A.14.d Build/package reproducibly where supported, compute per-file/
        package checksums, sign/notarize, and independently verify signatures.
        Evidence: reproducibility test.
  - [ ] B5A.14.e Install fresh, upgrade supported prior versions, preserve/
        migrate state, repair, uninstall cleanly, and handle dependency/
        permission failures. Evidence: lifecycle matrix.
  - [ ] B5A.14.f Launch/open and run target-specific smoke/fidelity/performance/
        security checks with read-back. Evidence: target journey.
  - [ ] B5A.14.g Roll back failed install/upgrade/release and prove prior target
        viability with no orphaned partial state. Evidence: rollback drill.
  - [ ] B5A.14.h Assemble one immutable release-evidence bundle per exact target
        profile/version; prohibit inherited proof. Evidence: coverage gate.
- [ ] B5A.15 Complete event-handler/publisher operations for asset, session,
      command, interchange, render, build, deployment, runtime, and recall
      events with schema/version registry, subscriptions, replay, dead letter,
      redaction, and consumer status. Evidence: cross-domain event conformance
      and no duplicate side effects.
  - [ ] B5A.15.a Register versioned minimal event schemas with stable event/
        aggregate IDs, revisions, ordering key, time, tenant, trace/causation,
        data classification, and compatibility. Evidence: event registry.
  - [ ] B5A.15.b Publish asset/session/command/interchange/render/build/
        deployment/runtime/recall events transactionally from authoritative
        state with no protected payload. Evidence: outbox tests.
  - [ ] B5A.15.c Create authorized subscriptions with event/filter/tenant scope,
        endpoint/topic, secret, delivery ordering, rate, retention, and
        lifecycle. Evidence: subscription tests.
  - [ ] B5A.15.d Sign and deliver with backoff, replay protection, idempotency,
        bounded payload/logging, secret rotation, and explicit consumer
        acknowledgement/status. Evidence: delivery suite.
  - [ ] B5A.15.e Dead-letter exhausted/invalid events with diagnostics and
        authorize bounded replay by exact range/version while preserving event
        identity. Evidence: replay journey.
  - [ ] B5A.15.f Reconcile consumer projections and prove duplicates/reordering/
        retries do not duplicate commands, builds, deployments, releases, or
        recalls. Evidence: conformance matrix.
- [ ] B5A.16 Complete TypeScript, Python, C++, CLI, remote-extension, and host
      plugin developer surfaces for capability discovery, auth/session,
      commands, assets, jobs, events, examples, diagnostics, compatibility,
      versioning, and deprecation. Evidence: generated public contracts and
      external-client conformance.
  - [ ] B5A.16.a Generate shared schemas/types and implement consistent
        endpoint, authentication, session, version negotiation, timeout/retry,
        idempotency, error, logging/redaction, and telemetry foundations.
        Evidence: core parity tests.
  - [ ] B5A.16.b Implement capability/format/host discovery and auth/enrollment/
        session lifecycle across TypeScript, Python, C++, CLI, remote extension,
        and host plugin. Evidence: discovery/session matrix.
  - [ ] B5A.16.c Implement typed command plan/execute/status/cancel/result,
        artifact transfer, job/batch, validation/release, and event subscription
        across applicable surfaces. Evidence: operation matrix.
  - [ ] B5A.16.d Provide executable examples for connect, inspect, interchange,
        build/render, release/read-back, failure recovery, and revoke without
        embedded secrets or unsafe commands. Evidence: example tests.
  - [ ] B5A.16.e Provide bounded diagnostics, support bundle, compatibility
        inspection, feature detection, and honest unsupported/degraded states.
        Evidence: diagnostic tests.
  - [ ] B5A.16.f Publish versions/changelogs/migration guides/deprecation
        notices and enforce compatibility windows in clients/servers. Evidence:
        lifecycle tests.
  - [ ] B5A.16.g Run external conformance clients for every supported language/
        surface and compare observable behavior against generated contracts.
        Evidence: public-client report.

### B6 — Persistence, APIs, fleet, workers, and integration

- [ ] B6.1 Decide authoritative stores for hosts, sessions, commands, jobs,
      artifacts, checks, templates, builds, and releases. Evidence: ownership
      ADR.
  - [ ] B6.1.a Inventory current databases, artifact/evidence stores, indexes,
        caches, queues, logs, files, and target state for every named entity.
        Evidence: storage map.
  - [ ] B6.1.b Assign one authoritative store/owner per entity/property plus
        projections, consistency, retention, mutation, and recovery boundaries.
        Evidence: ownership matrix.
  - [ ] B6.1.c Resolve duplicate/shadow authorities, especially artifact bytes/
        metadata and release evidence, with migration/compatibility plan.
        Evidence: decision record.
  - [ ] B6.1.d Trace complete lifecycle and downstream reconciliation for each
        authority. Evidence: approved ADR.
- [ ] B6.2 Add missing schemas and safe migrations without shadowing artifact
      store or release-evidence data. Evidence: rehearsal.
  - [ ] B6.2.a Specify explicit normalized tables/columns/types, keys,
        constraints, indexes, tenancy, state invariants, audit/outbox refs, and
        external artifact/evidence refs. Evidence: schema review.
  - [ ] B6.2.b Write additive expand migrations and bounded resumable backfills
        safe under concurrent service/host traffic. Evidence: migration tests.
  - [ ] B6.2.c Operate old/new versions through explicit translation or dual
        behavior without copying authoritative artifact/evidence bytes into
        shadow stores. Evidence: compatibility suite.
  - [ ] B6.2.d Rehearse production-size upgrade, backfill, lock/latency impact,
        interruption, rollback/roll-forward, and contract cleanup. Evidence:
        rehearsal report.
- [ ] B6.3 Implement immutable artifacts/checksums and transaction-bound
      metadata/audit/outbox writes. Evidence: rollback tests.
  - [ ] B6.3.a Stage artifact bytes, verify size/type/security/checksum, and
        atomically promote a content-addressed immutable ref. Evidence: blob
        tests.
  - [ ] B6.3.b Persist metadata/version/dependency/provenance/lifecycle records
        with exact artifact refs and prohibit ref content replacement. Evidence:
        immutability tests.
  - [ ] B6.3.c Write state mutation, immutable audit event, and transactional
        outbox event in one database transaction. Evidence: atomicity tests.
  - [ ] B6.3.d Reconcile staged/promoted orphan bytes and outbox retries after
        rollback/crash without phantom metadata or duplicate side effects.
        Evidence: fault tests.
  - [ ] B6.3.e Independently rehash artifact bytes at validation/release/read-
        back boundaries. Evidence: corruption test.
- [ ] B6.4 Implement host/session/command APIs with mutual auth, idempotency,
      authorization, deadline, and audit. Evidence: contract suite.
  - [ ] B6.4.a Implement enrollment/host identity, capability/health,
        maintenance/drain/quarantine, and revoke endpoints with mutual
        authentication. Evidence: host API tests.
  - [ ] B6.4.b Implement session open/heartbeat/reconnect/suspend/resume/close/
        terminate endpoints with scoped grants and fencing epochs. Evidence:
        session API tests.
  - [ ] B6.4.c Implement typed command preview/submit/status/stream/cancel/
        reconcile endpoints against the command registry. Evidence: command API
        tests.
  - [ ] B6.4.d Enforce tenant/actor/host/object/property/path/capability
        authorization, expected revision, idempotency, and deadlines on every
        path. Evidence: negative matrix.
  - [ ] B6.4.e Persist redacted audit/outbox events and filter lists/errors/
        expansions so unauthorized existence cannot leak. Evidence: contract
        suite.
- [ ] B6.5 Implement interchange/job/check/build/release APIs with optimistic
      concurrency. Evidence: stale revision tests.
  - [ ] B6.5.a Implement interchange plan/create/configure/run/status/rerun/
        cancel/result endpoints with immutable job/DAG revisions. Evidence:
        interchange contracts.
  - [ ] B6.5.b Implement measurement/check/verdict/review/waiver endpoints with
        raw-result separation and freshness. Evidence: check contracts.
  - [ ] B6.5.c Implement template/build preview/run/status/cancel/artifact and
        release candidate/evidence/approve/deploy/recall endpoints. Evidence:
        build/release contracts.
  - [ ] B6.5.d Require expected revision/ETag and idempotency on mutations and
        return typed conflict/stale/partial/unavailable errors. Evidence:
        concurrency suite.
  - [ ] B6.5.e Authorize object/property/target/approval authority and emit
        transactional audit/outbox state. Evidence: security tests.
- [ ] B6.6 Implement remote gateway backpressure, connection limits, and circuit
      breakers. Evidence: load/fault tests.
  - [ ] B6.6.a Define per-environment/tenant/host/IP/session limits for
        handshakes, connections, streams, commands, bytes, rates, buffers, and
        idle/duration. Evidence: gateway policy.
  - [ ] B6.6.b Enforce bounded admission queues, token/leaky-bucket limits,
        prioritization, fair sharing, and explicit retry-after/degraded states.
        Evidence: load tests.
  - [ ] B6.6.c Propagate streaming backpressure end-to-end and terminate abusive
        or non-reading peers without unbounded memory. Evidence: slow-peer
        tests.
  - [ ] B6.6.d Trip half-open/closed circuit breakers by dependency/host/profile
        with jittered recovery and no cross-tenant cascade. Evidence: fault
        tests.
  - [ ] B6.6.e Expose bounded saturation/rejection/breaker telemetry and
        preserve in-flight command reconciliation. Evidence: overload drill.
- [ ] B6.7 Implement worker/host capability scheduling and anti-starvation.
      Evidence: queue tests.
  - [ ] B6.7.a Persist exact work requirements for host/DCC/plugin/tool/model/
        target versions, capabilities, resources/licenses, locality, deadlines,
        affinity, and safety scope. Evidence: requirement schema.
  - [ ] B6.7.b Match only live compatible enrolled workers/hosts and reserve
        CPU/GPU/memory/storage/license/session capacity atomically. Evidence:
        matching tests.
  - [ ] B6.7.c Apply tenant/class weights, priorities, aging, quotas,
        concurrency, reservations, and starvation bounds. Evidence: fairness
        policy.
  - [ ] B6.7.d Dispatch with fenced lease/heartbeat and reconcile loss,
        capability change, maintenance, quarantine, or late completion.
        Evidence: lifecycle tests.
  - [ ] B6.7.e Prove urgent work progresses without starving ordinary work and
        incompatible work never dispatches. Evidence: queue report.
- [ ] B6.8 Implement artifact upload/download with resumable transfer, checksum,
      range, authorization, and egress limits. Evidence: interruption and abuse
      tests.
  - [ ] B6.8.a Create an authorized transfer session bound to tenant/project,
        artifact/version or upload intent, size/type/checksum, purpose, expiry,
        rate/egress quota, and idempotency. Evidence: transfer schema.
  - [ ] B6.8.b Upload numbered checksum-protected chunks with overlap/gap/order/
        duplicate handling, bounded storage, expiry, and resume cursor.
        Evidence: upload interruption tests.
  - [ ] B6.8.c Assemble, rehash, scan, and atomically promote only exact
        complete content; clean abandoned/invalid sessions. Evidence: completion
        tests.
  - [ ] B6.8.d Download authorized immutable ranges/chunks with signed short-
        lived grants, conditional revision/checksum, bandwidth/egress limits,
        and resume. Evidence: download tests.
  - [ ] B6.8.e Reject cross-tenant refs, enumeration, range abuse, checksum/
        size/type mismatch, replay, quota bypass, and revoked artifacts.
        Evidence: abuse suite.
- [ ] B6.9 Implement permission-aware artifact, host, command, and release
      search. Evidence: secrets/paths/unreleased names do not leak.
  - [ ] B6.9.a Define separate approved index projections and searchable fields
        for artifacts, hosts, commands, and releases. Evidence: field registry.
  - [ ] B6.9.b Exclude secrets/credentials, raw command arguments/log content,
        local paths, protected metadata, and unauthorized unreleased names.
        Evidence: index inspection.
  - [ ] B6.9.c Enforce tenant/project/host/object/property/rights/release
        authorization before query, suggestions, filters, facets, counts, and
        snippets. Evidence: inference tests.
  - [ ] B6.9.d Reconcile indexes on permission/lifecycle/version/quarantine/
        recall/deletion changes. Evidence: propagation tests.
- [ ] B6.10 Implement signed deep links between Oshun Studio, Bellona control
      room, Yemaya, and target systems. Evidence: tenant/expiry/replay tests.
  - [ ] B6.10.a Register allowed source-destination route/context pairs,
        audiences, purposes, return paths, and prohibited embedded data/
        authority. Evidence: link registry.
  - [ ] B6.10.b Mint signed bounded tenant/project/object/revision/action-hint/
        expiry/nonce links with issuer/audience/key version. Evidence: signing
        tests.
  - [ ] B6.10.c Validate signature/audience/tenant/expiry/replay/current
        authorization/object existence/revision/lifecycle at destination.
        Evidence: negative suite.
  - [ ] B6.10.d Render safe forbidden/expired/missing/stale states without
        existence leakage and never execute a mutation from link context alone.
        Evidence: route journey.
- [ ] B6.11 Implement target adapter health/capability probes. Evidence: UI uses
      live state.
  - [ ] B6.11.a Define bounded probe contracts for adapter/service/target
        identity/version, auth, reachability, capabilities/limits, dependencies,
        capacity/license, latency, and freshness. Evidence: probe schema.
  - [ ] B6.11.b Run least-privilege non-mutating probes with timeouts/rate
        limits/circuit breakers and safe credentials. Evidence: probe tests.
  - [ ] B6.11.c Compute healthy/degraded/unavailable/incompatible/
        unauthenticated/stale states with component reasons. Evidence: state
        fixtures.
  - [ ] B6.11.d Cache only within declared freshness, emit bounded telemetry,
        and drive scheduler/API/UI eligibility from the same projection.
        Evidence: live-state integration test.
- [ ] B6.12 Implement two-tenant/project/host isolation across API, gateway,
      artifact, search, queue, logs, and export. Evidence: integration suite.
  - [ ] B6.12.a Create colliding tenant/project/host IDs, names, checksums,
        paths, artifacts, commands, jobs, and releases in isolated fixtures.
        Evidence: isolation corpus.
  - [ ] B6.12.b Prove API/list/error/gateway/session/command authorization and
        connection/resource isolation. Evidence: control-plane suite.
  - [ ] B6.12.c Prove artifact/ref/signed URL/cache/search/queue/worker/log/
        telemetry/event isolation including count and timing inference.
        Evidence: data-plane suite.
  - [ ] B6.12.d Prove build/export/release/recall/backup/restore/admin
        operations cannot cross boundaries without explicit governed authority.
        Evidence: integration suite.
- [ ] B6.13 Generate OpenAPI, remote protocol, SDK, and host consumer contracts.
      Evidence: drift gate.
  - [ ] B6.13.a Generate OpenAPI with stable operations, schemas/errors, auth,
        pagination, idempotency/concurrency, events, examples, and deprecations.
        Evidence: spec validation.
  - [ ] B6.13.b Generate remote gateway/host protocol message and state-machine
        contracts with negotiation/capability versions. Evidence: protocol
        artifacts.
  - [ ] B6.13.c Generate SDK types/clients and host plugin/extension consumer
        types/fixtures for every supported language/surface. Evidence: consumer
        builds.
  - [ ] B6.13.d Compare generated/checked-in server and consumer artifacts and
        block undocumented breaking drift. Evidence: CI drift gate.
- [ ] B6.14 Implement compatibility telemetry for host/plugin/adapter/target
      versions. Evidence: unsupported or expiring versions visible.
  - [ ] B6.14.a Emit bounded version/capability/support-window observations from
        enrollment, session, command, job, build, and target probes. Evidence:
        telemetry schema.
  - [ ] B6.14.b Join observations to the approved compatibility registry without
        logging host secrets, local paths, project names, or unbounded versions.
        Evidence: privacy tests.
  - [ ] B6.14.c Surface supported/degraded/expiring/deprecated/incompatible/
        unknown distributions and affected operations/releases. Evidence:
        compatibility views.
  - [ ] B6.14.d Alert owners before expiry and link to upgrade/drain/quarantine/
        exception workflows with outcome tracking. Evidence: expiry drill.

### B7 — Control-room and Studio experience

- [ ] B7.1 Refactor the existing control-room hub around sessions, host health,
      interchange failures, consistency gates, builds, cost, and releases.
      Evidence: live-data design review.
  - [ ] B7.1.a Define operator questions, critical states, prioritized actions,
        role visibility, and canonical drill-down routes. Evidence: task map.
  - [ ] B7.1.b Replace fixture/duplicated summaries with authorized live
        projections for sessions, hosts, interchange/gates, builds, cost, and
        releases. Evidence: data contract tests.
  - [ ] B7.1.c Compose a restrained task-led workspace with one operational
        focus, clear hierarchy, and no decorative hero or generic card mosaic.
        Evidence: frontend review.
  - [ ] B7.1.d Preserve tenant/project/time/filter context through drill-down
        and render loading/empty/stale/partial/unavailable/failed states.
        Evidence: Playwright journey.
- [ ] B7.2 Build host fleet, session, command, ingest, interchange, artifact,
      QA, template, batch, schedule, release, and audit routes. Evidence: route
      integrity.
  - [ ] B7.2.a Define canonical route IDs/hierarchy/params/loaders/permissions/
        breadcrumbs/titles/deep links and ownership. Evidence: route registry.
  - [ ] B7.2.b Implement host/session/command routes with exact enrolled host,
        grant, revision, and live-state context. Evidence: control route tests.
  - [ ] B7.2.c Implement ingest/interchange/artifact/QA routes with immutable
        source/job/output/measurement context. Evidence: asset route tests.
  - [ ] B7.2.d Implement template/batch/schedule/release/audit routes with
        operation authority and protected evidence boundaries. Evidence:
        operations route tests.
  - [ ] B7.2.e Verify direct load/refresh/history, signed link, not-found,
        forbidden, stale revision, and permission change on every route.
        Evidence: Playwright integrity suite.
- [ ] B7.3 Build live bridge-session view with identity, DCC state, grants,
      heartbeat, latency, load, command history, and termination. Evidence:
      fault journey.
  - [ ] B7.3.a Load immutable session/host/actor/project/purpose/grant/
        negotiated-version identity and subscribe to resumable live updates.
        Evidence: session view contract.
  - [ ] B7.3.b Display heartbeat/freshness, latency/clock, DCC/project/modal/
        dirty state, resources/load/licenses, and maintenance/quarantine.
        Evidence: live-state fixtures.
  - [ ] B7.3.c Show exact grants, expiry, permitted command/path/resource scope,
        active reservations, and redacted command/attempt/result history.
        Evidence: least-privilege view.
  - [ ] B7.3.d Preview and authorize suspend/drain/terminate/quarantine actions
        with in-flight blast radius and step-up. Evidence: control journey.
  - [ ] B7.3.e Reconcile disconnect/reconnect, event loss/reorder, expiry,
        command uncertainty, and late result without false healthy/success.
        Evidence: fault journey.
- [ ] B7.4 Build interchange DAG with stage status, host/worker, attempts, logs,
      artifacts, measurements, cache, cost, and rerun. Evidence: live job
      journey.
  - [ ] B7.4.a Load immutable job/DAG/plan and subscribe to ordered resumable
        stage/attempt updates with snapshot reconciliation. Evidence: live-data
        tests.
  - [ ] B7.4.b Render dependencies, stage/attempt state, host/worker/resources,
        progress/timing, cache, cost, artifacts, measures, and target results.
        Evidence: DAG fixtures.
  - [ ] B7.4.c Inspect exact inputs/config/toolchain/logs/checkpoints/findings/
        outputs/failure/lineage for a selected stage. Evidence: inspector
        journey.
  - [ ] B7.4.d Provide equivalent ordered/dependency semantic table and keyboard
        selection; do not require graph manipulation. Evidence: accessibility
        parity.
  - [ ] B7.4.e Preview downstream invalidation/loss/cost/capacity and enqueue an
        authorized exact rerun revision. Evidence: rerun journey.
- [ ] B7.5 Build safe command composer only for allowlisted typed commands.
      Evidence: raw command shell is absent.
  - [ ] B7.5.a Filter commands by current host/plugin/version, negotiated
        capabilities, session grants, actor authority, project/object context,
        and host state. Evidence: command eligibility tests.
  - [ ] B7.5.b Generate forms from typed schemas with units/bounds/enums/object
        pickers/path virtual roots/defaults and property-level authorization.
        Evidence: form tests.
  - [ ] B7.5.c Resolve exact target/revision and preview normalized command,
        semantic diff, dependencies, side effects, cost/risk, reversibility, and
        required approval. Evidence: dry-run journey.
  - [ ] B7.5.d Require step-up/approval where defined and submit idempotently
        with deadline, expected revision, and immutable audit. Evidence:
        execution tests.
  - [ ] B7.5.e Display progress/result/partial/unknown/cancel/compensation and
        target read-back. Evidence: command fault journey.
  - [ ] B7.5.f Prove no raw shell, script, eval, console, unbounded argument, or
        arbitrary path/network field exists. Evidence: security assertion.
- [ ] B7.6 Build source/USD/target preview and diff with semantic parity.
      Evidence: keyboard/screen-reader journey.
  - [ ] B7.6.a Load authorized exact source/USD/target representations and map
        comparable hierarchy/objects/properties/cameras/time. Evidence: preview
        setup tests.
  - [ ] B7.6.b Synchronize camera/framing, variant/layer, selection/isolation,
        transport, and overlays across available visual previews. Evidence:
        interaction journey.
  - [ ] B7.6.c Display structural, material, rig, animation, metadata,
        dependency, measurement, and calibrated image differences at exact
        locations. Evidence: diff fixtures.
  - [ ] B7.6.d Provide tree/table/list alternatives with before/after values,
        units, sources, status, and keyboard focus synchronized to visuals.
        Evidence: semantic parity.
  - [ ] B7.6.e Render unavailable host/renderer, unsupported preview, stale
        mapping, partial data, and nondeterminism honestly. Evidence: state
        matrix.
- [ ] B7.7 Build consistency gate panel with raw measures, policy, freshness,
      remediation, rerun, and authorized waiver. Evidence: state matrix.
  - [ ] B7.7.a Show exact source/target revisions, fidelity profile/version,
        validators, raw measures/units/ranges/uncertainty, and artifact reports.
        Evidence: evidence view.
  - [ ] B7.7.b Show separate policy rules/thresholds/verdict/reasons,
        freshness/staleness dependencies, limitations, and non-pass states.
        Evidence: verdict fixtures.
  - [ ] B7.7.c Link failures to exact objects/properties/frames/files and
        allowlisted remediation or rerun plan with impact preview. Evidence:
        remediation journey.
  - [ ] B7.7.d Request/review/approve/reject eligible waivers with exact scope,
        loss, target, purpose, mitigation, expiry, and approver. Evidence:
        waiver tests.
  - [ ] B7.7.e Prove stale/unavailable/unsupported/low-confidence/validator-
        failure or expired waiver never renders or releases as pass. Evidence:
        state matrix.
- [ ] B7.8 Build artifact lineage and dependency inspector. Evidence: impact
      preview supports quarantine/retire.
  - [ ] B7.8.a Traverse authoritative source/version/representation/layer/
        dependency/command/job/build/release/target/consumer edges with exact
        revisions. Evidence: lineage query.
  - [ ] B7.8.b Present upstream/downstream hierarchy/graph plus semantic table,
        edge types, checksums, rights/license, validation, lifecycle, and
        unresolved refs. Evidence: inspector parity.
  - [ ] B7.8.c Filter by relation/depth/status/target and inspect path-level
        transformation/evidence without leaking unauthorized nodes. Evidence:
        authorization tests.
  - [ ] B7.8.d Preview quarantine/retire/supersede/revoke/recall blast radius,
        active work, releases, targets, blocked actions, and remediation.
        Evidence: impact journey.
- [ ] B7.9 Build batch/build/release operations with preview, progress, partial
      result, retry, cancel, and read-back. Evidence: mixed-result journey.
  - [ ] B7.9.a Resolve a stable selected/query item set, exact template/
        parameters/targets, caller authority, and current policy. Evidence:
        operation contract.
  - [ ] B7.9.b Preview per-item eligibility/loss/dependency/gates and aggregate
        resources/licenses/capacity/schedule/cost/target impact. Evidence:
        preview tests.
  - [ ] B7.9.c Authorize the exact preview revision and execute idempotently
        with per-item/stage progress, attempts, artifacts, and audit. Evidence:
        execution tests.
  - [ ] B7.9.d Pause/cancel/retry only eligible scope and show succeeded/failed/
        skipped/cancelled/partial/unknown/compensated separately. Evidence:
        mixed-result journey.
  - [ ] B7.9.e Read back builds/packages/targets/releases and keep unresolved
        partial state visible. Evidence: reconciliation report.
- [ ] B7.10 Build mobile/tablet incident, session terminate, review, approve,
      and release supervision. Evidence: step-up controls.
  - [ ] B7.10.a Prioritize incident queue, host/session health, command/job
        failure, evidence review, approve/reject, terminate/quarantine, and
        release/recall tasks for compact viewports. Evidence: mobile task map.
  - [ ] B7.10.b Provide semantic summaries/tables/thumbnails and exact evidence
        links without requiring dense graph/3D/full-scene editing. Evidence:
        compact journey.
  - [ ] B7.10.c Implement touch/keyboard targets, reflow/zoom/orientation/safe
        areas, reduced motion, interruption, offline/reconnect, and focus
        recovery. Evidence: responsive accessibility suite.
  - [ ] B7.10.d Enforce exact revision/impact, step-up/dual control, reason,
        confirmation, immutable audit, and read-back for critical actions.
        Evidence: mobile security tests.
- [ ] B7.11 Build governed Studio facade showing permitted summaries and deep
      links, not a duplicate console. Evidence: parity/ownership test.
  - [ ] B7.11.a Define permitted summary fields/states/actions and prohibited
        host command, secret, path, raw log, protected artifact, and admin data.
        Evidence: facade contract.
  - [ ] B7.11.b Project authorized live job/release/target summaries with source
        owner, freshness, limitations, and canonical Bellona identity. Evidence:
        projection tests.
  - [ ] B7.11.c Route detailed inspection/mutation through signed expiring deep
        links and reauthorize in the control room. Evidence: link journey.
  - [ ] B7.11.d Prove no duplicate mutation endpoints/state or divergent status
        exists in Studio and parity reconciles from authoritative events.
        Evidence: ownership test.
- [ ] B7.12 Build command palette and saved views filtered by role. Evidence:
      unauthorized commands never appear or execute.
  - [ ] B7.12.a Register stable navigation/operation IDs, keywords, context
        requirements, route/target resolvers, capabilities, step-up, and
        availability. Evidence: command registry.
  - [ ] B7.12.b Filter palette, shortcuts, recent items, suggestions, and saved
        view content by current role/tenant/project/host/object authority.
        Evidence: visibility tests.
  - [ ] B7.12.c Create/update/share/delete saved queries/layouts with owner/
        scope/version/permissions and safe defaults. Evidence: saved-view tests.
  - [ ] B7.12.d Reauthorize resolved target/revision at navigation or execution
        and reject stale permission/context. Evidence: execution negative suite.
- [ ] B7.13 Build honest disconnected-host, incompatible-version, missing-DCC,
      unavailable-validator, stale, partial, fixture, and failed states.
      Evidence: state matrix.
  - [ ] B7.13.a Define each state, source signal, precedence, reason code,
        diagnostic detail, owner, permitted action, retryability, and release
        effect. Evidence: state contract.
  - [ ] B7.13.b Render disconnected/missing/incompatible host dependencies and
        unavailable validators with last-known time and safe remediation.
        Evidence: dependency matrix.
  - [ ] B7.13.c Distinguish stale/partial data and mixed per-item/per-stage/
        target outcomes from empty/success. Evidence: partial-state tests.
  - [ ] B7.13.d Persistently label fixtures/demos and prohibit production
        mutation or evidence claims. Evidence: fixture tests.
  - [ ] B7.13.e Preserve typed failure diagnostics, retry/compensation, and
        unknown/late result without optimistic completion. Evidence: fault
        journey.
- [ ] B7.14 Apply existing theming/i18n/mobile/accessibility work without
      introducing decorative dashboard clutter. Evidence: frontend conformance
      review.
  - [ ] B7.14.a Inventory/reuse existing design tokens, components, themes,
        locale/formatting infrastructure, responsive patterns, and accessibility
        primitives. Evidence: reuse map.
  - [ ] B7.14.b Implement restrained visual hierarchy, typography, spacing,
        color, motion, and operational density around task priority rather than
        ornamental cards/gradients/hero copy. Evidence: design review.
  - [ ] B7.14.c Externalize/localize strings and test expansion, plural/
        date/number/time-zone formatting, RTL where supported, and technical
        identifiers. Evidence: i18n suite.
  - [ ] B7.14.d Validate wide, compact, and single-column layouts, touch/
        keyboard, zoom/reflow, themes/contrast, reduced motion, and semantic
        alternatives. Evidence: Playwright conformance suite.
- [ ] B7.15 Add permission-filtered control-room routes or governed deep links
      for render, gameplay, virtual production, XR, mocap, MetaHuman, DaVinci,
      Houdini, audio/video, cross-domain compilation, agent/MCP plans,
      package-builder, events, and developer compatibility. Evidence: every
      retained specialized capability is reachable by its authorized audience
      and no duplicate mutation UI exists.
  - [ ] B7.15.a Map each specialized capability to its authoritative existing or
        new control-room route, permitted facade/deep-link entry, owner, and
        required roles. Evidence: reachability matrix.
  - [ ] B7.15.b Add task-led routes for render/gameplay/VP/XR/mocap/MetaHuman
        operations with profile-specific states/evidence and no generic
        inheritance. Evidence: route journeys.
  - [ ] B7.15.c Add task-led routes for DaVinci/Houdini/A-V/cross-domain
        compilation/package operations with exact source-target lineage.
        Evidence: route journeys.
  - [ ] B7.15.d Add task-led agent-plan/event/developer compatibility routes
        with risk/approval, delivery status, and version matrices. Evidence:
        route journeys.
  - [ ] B7.15.e Filter navigation/routes/data/actions by authorization and
        feature/profile availability; reauthorize on load/action. Evidence: role
        tests.
  - [ ] B7.15.f Prove every authorized capability is reachable and every
        mutation has exactly one authoritative UI/service surface. Evidence:
        zero-orphan/duplication audit.

### B8 — Per-host compatibility and release matrix

- [ ] B8.1 Validate Blender host install, auth, session, pull, push, import,
      export, round-trip, and revoke. Evidence: supported-version matrix.
  - [ ] B8.1.a Install/upgrade/rollback/uninstall the signed Bellona plugin on
        every supported Blender/OS version and verify files/preferences/
        dependency cleanup. Evidence: install matrix.
  - [ ] B8.1.b Enroll/authenticate/rotate/revoke the Blender host and exercise
        scoped session open/heartbeat/reconnect/close. Evidence: security
        journey.
  - [ ] B8.1.c Pull/push/import/export representative Blender scenes/assets
        through typed commands with authorization, cancellation, undo, and crash
        recovery. Evidence: command traces.
  - [ ] B8.1.d Round-trip Blender geometry/material/rig/animation/camera/
        dependencies and compare known fidelity values/declared loss. Evidence:
        version matrix.
- [ ] B8.2 Validate Maya host install, auth, session, pull, push, import,
      export, round-trip, and revoke. Evidence: supported-version matrix.
  - [ ] B8.2.a Install/upgrade/rollback/uninstall the signed Bellona plugin on
        every supported Maya/OS version and verify modules/preferences/
        dependency cleanup. Evidence: install matrix.
  - [ ] B8.2.b Enroll/authenticate/rotate/revoke the Maya host and exercise
        scoped session open/heartbeat/reconnect/close. Evidence: security
        journey.
  - [ ] B8.2.c Pull/push/import/export representative Maya scenes/assets through
        typed commands with authorization, cancellation, undo, and crash
        recovery. Evidence: command traces.
  - [ ] B8.2.d Round-trip Maya geometry/material/rig/animation/camera/
        dependencies and compare known fidelity values/declared loss. Evidence:
        version matrix.
- [ ] B8.3 Validate Houdini host install, auth, session, pull, push, import,
      export, round-trip, and revoke. Evidence: supported-version matrix.
  - [ ] B8.3.a Install/upgrade/rollback/uninstall the signed Bellona package on
        every supported Houdini/OS version and verify package paths/assets/
        dependency cleanup. Evidence: install matrix.
  - [ ] B8.3.b Enroll/authenticate/rotate/revoke the Houdini host and exercise
        scoped session open/heartbeat/reconnect/close. Evidence: security
        journey.
  - [ ] B8.3.c Pull/push/import/export and cook representative Houdini scenes/
        HDAs/USD through typed commands with cancel/checkpoint/license/crash
        recovery. Evidence: command traces.
  - [ ] B8.3.d Round-trip geometry/material/rig/animation/camera/dependencies/
        HDA outputs and compare fidelity/nondeterminism. Evidence: version
        matrix.
- [ ] B8.4 Validate 3ds Max host install, auth, session, pull, push, import,
      export, round-trip, and revoke. Evidence: supported-version matrix.
  - [ ] B8.4.a Install/upgrade/rollback/uninstall the signed Bellona plugin on
        every supported 3ds Max/Windows version and verify files/settings/
        dependency cleanup. Evidence: install matrix.
  - [ ] B8.4.b Enroll/authenticate/rotate/revoke the 3ds Max host and exercise
        scoped session open/heartbeat/reconnect/close. Evidence: security
        journey.
  - [ ] B8.4.c Pull/push/import/export representative 3ds Max scenes/assets
        through typed commands with authorization, cancellation, undo, and crash
        recovery. Evidence: command traces.
  - [ ] B8.4.d Round-trip 3ds Max geometry/material/rig/animation/camera/
        dependencies and compare known fidelity values/declared loss. Evidence:
        version matrix.
- [ ] B8.5 Validate Unreal host install, auth, session, pull, push, import,
      export, round-trip, and revoke using the local UE5 path where applicable.
      Evidence: supported-version matrix.
  - [ ] B8.5.a Build/install/enable/upgrade/rollback/uninstall the signed
        Bellona plugin on every supported Unreal/platform version, including
        local UE 5.5 where applicable. Evidence: install matrix.
  - [ ] B8.5.b Enroll/authenticate/rotate/revoke editor/on-box hosts and
        exercise scoped session open/heartbeat/reconnect/close without editor
        deadlock. Evidence: security journey.
  - [ ] B8.5.c Pull/push/import/export/cook representative Unreal assets/maps
        through typed commands with transactions, cancel, compile, and crash
        recovery. Evidence: command traces.
  - [ ] B8.5.d Round-trip Unreal geometry/material/skeleton/animation/camera/
        metadata/dependencies and validate editor/runtime load. Evidence:
        version matrix.
- [ ] B8.6 Validate Unity host install, auth, session, pull, push, import,
      export, round-trip, and revoke. Evidence: supported-version matrix.
  - [ ] B8.6.a Install/upgrade/rollback/uninstall the signed Bellona package on
        every supported Unity/platform version and verify package/project/
        dependency cleanup. Evidence: install matrix.
  - [ ] B8.6.b Enroll/authenticate/rotate/revoke the Unity host and exercise
        scoped session open/heartbeat/reconnect/close across domain reload.
        Evidence: security journey.
  - [ ] B8.6.c Pull/push/import/export/build representative Unity assets/scenes
        through typed commands with undo, cancel, compile, and crash recovery.
        Evidence: command traces.
  - [ ] B8.6.d Round-trip geometry/material/rig/animation/camera/metadata/
        dependencies and validate editor/player load. Evidence: version matrix.
- [ ] B8.7 Validate Godot host install, auth, session, pull, push, import,
      export, round-trip, and revoke. Evidence: supported-version matrix.
  - [ ] B8.7.a Install/upgrade/rollback/uninstall the signed Bellona add-on on
        every supported Godot/platform version and verify project/settings/
        dependency cleanup. Evidence: install matrix.
  - [ ] B8.7.b Enroll/authenticate/rotate/revoke the Godot host and exercise
        scoped session open/heartbeat/reconnect/close. Evidence: security
        journey.
  - [ ] B8.7.c Pull/push/import/export/build representative Godot resources/
        scenes through typed commands with undo, cancel, reimport, and crash
        recovery. Evidence: command traces.
  - [ ] B8.7.d Round-trip Godot geometry/material/skeleton/animation/camera/
        metadata/dependencies and validate editor/runtime load. Evidence:
        version matrix.
- [ ] B8.8 Validate host offline/reconnect without duplicate commands or partial
      hidden state. Evidence: network fault tests.
  - [ ] B8.8.a Inject disconnect before dispatch, after acceptance, during
        execution/streaming/commit, and after host completion before receipt.
        Evidence: fault matrix.
  - [ ] B8.8.b Assert gateway/session liveness, command uncertainty, user state,
        reservations, and downstream job state become explicit. Evidence: state
        tests.
  - [ ] B8.8.c Reauthenticate and reconcile session epoch, command IDs/results,
        host object revisions, artifacts, and checkpoints on reconnect.
        Evidence: reconciliation tests.
  - [ ] B8.8.d Retry/resume/compensate only safe work and prove no duplicate
        command side effect or hidden partial state. Evidence: network fault
        report.
- [ ] B8.9 Validate DCC crash and restart recovery. Evidence: session/job state
      reconciles.
  - [ ] B8.9.a Crash each supported host during idle, dirty scene, import/
        export, mutation, save, render/build, and result commit. Evidence: crash
        matrix.
  - [ ] B8.9.b Assert session/command/job/lease state, checkpoints, temp files,
        artifact promotion, and user-visible diagnostics are safe. Evidence:
        invariant tests.
  - [ ] B8.9.c Restart host/plugin, reauthenticate, inspect project/object/file
        state, and correlate recovered command journals/checkpoints. Evidence:
        restart journey.
  - [ ] B8.9.d Resume/retry/compensate or require human resolution according to
        command contract with no false success. Evidence: reconciliation report.
- [ ] B8.10 Validate plugin upgrade/downgrade and server version skew. Evidence:
      compatibility behavior.
  - [ ] B8.10.a Test every supported plugin/server/protocol/host version
        combination and boundary outside the compatibility window. Evidence:
        skew matrix.
  - [ ] B8.10.b Upgrade with active/idle sessions and verify drain, state
        preservation, negotiation, health, and rollback. Evidence: upgrade
        journey.
  - [ ] B8.10.c Downgrade only where approved and verify schema/state/config/
        artifact compatibility or explicit rejection. Evidence: downgrade tests.
  - [ ] B8.10.d Surface degraded/expiring/incompatible status before command
        dispatch and never silently select an unsafe protocol. Evidence:
        compatibility behavior.
- [ ] B8.11 Validate unit, axis, color, material, rig, animation, camera,
      metadata, and unsupported-feature behavior for every supported pair.
      Evidence: pairwise coverage matrix.
  - [ ] B8.11.a Generate the exact supported source-host/version to target-host/
        version/profile pair matrix with declared unsupported cells. Evidence:
        coverage registry.
  - [ ] B8.11.b Run analytical unit/axis/transform/hierarchy/camera and known
        color/OCIO fixtures per pair. Evidence: foundational results.
  - [ ] B8.11.c Run material/texture/shader and skeleton/skinning/blend-shape/
        animation fixtures per pair. Evidence: asset results.
  - [ ] B8.11.d Run metadata/naming/dependency/variant/scene and target import/
        runtime fixtures per pair. Evidence: scene results.
  - [ ] B8.11.e Assert every unsupported/lossy/substituted feature produces
        exact diagnostics, preview/acceptance, and never silent pass. Evidence:
        behavior matrix.
- [ ] B8.12 Validate large asset, large scene, many dependencies, and slow link.
      Evidence: performance and backpressure.
  - [ ] B8.12.a Define representative maximum-supported bytes, geometry/
        texture/animation, object/layer count, dependency fan-out/depth, and
        network latency/bandwidth/loss fixtures. Evidence: workload plan.
  - [ ] B8.12.b Measure ingest/scan/resolve/convert/validate/preview/transfer/
        import/build/read-back latency, throughput, memory, storage, and cost.
        Evidence: performance report.
  - [ ] B8.12.c Exercise resumable chunking, incremental conversion, caching,
        pagination/lazy preview, scheduling, quotas, and end-to-end
        backpressure. Evidence: behavior tests.
  - [ ] B8.12.d Inject interruption/cancel/resource exhaustion and prove bounded
        memory/storage, cleanup, recovery, fairness, and no corrupt partial
        release. Evidence: overload report.
- [ ] B8.13 Validate path/resolver behavior across supported operating systems.
      Evidence: portable package fixtures.
  - [ ] B8.13.a Cover Windows drives/UNC/device/reserved names/case, macOS case/
        Unicode, and Linux roots/symlinks/case with supported host combinations.
        Evidence: OS path corpus.
  - [ ] B8.13.b Test separators/dot segments/encoding/normalization/length/
        collisions/symlinks/junctions/mounts and virtual-root containment.
        Evidence: security results.
  - [ ] B8.13.c Test resolver contexts, environment independence, relative/
        virtual mappings, external refs, packages, and dependency checksums on
        every OS. Evidence: resolver matrix.
  - [ ] B8.13.d Move packages among clean supported OS/host environments and
        prove resolution/read-back without source-machine paths. Evidence:
        portability fixtures.
- [ ] B8.14 Publish known limitations and loss profiles per host/target pair.
      Evidence: UI and generated documentation derive from the same registry.
  - [ ] B8.14.a Record each pair's unsupported/partial/substituted/baked/
        nondeterministic features, bounds, symptoms, mitigation, and affected
        versions in the compatibility registry. Evidence: limitation rows.
  - [ ] B8.14.b Link each limitation to source tests, raw measurements, adapter/
        host issue, owner, status, and expiry/review date. Evidence: provenance
        audit.
  - [ ] B8.14.c Generate user documentation, conversion preview diagnostics,
        API/SDK discovery, and control-room explanations from the same versioned
        data. Evidence: generation parity.
  - [ ] B8.14.d Block unacknowledged material loss and test that docs/UI update
        with registry changes. Evidence: stale-content gate.
- [ ] B8.15 Validate the complete typed command registry for each supported
      host/version/plugin rather than only connect/pull/push. Evidence:
      command-capability matrix includes success, unsupported, authorization,
      invalid revision, cancellation, undo/compensation, crash, and audit.
  - [ ] B8.15.a Enumerate every command ID/version/category and every supported
        host/version/plugin tuple; declare required, optional, and unsupported
        cells. Evidence: command-capability matrix.
  - [ ] B8.15.b Generate schema-valid boundary and invalid argument cases for
        each supported command, including path/object/property/resource limits.
        Evidence: command fixtures.
  - [ ] B8.15.c Test success/idempotent replay/result/read-back and explicit
        unsupported/incompatible behavior per cell. Evidence: functional
        conformance.
  - [ ] B8.15.d Test unauthorized property/scope, stale revision/precondition,
        deadline/cancel, duplicate, session expiry, and host quarantine per
        applicable cell. Evidence: control conformance.
  - [ ] B8.15.e Test undo/compensation/partial/unknown outcome and host/plugin
        crash/reconnect reconciliation per mutating cell. Evidence: recovery
        conformance.
  - [ ] B8.15.f Verify redacted immutable audit and coverage gate; prohibit
        connect/pull/push from standing in for untested commands. Evidence:
        release audit.
- [ ] B8.16 Validate renderers/farms, virtual-production devices, XR
      devices/runtimes, mocap vendors, MetaHuman/Live Link, Resolve, Houdini,
      engine runtime/gameplay, and package/install targets independently.
      Evidence: specialized pairwise matrix with declared unavailable cells.
  - [ ] B8.16.a Register exact producer/control-room/host/plugin/device/runtime/
        target version tuples and required specialized capabilities. Evidence:
        specialized matrix.
  - [ ] B8.16.b Validate each renderer/farm and VP device/stage combination for
        auth, commands, resources, safety, output, failure, and read-back.
        Evidence: independent rows.
  - [ ] B8.16.c Validate each XR platform/device/runtime and mocap
        vendor/device/ protocol combination for live/deploy/quality/failure
        behavior. Evidence: independent rows.
  - [ ] B8.16.d Validate each MetaHuman/Live Link, Resolve, and Houdini version
        combination for identity/media/procedural workflows and round-trip.
        Evidence: independent rows.
  - [ ] B8.16.e Validate each engine runtime/gameplay and package/install target
        for scenario/build/install/upgrade/rollback/read-back. Evidence:
        independent rows.
  - [ ] B8.16.f Declare unavailable/unsupported cells with reason/owner/plan and
        prevent any specialized profile from inheriting generic bridge or
        another target's evidence. Evidence: independence gate.

### B9 — Bellona observability, verification, and launch

- [ ] B9.1 Instrument host, session, command, transfer, interchange, cache,
      validation, build, release, and recall traces/metrics. Evidence: bounded
      schema.
  - [ ] B9.1.a Define bounded span/event/metric names, attributes, units,
        cardinality, sampling, retention, and owners for every subsystem.
        Evidence: telemetry registry.
  - [ ] B9.1.b Propagate tenant-safe trace/session/command/job/stage/artifact/
        build/release correlation across gateway, hosts, workers, adapters, and
        targets. Evidence: traced fixture.
  - [ ] B9.1.c Instrument success, latency, traffic/load, retries/backpressure,
        partial/unknown outcomes, typed failures, gates, cost, and recall
        closure. Evidence: signal tests.
  - [ ] B9.1.d Redact credentials, tokens, local paths, asset/project names,
        command payloads, content, free text, and unbounded identifiers.
        Evidence: telemetry privacy scan.
- [ ] B9.2 Define SLOs for connection, command, transfer, queue, conversion,
      validation, build, target availability, and release verification.
      Evidence: error-budget policy.
  - [ ] B9.2.a Define user-visible indicators, populations, windows, sources,
        exclusions, and measurement quality for each named operation. Evidence:
        SLI catalog.
  - [ ] B9.2.b Ratify latency, availability, freshness, correctness, fidelity,
        durability, and capacity objectives by support tier/profile. Evidence:
        target matrix.
  - [ ] B9.2.c Build dashboards and multi-window burn alerts with actionable
        host/adapter/target drill-down and bounded labels. Evidence: alert
        tests.
  - [ ] B9.2.d Define error-budget release freeze, exception, remediation,
        recovery, and retrospective policy. Evidence: policy exercise.
- [ ] B9.3 Run protocol, contract, migration, and version-skew suites. Evidence:
      host/client/server matrix.
  - [ ] B9.3.a Test current/supported prior gateway-host protocol negotiation,
        messages, state machines, capabilities, errors, and unknown fields.
        Evidence: protocol matrix.
  - [ ] B9.3.b Test API/event/SDK/host-plugin contracts across server/client/
        consumer version combinations. Evidence: contract matrix.
  - [ ] B9.3.c Test schema migration upgrade/backfill/mixed-version/rollback/
        roll-forward/cleanup at representative scale. Evidence: migration
        report.
  - [ ] B9.3.d Test host/plugin/adapter/target version boundaries,
        deprecation/expiry, downgrade, and fail-closed unsupported behavior.
        Evidence: skew suite.
- [ ] B9.4 Run known-value USD, geometry, transform, material, rig, animation,
      camera, dependency, and fidelity tests. Evidence: gate registry coverage.
  - [ ] B9.4.a Register independently derived fixtures with source, generator,
        expected raw values/tolerances, profile, rights, and checksums.
        Evidence: fixture registry.
  - [ ] B9.4.b Cover USD composition/schema/resolver/dependency and geometry/
        topology/transform/unit/axis measures. Evidence: foundational report.
  - [ ] B9.4.c Cover material/texture/color/render and skeleton/skinning/blend-
        shape/animation measures. Evidence: asset report.
  - [ ] B9.4.d Cover camera/light/scene/metadata/dependency, target import/
        runtime, and round-trip fidelity separately per profile. Evidence:
        target report.
  - [ ] B9.4.e Prove every production measure/gate/profile/version maps to
        current known-value proof and non-pass states cannot satisfy coverage.
        Evidence: gate audit.
- [ ] B9.5 Run `usdchecker` and pinned custom-validator goldens. Evidence:
      current reports.
  - [ ] B9.5.a Pin OpenUSD/`usdchecker` and custom validator code/config/
        dependencies/profiles with checksums. Evidence: validator manifest.
  - [ ] B9.5.b Run valid/boundary/invalid/malicious USD stages/packages in
        hermetic resolver contexts and compare raw reports to goldens. Evidence:
        golden suite.
  - [ ] B9.5.c Verify typed parser mapping, severity/location/rule identity,
        truncation, unknown output, and validator failure/unavailability.
        Evidence: parser tests.
  - [ ] B9.5.d Publish exact current report artifacts/versions and block stale
        validator evidence at release. Evidence: freshness gate.
- [ ] B9.6 Run real-database, artifact, gateway, host, queue, cache, build,
      target, and search integration tests. Evidence: durable round-trip.
  - [ ] B9.6.a Stand up production-like isolated database/artifact/evidence/
        gateway/queue/cache/search/build/target services plus supported host
        adapters. Evidence: environment manifest.
  - [ ] B9.6.b Exercise enrollment/session/command, intake/interchange/cache/
        validation, build/release/deploy/read-back, and search/audit lifecycle.
        Evidence: durable round-trip.
  - [ ] B9.6.c Exercise duplicate/reordered messages, concurrency, restart,
        stale cache/index, partial target, and cleanup/reconciliation. Evidence:
        fault cases.
  - [ ] B9.6.d Verify checksums, immutable lineage, tenancy, authorization,
        audit/outbox, and target truth at every boundary. Evidence: invariant
        report.
- [ ] B9.7 Run full-journey Playwright: host connect to verified release.
      Evidence: real or hermetic supported host adapter.
  - [ ] B9.7.a Provision a supported real or hermetic host/plugin/version with a
        known rights-cleared asset/scene, credentials, target, and expected
        measures. Evidence: fixture manifest.
  - [ ] B9.7.b Enroll/approve/connect the host, inspect health/capabilities,
        open scoped session, and execute typed pull/ingest commands. Evidence:
        Playwright trace.
  - [ ] B9.7.c Preview conversion/loss, run interchange, inspect source/USD/
        target diff and raw/current gates, and approve exact evidence. Evidence:
        review assertions.
  - [ ] B9.7.d Build candidate/evidence, deploy, run target smoke/fidelity,
        independently read back, and assert verified release. Evidence: journey
        report.
- [ ] B9.8 Run full-journey Playwright: host failure, reconnect, rerun, compare,
      waive/reject, and release. Evidence: exact state.
  - [ ] B9.8.a Inject disconnect/crash during a deterministic command or stage
        and assert session/command/job/attempt/partial/unknown state. Evidence:
        failure trace.
  - [ ] B9.8.b Reconnect/restart, reconcile host/project/object/artifact state,
        and rerun/resume from the safe boundary. Evidence: recovery assertions.
  - [ ] B9.8.c Compare exact failed/prior/new revisions and inspect raw fidelity
        loss with semantic parity. Evidence: comparison trace.
  - [ ] B9.8.d Exercise eligible scoped waiver and non-waivable rejection,
        expiry/staleness, and named approval. Evidence: decision assertions.
  - [ ] B9.8.e Release only the eligible new revision and prove failed/stale/
        unwaived outputs cannot publish. Evidence: exact-state report.
- [ ] B9.9 Run full-journey Playwright: artifact quarantine/recall and
      downstream reconciliation. Evidence: target verification.
  - [ ] B9.9.a Release a fixture artifact into library, dependent project/build/
        package, and supported target with traceable consumer refs. Evidence:
        setup trace.
  - [ ] B9.9.b Initiate quarantine or recall from an exact finding/revision and
        assert blast-radius preview, authority, step-up, and audit. Evidence:
        action trace.
  - [ ] B9.9.c Assert new use/build/release is blocked and active/derived/
        deployed state transitions per policy with explicit partial results.
        Evidence: propagation assertions.
  - [ ] B9.9.d Retry unreachable recipients and independently read back every
        target/consumer until closure or visible unresolved state. Evidence:
        reconciliation report.
- [ ] B9.10 Run WCAG 2.2 AA, keyboard, announcement, non-drag, and semantic
      preview parity suites. Evidence: themes/viewports/locales.
  - [ ] B9.10.a Run automated WCAG checks across authenticated roles/routes,
        themes, locales, and wide/compact/single-column viewports. Evidence:
        accessibility report.
  - [ ] B9.10.b Complete keyboard-only host/session/command, DAG, preview/diff,
        gate/waiver, batch/schedule, release/recall journeys. Evidence: keyboard
        traces.
  - [ ] B9.10.c Verify names/roles/values, focus order/restoration, live
        announcements, errors, step-up dialogs, and stale/partial/failure state.
        Evidence: assistive-technology matrix.
  - [ ] B9.10.d Verify non-drag alternatives, 200–400% zoom/reflow, touch
        targets, color independence, reduced motion, and semantic graph/3D/image
        parity. Evidence: conformance suite.
- [ ] B9.11 Run fleet, concurrent session, command, transfer, large scene,
      conversion, batch, build, and search load tests. Evidence: backpressure
      and SLO.
  - [ ] B9.11.a Define representative/maximum host fleet, tenants, session/
        command mixes, artifact/scene sizes, dependencies, network, batch/build,
        search corpus, resources, and budgets. Evidence: load plan.
  - [ ] B9.11.b Measure enrollment/heartbeat/session/gateway/command streaming
        and artifact transfer under sustained/burst/slow/abusive clients.
        Evidence: control-plane report.
  - [ ] B9.11.c Measure scan/resolve/convert/cache/validate/preview/search,
        batch/scheduling/build/package/deploy/read-back under load. Evidence:
        data-plane report.
  - [ ] B9.11.d Verify quotas/fairness/backpressure/circuit breakers/cancel/
        recovery, bounded memory/storage/logs, correctness, cost, and SLO.
        Evidence: overload report.
- [ ] B9.12 Run command injection, host spoof, plugin tamper, malicious
      asset/USD/archive, path, SSRF, secret leak, artifact tamper, auth, tenant,
      and release bypass tests. Evidence: zero blockers.
  - [ ] B9.12.a Test command/argument/schema confusion, shell/script/eval/
        console escape, expected-revision bypass, and unsafe compensation.
        Evidence: command security suite.
  - [ ] B9.12.b Test enrollment/host/session spoof, credential replay/theft,
        attestation/signature bypass, plugin downgrade/tamper, and compromised
        host results. Evidence: identity/supply-chain suite.
  - [ ] B9.12.c Test malicious files/archives/USD/resolvers/dependencies,
        traversal/symlink/Unicode paths, SSRF/rebinding/redirect, and parser/
        decompression abuse. Evidence: content security suite.
  - [ ] B9.12.d Test secret/path/content leakage through APIs/UI/search/logs/
        traces/events/support bundles and two-tenant isolation. Evidence:
        disclosure suite.
  - [ ] B9.12.e Test artifact/evidence/signature/target read-back tamper and
        gate/waiver/approval/release/recall bypass. Evidence: release security
        suite with zero blockers.
- [ ] B9.13 Run gateway/host/worker loss, artifact corruption, cache loss,
      backup/restore, release rollback, and recall drills. Evidence: RPO/RTO.
  - [ ] B9.13.a Inject gateway/host/worker process/network/resource loss during
        sessions, commands, transfer, conversion, build, and commit. Evidence:
        compute drill.
  - [ ] B9.13.b Corrupt/lose staged, authoritative, cached, packaged, and
        deployed artifact bytes and verify checksum detection/quarantine/
        recomputation/recovery. Evidence: artifact drill.
  - [ ] B9.13.c Restore databases/artifacts/evidence/config/keys/indexes/queues
        from backup and reconcile sessions/jobs/builds/search/audit/targets.
        Evidence: restore report.
  - [ ] B9.13.d Roll back failed release and execute quarantine/recall during
        active downstream use with per-target read-back. Evidence: release
        drill.
  - [ ] B9.13.e Measure achieved RPO/RTO, data loss, duplicate work, target
        inconsistency, and unresolved consumers against objectives. Evidence:
        scorecard.
- [ ] B9.14 Publish host install, bridge, interchange, fidelity, build, release,
      security incident, and recovery runbooks. Evidence: game-day.
  - [ ] B9.14.a Assign owner/audience/prerequisites/triggers/authority/
        diagnostics/actions/safety/escalation/rollback to each runbook.
        Evidence: template audit.
  - [ ] B9.14.b Write per-host install/update/revoke and bridge/session/command/
        gateway operational runbooks. Evidence: reviewed docs.
  - [ ] B9.14.c Write intake/interchange/dependency/fidelity/validator/waiver
        and build/render/package/release/target runbooks. Evidence: reviewed
        docs.
  - [ ] B9.14.d Write security/privacy/supply-chain incident, quarantine/recall,
        backup/restore/disaster/dependency-outage runbooks. Evidence: reviewed
        docs.
  - [ ] B9.14.e Execute representative game-days, remediate deviations, and
        revalidate every command/link/owner. Evidence: exercise report.
- [ ] B9.15 Produce engineering-readiness evidence per supported host/target
      pair. Evidence: incomplete pairs cannot inherit another pair's proof.
  - [ ] B9.15.a Build one evidence bundle per exact host/plugin/OS/server/
        protocol/source-target/profile tuple with owners and support window.
        Evidence: pair registry.
  - [ ] B9.15.b Map requirements to design/code/contracts/migrations/known-
        value/fidelity/security/accessibility/performance/recovery/runbook proof
        with exact versions. Evidence: traceability index.
  - [ ] B9.15.c Include command-by-command, conversion/round-trip, build/deploy,
        target smoke/read-back, limitations, waivers, and approvals per pair.
        Evidence: readiness bundle.
  - [ ] B9.15.d Surface failed/stale/missing/unavailable evidence explicitly and
        block the pair without borrowing another pair's proof. Evidence:
        independence gate.
  - [ ] B9.15.e Reproduce and verify bundles/checksums/links from authoritative
        evidence. Evidence: clean-room generation.
- [ ] B9.16 Obtain named DCC/engine, pipeline, security, accessibility,
      engineering, and product approvals. Evidence: human signoff.
  - [ ] B9.16.a Define approver scope/competence/independence, required
        evidence, supported host/target coverage, decision options, conditions,
        and expiry. Evidence: approval policy.
  - [ ] B9.16.b Present the exact release candidate/readiness bundles,
        compatibility matrix, limitations, risks, waivers, and unresolved items.
        Evidence: review packet.
  - [ ] B9.16.c Capture named signed approve/reject/conditional decisions with
        role, time, exact revisions/pairs, rationale, and conditions. Evidence:
        signoff records.
  - [ ] B9.16.d Invalidate/renew approvals after material code/config/profile/
        host/target/evidence/risk changes. Evidence: staleness tests.
- [ ] B9.17 Run distinct full journeys for render queue, gameplay systems,
      virtual-production stage, XR deployment, mocap stream/record/retarget,
      MetaHuman, DaVinci conform, Houdini cook, audio/video conversion,
      cross-domain/lore compilation, agent/MCP dry-run approval, package
      install/rollback, events, and SDK clients. Evidence: no generic bridge or
      build journey substitutes for specialized proof.
  - [ ] B9.17.a Run independent render-queue and gameplay-system journeys with
        specialized resources, failures, quality/scenarios, target read-back,
        and rollback. Evidence: production traces.
  - [ ] B9.17.b Run independent VP-stage, each XR-platform deployment, and each
        mocap vendor live/record/retarget journey with device safety, consent,
        faults, and target read-back. Evidence: device traces.
  - [ ] B9.17.c Run independent MetaHuman/Live Link, DaVinci conform/delivery,
        Houdini cook/farm, and A/V conversion/package journeys. Evidence:
        creative traces.
  - [ ] B9.17.d Run cross-domain/lore compilation through producer contracts,
        mappings, build/deploy/read-back, and correction/recall propagation.
        Evidence: integration trace.
  - [ ] B9.17.e Run agent/MCP intent-to-plan/dry-run/approval/execution/
        intervention/read-back and adversarial boundaries. Evidence: agent
        trace.
  - [ ] B9.17.f Run package fresh install/upgrade/uninstall/rollback, event
        publish/deliver/replay/dead-letter, and every external SDK/CLI/plugin
        client. Evidence: ecosystem traces.
  - [ ] B9.17.g Prohibit generic bridge/build success from satisfying any
        missing specialized journey. Evidence: coverage gate.
- [ ] B9.18 Run agent/remote-control adversarial suites for ambiguous intent,
      prompt/tool injection, stale selection/revision, hidden destructive side
      effect, approval confusion, privilege expansion, organization/spend
      bypass, unsafe path/network target, late result, host compromise, and
      audit tamper. Evidence: fail-closed thresholds and incident drills.
  - [ ] B9.18.a Test ambiguous/conflicting/incomplete intent, wrong context,
        hallucinated object, prompt/content/tool-output injection, and
        unsupported goal; require safe clarification/refusal. Evidence: intent
        suite.
  - [ ] B9.18.b Test stale selection/revision, TOCTOU between dry-run/approval/
        execution, hidden/described-mismatched side effects, irreversible
        actions, and approval scope/confusion. Evidence: plan integrity suite.
  - [ ] B9.18.c Test role/session/command/path/property privilege expansion,
        cross-tenant/org policy evasion, step-up/dual-control bypass, and
        chained-tool authority laundering. Evidence: authorization suite.
  - [ ] B9.18.d Test token/time/cost/spend/capacity budget evasion, parallel
        fan-out, retry loops, late results, cancellation races, and partial
        completion disguised as success. Evidence: resource/control suite.
  - [ ] B9.18.e Test traversal/SSRF/rebinding/redirect/credential exfiltration,
        malicious host/plugin compromise, falsified results/read-back, and
        quarantine escape. Evidence: execution security suite.
  - [ ] B9.18.f Test audit suppression/redaction abuse/reordering/deletion/
        tamper and ensure immutable intent/plan/approval/tool/result/target
        reconstruction. Evidence: audit integrity suite.
  - [ ] B9.18.g Ratify fail-closed thresholds, run pause/kill/quarantine/recall/
        recovery incident drills, and close every release blocker. Evidence:
        adversarial release report.
- [ ] B9.19 EVE-SEAM-EXIT: require "required Eve reads and writes admitted with
      source and behavior evidence, or an exact task-0.8 explicit non-goal
      proves zero operations; a named defer preserves ownership but never passes
      completion" before this phase exits.
      Evidence: source-derived totality record and passing verifier.
  - [ ] B9.19.a Re-run the route, bridge, state-signal, and complete domain-code
        envelope census; review every changed candidate instead of carrying a
        stale phase disposition forward. Evidence: current ratchets.
  - [ ] B9.19.b Bind the exact ratified task-0.8 workflow and requirement set;
        classify it as a required seam or an exact source-backed non-goal.
        Evidence: charter-bound disposition set.
  - [ ] B9.19.c For a required seam, admit both read and write operations with
        byte-pinned source and behavior evidence. A named defer retains its owner
        and unblock condition but fails completion. Evidence: operation proofs.
  - [ ] B9.19.d Permit a zero-operation boundary only for an exact ratified
        task-0.8 explicit non-goal; absence or unavailability is not a non-goal.
        Evidence: source-bound non-goal proof.
  - [ ] B9.19.e Run the totality verifier, every retained gate-case CLI, and the
        injected durable-candidate control; retain red and green receipts.
        Evidence: release gate logs.

---

## 5. Cross-domain integration completion

- [ ] X.1 Prove Isis-generated assets enter Yemaya projects with immutable
      generation and rights provenance. Evidence: cross-domain contract,
      integration test, and traced journey.
  - [ ] X.1.a Define the Isis export/Yemaya intake contract for immutable asset/
        generation revisions, ingredients, model/tool/config/seed, rights/
        license, disclosures, checksums, and lifecycle. Evidence: contract.
  - [ ] X.1.b Authorize and transfer the minimal tenant/project-scoped manifest
        idempotently without granting Yemaya Isis generation authority.
        Evidence: seam tests.
  - [ ] X.1.c Create a Yemaya project reference/import revision that retains
        canonical Isis identity and provenance. Evidence: integration test.
  - [ ] X.1.d Propagate Isis supersession/revoke/quarantine/recall and verify
        Yemaya project/derivative state by read-back. Evidence: traced journey.
- [ ] X.2 Prove Yemaya-promoted assets enter Oshun output gallery through the
      editorial boundary. Evidence: cross-domain contract, integration test, and
      traced journey.
  - [ ] X.2.a Define the Yemaya promotion/gallery-ingest contract with exact
        asset/project/release revisions, editorial decision, rights, quality,
        provenance, renditions, locale, and lifecycle. Evidence: contract.
  - [ ] X.2.b Require authorized editorial promotion and current gallery
        eligibility without granting the gallery Yemaya mutation authority.
        Evidence: boundary tests.
  - [ ] X.2.c Materialize permission-aware gallery projection/renditions and
        verify displayed/downloaded bytes/metadata against the manifest.
        Evidence: integration journey.
  - [ ] X.2.d Propagate supersession/unpublish/revoke/recall and reconcile
        search/cache/gallery/consumer state. Evidence: read-back test.
- [ ] X.3 Prove Aja motion assets can enter Yemaya/Bellona without bypassing
      consent, watermark, or validation. Evidence: cross-domain contract,
      integration test, and traced journey.
  - [ ] X.3.a Define Aja motion export contract with exact artifact/skeleton/
        mapping/profile/quality/consent/rights/watermark/provenance revisions
        and intended consumers. Evidence: contract.
  - [ ] X.3.b Enforce current consent, quality, compatibility, disclosure, and
        watermark gates at Aja handoff and Yemaya/Bellona intake. Evidence:
        negative tests.
  - [ ] X.3.c Import/reference through public Yemaya and Bellona contracts while
        preserving Aja authority and immutable lineage. Evidence: integrations.
  - [ ] X.3.d Propagate subject/consent/quality/artifact revoke or recall to
        projects/builds/packages/targets and verify closure. Evidence: traced
        journey.
- [ ] X.4 Prove Bellona can package Yemaya/Aja/Isis artifacts without mutating
      their authoritative lineage. Evidence: cross-domain contract, integration
      test, and traced journey.
  - [ ] X.4.a Define a common immutable producer-artifact reference contract
        with domain authority, exact revision/checksum, dependencies, rights/
        consent/canon/quality, and recall endpoint. Evidence: contract.
  - [ ] X.4.b Resolve and lock producer refs through public APIs without copying
        or rewriting authoritative provenance records. Evidence: resolver tests.
  - [ ] X.4.c Build Bellona package manifests/representations with conversion
        lineage pointing back to each producer identity. Evidence: integration
        test.
  - [ ] X.4.d Read back package/target and prove source authority/provenance
        unchanged; propagate producer recall. Evidence: traced journey.
- [ ] X.5 Prove Metis media uses curated Isis and Yemaya surfaces rather than
      raw generation machinery. Evidence: cross-domain contract, integration
      test, and traced journey.
  - [ ] X.5.a Define the curated-media discovery/selection contract with
        approved asset/release/rendition, audience/locale, rights/consent,
        accessibility, correctness, and provenance. Evidence: contract.
  - [ ] X.5.b Restrict Metis identity/capabilities to curated search/preview/use
        and prohibit raw generation/model/job/configuration endpoints. Evidence:
        least-privilege tests.
  - [ ] X.5.c Select and attach exact eligible Isis/Yemaya media revisions to a
        Metis lesson through public seams. Evidence: integration journey.
  - [ ] X.5.d Propagate media correction/unpublish/revoke/recall and verify
        learner rendering/catalog/cache reconciliation. Evidence: read-back.
- [ ] X.6 Prove Metis embodied instruction calls Aja only through the canonical
      adapter and handles degraded health. Evidence: cross-domain contract,
      integration test, and traced journey.
  - [ ] X.6.a Define/version the canonical health/capability, demonstration,
        overlay, asynchronous handoff/status/result/cancel contracts. Evidence:
        adapter spec.
  - [ ] X.6.b Prevent Metis imports/credentials/calls to Aja internal jobs,
        models, media, stores, or privileged APIs. Evidence: architecture and
        auth tests.
  - [ ] X.6.c Run demonstration/overlay/handoff journeys with idempotency,
        correlation, consent/quality/provenance, and intended-use boundaries.
        Evidence: integration trace.
  - [ ] X.6.d Handle healthy/degraded/unavailable/incompatible/stale/late result
        states without fabricating guidance or success. Evidence: failure
        journey.
- [ ] X.7 Prove Veritas and Metis grounding share generic mechanics while
      retaining distinct source/claim policies. Evidence: cross-domain contract,
      integration test, and traced journey.
  - [ ] X.7.a Define shared source ingestion, chunking, retrieval, citation,
        claim-evidence, revision, correction, audit, and evaluator primitives.
        Evidence: generic contracts.
  - [ ] X.7.b Define separate Veritas publication/source-risk and Metis
        pedagogy/curriculum/learner claim policies, roles, thresholds, and
        outputs. Evidence: policy contracts.
  - [ ] X.7.c Run identical mechanical fixtures through both domains and prove
        shared behavior where intended. Evidence: integration comparison.
  - [ ] X.7.d Run policy-divergent fixtures and prove neither domain inherits
        the other's admissibility, verdict, review, or release decision.
        Evidence: boundary journey.
- [ ] X.8 Prove Euterpe-generated or edited audio carries rights/provenance when
      consumed by Isis, Yemaya, Veritas, or Metis. Evidence: cross-domain
      contract, integration test, and traced journey.
  - [ ] X.8.a Define Euterpe audio handoff with source/voice/performer/model/
        edit revisions, rights/license/consent, generated/edited ranges,
        disclosure, quality, renditions, and checksum. Evidence: contract.
  - [ ] X.8.b Enforce consumer-specific intended use, locale, accessibility,
        quality, rights, and consent at each intake. Evidence: eligibility
        tests.
  - [ ] X.8.c Run independent Isis, Yemaya, Veritas, and Metis consumption
        journeys retaining canonical Euterpe lineage. Evidence: integration
        matrix.
  - [ ] X.8.d Propagate source/voice/performer rights or consent revoke and
        verify every derivative/publication/lesson/project. Evidence: recall
        trace.
- [ ] X.9 Prove identity, tenant, workspace, locale, and trace context survive
      every registered cross-domain handoff. Evidence: cross-domain contract,
      integration test, and traced journey.
  - [ ] X.9.a Register every handoff with allowed identity/service actor,
        tenant/project/workspace mapping, locale/time zone, trace/baggage, and
        data-classification rules. Evidence: context registry.
  - [ ] X.9.b Propagate authenticated actor/on-behalf-of and least-privilege
        service identity without forwarding reusable credentials. Evidence:
        identity tests.
  - [ ] X.9.c Map tenant/workspace/object scopes explicitly and reject missing,
        ambiguous, stale, or cross-tenant context. Evidence: isolation tests.
  - [ ] X.9.d Preserve locale/time zone and tenant-safe trace/causation through
        async queues/webhooks/retries while bounding/redacting baggage.
        Evidence: traced matrix.
- [ ] X.10 Prove cross-domain retries are idempotent and do not duplicate jobs,
      assets, reviews, releases, grades, or notifications. Evidence:
      cross-domain contract, integration test, and traced journey.
  - [ ] X.10.a Define operation-specific idempotency scope/key/request hash,
        retention window, response replay, conflict, and retry-safe boundary.
        Evidence: contract.
  - [ ] X.10.b Persist producer outbox and consumer inbox/dedupe state
        transactionally with domain mutation. Evidence: atomicity tests.
  - [ ] X.10.c Inject duplicate, delayed, reordered, lost-ack, timeout, crash,
        and concurrent retry at every handoff. Evidence: fault matrix.
  - [ ] X.10.d Verify authoritative counts/identities and downstream read-back
        show one job/asset/review/release/grade/notification side effect.
        Evidence: traced journey.
- [ ] X.11 Prove source, consent, rights, model, canon, or artifact revocation
      reaches every registered downstream consumer. Evidence: cross-domain
      contract, integration test, and traced journey.
  - [ ] X.11.a Maintain a complete versioned consumer/dependency registry from
        each revocable authority to derivatives, caches, indexes, packages,
        releases, channels, targets, and external consumers. Evidence: registry.
  - [ ] X.11.b Emit signed minimal revocation events with authority, exact
        subject/revision/scope, effective time, reason class, replacement, and
        correlation. Evidence: event contract.
  - [ ] X.11.c Compute impact and execute fail-closed quarantine/unpublish/
        recall/recompute/review actions per consumer with checkpoint/retry.
        Evidence: propagation tests.
  - [ ] X.11.d Verify every consumer/target by read-back and retain unreachable/
        failed recipients as unresolved incidents. Evidence: closure report.
- [ ] X.12 Prove cross-domain search and notifications do not leak protected
      titles, counts, source identities, learner data, biometric data, or
      unreleased media. Evidence: cross-domain contract, integration test, and
      traced journey.
  - [ ] X.12.a Define allowed search/notification fields and prohibited titles/
        counts/identity/learner/biometric/unreleased/content metadata by
        domain/role/purpose. Evidence: data policy.
  - [ ] X.12.b Build authorization-filtered minimal projections before indexing
        or notification fan-out. Evidence: projection tests.
  - [ ] X.12.c Test query/suggestion/facet/count/snippet/timing/error and
        notification recipient/subject/body/link/preview inference attacks.
        Evidence: leakage suite.
  - [ ] X.12.d Propagate permission/release/consent/revocation changes to
        indexes, caches, digests, queued notifications, and external delivery.
        Evidence: reconciliation journey.
- [ ] X.13 Prove deep links enforce current authorization, tenant, expiry, and
      object existence instead of trusting embedded context. Evidence:
      cross-domain contract, integration test, and traced journey.
  - [ ] X.13.a Register allowed issuer/audience/route/purpose/context fields,
        maximum lifetime, return behavior, and prohibited secrets/authority.
        Evidence: link policy.
  - [ ] X.13.b Mint signed links with tenant/project/object/revision, audience,
        purpose, expiry, nonce, and key version. Evidence: signature tests.
  - [ ] X.13.c Validate signature/audience/tenant/expiry/replay plus current
        actor authorization, object existence/revision, and lifecycle at load.
        Evidence: negative matrix.
  - [ ] X.13.d Render safe forbidden/expired/missing/stale outcomes without
        existence leakage and require fresh authorization for any mutation.
        Evidence: browser journey.
- [ ] X.14 Prove audit and trace records reconstruct each cross-domain journey
      without storing protected content. Evidence: cross-domain contract,
      integration test, and traced journey.
  - [ ] X.14.a Define common correlation/causation/resource revision/event time/
        actor/service/domain/action/outcome fields and domain-owned audit event
        mappings. Evidence: audit contract.
  - [ ] X.14.b Apply structured allowlists/redaction/hashing to content, source/
        subject identity, biometrics, learner data, free text, secrets, URLs,
        and paths before export. Evidence: redaction tests.
  - [ ] X.14.c Preserve immutable ordered evidence refs through synchronous and
        asynchronous calls, queues, webhooks, retries, compensation, and recall.
        Evidence: trace continuity test.
  - [ ] X.14.d Reconstruct representative successful/failed/partial/retried/
        revoked journeys using refs and authorized drill-down, not copied
        protected content. Evidence: investigation exercise.
- [ ] X.15 Prove version negotiation and compatibility windows across all
      producer/consumer pairs. Evidence: compatibility matrix.
  - [ ] X.15.a Register every producer/consumer contract/profile/event/SDK pair
        with supported versions, capabilities, constraints, owner, environment,
        and support dates. Evidence: matrix.
  - [ ] X.15.b Negotiate the highest mutually approved version/capability
        deterministically and bind it to the handoff. Evidence: selection tests.
  - [ ] X.15.c Test current/prior/next unknown fields, optional capability,
        deprecation, expiry, downgrade, and unsupported combinations per pair.
        Evidence: skew suite.
  - [ ] X.15.d Surface expiring/incompatible consumers and block silent coercion
        or use outside the compatibility window. Evidence: operational gate.
- [ ] X.16 Run end-to-end failure drills across provider, queue, storage,
      search, host, target, and publisher dependencies. Evidence: no silent
      success or irrecoverable partial state.
  - [ ] X.16.a Define failure injection points and expected state/ownership/
        retry/compensation/recovery for each dependency and handoff. Evidence:
        drill plan.
  - [ ] X.16.b Inject provider and queue timeout/unavailable/duplicate/reorder/
        poison/dead-letter cases during representative journeys. Evidence:
        messaging report.
  - [ ] X.16.c Inject database/blob/cache/search/index corruption/loss/staleness
        and reconcile authoritative state. Evidence: storage/search report.
  - [ ] X.16.d Inject host/worker/target/publisher outage, partial commit,
        unknown outcome, and late result. Evidence: external dependency report.
  - [ ] X.16.e Verify UI/API/events/audit expose exact failed/partial/degraded
        state and recovery/read-back reaches closure within objectives.
        Evidence: end-to-end scorecard.
- [ ] X.17 Prove Metis lecture, diagram, simulation, voice, and other teaching
      media use Isis/Yemaya/Euterpe provider and production seams with
      source/correctness/accessibility/consent lineage intact. Evidence:
      contract, traced journey, revocation, and learner-render read-back.
  - [ ] X.17.a Define separate lecture/diagram/simulation/image/video/audio/
        voice handoff profiles with source/claim/correctness/accessibility/
        consent/rights/provenance requirements. Evidence: contracts.
  - [ ] X.17.b Route generation/production only through authorized Isis/Yemaya/
        Euterpe public seams and retain Metis pedagogical/editorial authority.
        Evidence: boundary tests.
  - [ ] X.17.c Run each media type from source-linked request through candidate/
        review/validation/lesson attachment and accessible learner render.
        Evidence: traced matrix.
  - [ ] X.17.d Propagate source correction/withdrawal and media/voice/consent
        revocation into lessons/catalogs/caches and verify learner read-back.
        Evidence: recall journey.
- [ ] X.18 Prove Veritas podcast, video, live, social, and multilingual assets
      use governed Euterpe/Yemaya/Isis production seams without granting those
      systems protected-source or publication authority. Evidence:
      least-privilege contract and correction/retraction journey.
  - [ ] X.18.a Define separate podcast/video/live/social/multilingual production
        requests containing only approved script/claim/source-safe refs,
        publication intent, locale, rights, and accessibility requirements.
        Evidence: contracts.
  - [ ] X.18.b Enforce least-privilege provider identities/views that cannot
        access protected sources or decide publication/correction. Evidence:
        authorization tests.
  - [ ] X.18.c Run each asset type through Euterpe/Yemaya/Isis production,
        Veritas review/gates, channel packaging/publication, and target read-
        back. Evidence: traced matrix.
  - [ ] X.18.d Propagate Veritas correction/retraction and provider asset/voice/
        consent recall without transferring editorial authority. Evidence:
        lifecycle journey.
- [ ] X.19 Prove Yemaya's engine/DCC, virtual-production, XR, mocap, render, and
      build actions use Bellona public contracts and Aja handoffs without
      duplicating bridge/runtime authority. Evidence: host-specific traced
      journeys and version/failure behavior.
  - [ ] X.19.a Define Yemaya-to-Bellona project/asset/action/target requests and
        Bellona status/result/evidence contracts with exact revisions and
        ownership. Evidence: seam spec.
  - [ ] X.19.b Define Aja motion/capture handoff contracts and prevent Yemaya or
        Bellona from duplicating Aja consent/quality authority. Evidence:
        boundary tests.
  - [ ] X.19.c Run engine/DCC, VP, XR, mocap, render, and build journeys through
        public typed commands per supported host/profile. Evidence: traces.
  - [ ] X.19.d Exercise version incompatibility, host/device/resource loss,
        partial target, cancel/rollback, and read-back without duplicate runtime
        control. Evidence: fault matrix.
- [ ] X.20 Prove Bellona cross-domain and lore-to-engine compilation preserves
      Hathor/Yemaya/Isis/Aja source entity IDs, canon, generated ingredients,
      consent, rights, revisions, mappings, and target artifacts. Evidence:
      deterministic evidence bundle and downstream read-back.
  - [ ] X.20.a Define producer-specific source manifests with stable entity/
        revision/canon/ingredient/consent/rights/artifact identities and
        correction/revoke hooks. Evidence: contracts.
  - [ ] X.20.b Record every normalized intermediate and source-to-target
        entity/property/asset mapping, default, substitution, omission, and
        human decision. Evidence: mapping ledger.
  - [ ] X.20.c Compile with pinned toolchain/templates/adapters/models/config/
        seeds/environment and preserve deterministic or disclosed
        nondeterministic step provenance. Evidence: compilation trace.
  - [ ] X.20.d Assemble signed evidence and read back target entities/assets/
        state to verify all source IDs/revisions/canon/rights/mappings.
        Evidence: deterministic bundle.
  - [ ] X.20.e Propagate source/canon/consent/rights/artifact changes through
        recompilation/quarantine/recall and verify target closure. Evidence:
        lifecycle test.
- [ ] X.21 Prove Veritas correction/retraction and Metis source withdrawal
      propagate into generated audio/video/lecture/diagram/simulation,
      newsletters/social/B2B, catalogs, and other registered derivatives.
      Evidence: complete consumer registry, saga, and verified read-back.
  - [ ] X.21.a Register every derivative type/channel/catalog/external consumer
        with producer source/claim refs, authoritative mutation endpoint,
        correction action, read-back, and owner. Evidence: zero-orphan registry.
  - [ ] X.21.b Emit versioned correction/retraction/withdrawal events with exact
        scope/effective time/replacement/reason/correlation and no protected
        payload. Evidence: event contract.
  - [ ] X.21.c Traverse generated media/teaching assets/publications/social/B2B/
        catalog derivatives and preview required update/unpublish/recompute/
        notification. Evidence: impact report.
  - [ ] X.21.d Execute a resumable idempotent propagation saga with per-consumer
        retry/compensation and explicit unreachable/partial state. Evidence:
        saga tests.
  - [ ] X.21.e Verify every registered target by read-back and keep incomplete
        closure as an incident. Evidence: reconciliation report.
- [ ] X.22 Prove Euterpe and Aja biometric/voice/performer/subject consent
      revocation reaches Isis-generated derivatives, Yemaya projects, Bellona
      packages/builds, Metis lessons, and Veritas media. Evidence: impact
      preview and fail-closed recall journey.
  - [ ] X.22.a Maintain protected dependency edges from Euterpe voice/ performer
        and Aja subject/source consent grants to all registered derivatives/uses
        without exposing identity. Evidence: dependency audit.
  - [ ] X.22.b Ingest revocation with authenticated authority, exact grant/
        subject-safe scope, effective time, purpose/region, replacement, and
        legal-hold semantics. Evidence: revocation contract.
  - [ ] X.22.c Preview affected Isis assets, Yemaya projects, Bellona artifacts/
        packages/builds/targets, Metis lessons, and Veritas publications with
        release risk. Evidence: impact view.
  - [ ] X.22.d Immediately block new use/export/release and execute quarantine/
        unpublish/recall/recompute across domains with retry. Evidence: fail-
        closed journey.
  - [ ] X.22.e Verify every store/cache/index/channel/target by read-back while
        preserving protected audit/legal-hold evidence. Evidence: closure
        report.
- [ ] X.23 Prove marketplaces, plugins/models, external SDK clients, webhooks,
      remote agents, DCC hosts, devices, distributors, and channel adapters use
      common identity/tenant/version/audit principles without sharing secrets or
      authority implicitly. Evidence: external-boundary contract and adversarial
      matrix.
  - [ ] X.23.a Define common external principal/device/workload identity,
        enrollment/authentication, tenant/project scope, capability grant,
        version negotiation, secret ownership, audit, and revoke primitives.
        Evidence: boundary standard.
  - [ ] X.23.b Define specialized profiles for marketplaces/models/plugins,
        SDK/webhook clients, agents/MCP, DCC/devices, and distributors/channels
        without collapsing their risks or authority. Evidence: profile matrix.
  - [ ] X.23.c Prove credentials/secrets are audience/environment/tenant/
        purpose-bounded, independently rotated, never forwarded, and cannot
        confer another integration's authority. Evidence: secret tests.
  - [ ] X.23.d Test spoof/replay/version downgrade, tenant confusion, privilege
        expansion, tool/content injection, supply-chain tamper, duplicate side
        effect, audit tamper, and revoke per profile. Evidence: adversarial
        matrix.
  - [ ] X.23.e Reconcile lifecycle/compatibility/security status across all
        external boundaries and surface unsupported/expired actors. Evidence:
        operational report.
- [ ] X.24 Prove domain-specific taxonomies remain interoperable through
      versioned mappings rather than collapsed into a universal lossy enum.
      Evidence: round-trip fixtures for assets, media, rights, people/roles,
      locale, status, quality, and release concepts.
  - [ ] X.24.a Define independently owned/versioned domain taxonomies with
        stable IDs, meanings, aliases, constraints, lifecycle, and code-owned
        floors. Evidence: taxonomy registry.
  - [ ] X.24.b Define pairwise/contextual mapping records with source/
        destination versions, exact/broader/narrower/compound/unmapped/
        prohibited relation, conditions, and owner. Evidence: mapping schema.
  - [ ] X.24.c Require explicit unmapped/ambiguous/loss states and human/policy
        decision instead of a universal fallback enum. Evidence: negative tests.
  - [ ] X.24.d Round-trip asset/media/rights/people-role/locale/status/quality/
        release fixtures and verify preserved identity or declared loss.
        Evidence: fixture matrix.
  - [ ] X.24.e Preview and migrate mapping/taxonomy version changes across
        contracts/search/data/gates/releases with compatibility windows.
        Evidence: lifecycle journey.

---

## 6. Rollout waves and final definition of done

Recommended waves:

1. Baseline, standards, scope decisions, and shared kit.
2. Tara migration proof.
3. Metis and Veritas, because their V1 authoring scope and grounding reuse are
   the clearest.
4. Isis integration across the existing three audience hosts.
5. Yemaya AAA-suite reconciliation plus study/case additions inside the existing
   Studio.
6. Aja and Bellona operations consoles, with supported-host matrices.
7. Euterpe integration and release profiles in its existing Studio.
8. Cross-domain revocation, recovery, performance, and launch exercises.

- [ ] DONE.1 Every checkbox claimed complete links to authoritative current
      evidence. Evidence: automated missing-evidence audit.
  - [ ] DONE.1.a Parse every parent/child checkbox and require exactly one
        non-placeholder evidence statement or immutable evidence reference.
        Evidence: ledger schema audit.
  - [ ] DONE.1.b Resolve referenced files/URLs/records, revisions/checksums,
        owners, dates, and environment against authoritative current sources.
        Evidence: evidence-link audit.
  - [ ] DONE.1.c Mark stale, missing, unverifiable, superseded, revoked, or
        unauthorized evidence as non-complete. Evidence: negative fixtures.
  - [ ] DONE.1.d Block a completed parent while any required child/evidence is
        incomplete. Evidence: hierarchy audit.
- [ ] DONE.2 Every discovered package, route, app, contract, migration, job,
      gate, publisher, and critical journey has an explicit disposition.
      Evidence: zero unclassified inventory rows.
  - [ ] DONE.2.a Regenerate normalized inventories from repository, deployment,
        contract/schema, migration, job/event, gate, publisher, and test
        sources. Evidence: inventory manifests.
  - [ ] DONE.2.b Match each stable inventory row to exactly one retained/
        refactored/migrated/replaced/removed/external/deferred disposition with
        owner/rationale/destination. Evidence: mapping audit.
  - [ ] DONE.2.c Resolve duplicate, conflicting, obsolete, and newly discovered
        rows through domain-owner review. Evidence: disposition log.
  - [ ] DONE.2.d Prove no unclassified or orphaned rows and no ledger
        disposition lacks a source item or explicit net-new scope. Evidence:
        zero-orphan audit.
- [ ] DONE.3 Every capability in Section 3 is implemented per applicable domain
      or has an approved decision record with compensating control. Evidence:
      final release-evidence index and requirement audit.
  - [ ] DONE.3.a Map each Section 3 capability to every applicable domain/
        profile/host/target with owner and applicability rationale. Evidence:
        coverage matrix.
  - [ ] DONE.3.b Link implemented cells to exact design/code/schema/migration/
        test/operation evidence. Evidence: traceability audit.
  - [ ] DONE.3.c Link non-implemented cells to named approved decision, risk,
        compensating control, validation, expiry, and remediation owner.
        Evidence: exception audit.
  - [ ] DONE.3.d Block completion on missing, inherited, stale, or inapplicable-
        without-rationale cells. Evidence: final requirement gate.
- [ ] DONE.4 Every domain-specific algorithm has independently verified
      known-value tests. Evidence: final release-evidence index and requirement
      audit.
  - [ ] DONE.4.a Register every production algorithm/version/profile and its
        inputs, outputs, units, applicability, owner, and release consumers.
        Evidence: algorithm registry.
  - [ ] DONE.4.b Provide analytically, independently, or externally derived
        valid/boundary/invalid fixtures with expected values/tolerances and
        checksums. Evidence: fixture manifests.
  - [ ] DONE.4.c Run current implementations against fixtures in supported
        environments and retain raw results/version provenance. Evidence: test
        reports.
  - [ ] DONE.4.d Prove no algorithm inherits another algorithm/profile's proof
        and unavailable evaluation cannot pass. Evidence: coverage gate.
- [ ] DONE.5 Every model-based evaluator has current calibration, uncertainty,
      subgroup, threshold, drift, and human-review evidence. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.5.a Register exact model/evaluator/config/preprocessing/corpus/
        labels/splits/intended-use versions and protected-data approvals.
        Evidence: evaluator registry.
  - [ ] DONE.5.b Measure performance, calibration, uncertainty/abstention,
        subgroup error, out-of-distribution behavior, and human disagreement on
        current held-out data. Evidence: evaluation report.
  - [ ] DONE.5.c Ratify pass/fail/review thresholds from declared costs and
        prohibit low-confidence/unavailable from passing. Evidence: threshold
        record.
  - [ ] DONE.5.d Verify drift monitoring, expiry, replacement/canary/rollback,
        and historical replay. Evidence: operational proof.
- [ ] DONE.6 Every provider or external host is live in the approved environment
      or fails loudly as not configured/unavailable. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.6.a Register each required provider/host/target/environment with
        approved identity, endpoint, version/profile, credentials owner,
        capability, and health contract. Evidence: dependency registry.
  - [ ] DONE.6.b Run least-privilege live capability/health and representative
        operation probes in the approved environment. Evidence: probe reports.
  - [ ] DONE.6.c Exercise missing credentials/configuration, outage, timeout,
        incompatible version, degraded, and revoked states. Evidence: failure
        matrix.
  - [ ] DONE.6.d Prove APIs/UI/jobs/releases show explicit unavailable/not-
        configured state and never fixture or false success. Evidence: honesty
        gate.
- [ ] DONE.7 Every mutation path has auth, object/property authorization,
      idempotency/concurrency, audit, tenancy, and negative tests. Evidence:
      final release-evidence index and requirement audit.
  - [ ] DONE.7.a Inventory every UI/API/job/CLI/SDK/webhook/agent/host/database
        mutation and its authoritative surface. Evidence: mutation registry.
  - [ ] DONE.7.b Test authentication, role/capability, tenant/project, object,
        property, purpose/consent, step-up/dual control, and secret boundaries.
        Evidence: authorization matrix.
  - [ ] DONE.7.c Test idempotency, expected revision/optimistic concurrency,
        duplicate/reorder/retry, TOCTOU, cancellation, and late result.
        Evidence: race suite.
  - [ ] DONE.7.d Verify immutable redacted audit/outbox, target read-back, and
        two-tenant isolation for success/failure/partial/compensation. Evidence:
        integration proof.
  - [ ] DONE.7.e Resolve or block every untested/duplicate/bypass mutation path.
        Evidence: zero-gap gate.
- [ ] DONE.8 Every release/export path recomputes current gates, locks exact
      revisions, writes deterministically where possible, and verifies by target
      read-back. Evidence: final release-evidence index and requirement audit.
  - [ ] DONE.8.a Inventory every release/export/publish/deploy/package/channel
        path with target/profile/version and authoritative gate owner. Evidence:
        release registry.
  - [ ] DONE.8.b Recompute current quality, rights/consent, safety/integrity,
        security, compatibility, approval, and target gates immediately before
        execution. Evidence: freshness tests.
  - [ ] DONE.8.c Lock exact source/dependency/config/toolchain/model/adapter/
        validator/profile/approval/target revisions and checksums. Evidence:
        candidate manifests.
  - [ ] DONE.8.d Produce deterministic bytes/manifests/signatures where possible
        or declare and bound nondeterminism. Evidence: reproducibility reports.
  - [ ] DONE.8.e Independently read back target contents/state/identity/
        checksums/semantics and block transport-only success. Evidence: target
        verification matrix.
- [ ] DONE.9 Every revocation/correction/recall path reaches all registered
      downstream dependencies and proves closure by read-back. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.9.a Register every revocable source/consent/right/model/canon/
        artifact/claim/release authority and downstream derivative/consumer/
        target edge. Evidence: dependency registry.
  - [ ] DONE.9.b Test exact impact preview, authority, effective scope/time,
        replacement, legal hold, and fail-closed new-use block. Evidence:
        control tests.
  - [ ] DONE.9.c Execute resumable idempotent propagation with checkpoint,
        retry, compensation, notification, and explicit unreachable/partial
        state. Evidence: saga matrix.
  - [ ] DONE.9.d Read back every store/cache/index/package/channel/target/
        external consumer and keep unresolved closure as an incident. Evidence:
        reconciliation proof.
- [ ] DONE.10 Every critical browser journey has deep Playwright coverage
      against live or explicitly labeled hermetic dependencies. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.10.a Inventory critical role/domain/profile/host/target journeys,
        mutations, failures, mobile paths, and accessibility alternatives.
        Evidence: browser coverage matrix.
  - [ ] DONE.10.b Use live approved dependencies or explicit production-
        equivalent hermetic adapters with persistent fixture labeling and
        contract parity. Evidence: environment manifests.
  - [ ] DONE.10.c Assert authoritative backend/target state, exact revisions,
        evidence/lineage, authorization, idempotency, and read-back—not only
        screenshots or DOM. Evidence: Playwright traces.
  - [ ] DONE.10.d Cover
        loading/empty/stale/partial/degraded/unavailable/failure/
        retry/cancel/recovery and retain diagnostics/artifacts. Evidence: fault
        coverage.
  - [ ] DONE.10.e Eliminate or approve every critical unautomated gap with
        owner, reason, equivalent proof, and expiry. Evidence: final audit.
- [ ] DONE.11 Every applicable route meets WCAG 2.2 AA with keyboard,
      announcement, non-drag, zoom, theme, viewport, locale, and semantic
      alternative coverage. Evidence: final release-evidence index and
      requirement audit.
  - [ ] DONE.11.a Inventory authenticated/public routes, roles, states, complex
        widgets, visualizations, media, and critical actions by applicability.
        Evidence: accessibility matrix.
  - [ ] DONE.11.b Run automated WCAG checks across themes, locales, and wide/
        compact/single-column viewports. Evidence: automated reports.
  - [ ] DONE.11.c Complete keyboard/screen-reader journeys with focus,
        announcements, error recovery, step-up, status, and dynamic updates.
        Evidence: assistive-technology traces.
  - [ ] DONE.11.d Verify non-drag controls, 200–400% zoom/reflow, touch targets,
        color independence, reduced motion, captions/transcripts, and semantic
        graph/3D/canvas/media parity. Evidence: manual/automated results.
  - [ ] DONE.11.e Close or explicitly block every severity/applicability gap; no
        domain/profile inherits another's evidence. Evidence: release gate.
- [ ] DONE.12 Every interactive and asynchronous path meets its ratified
      performance, reliability, capacity, and cost budgets. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.12.a Inventory interactive routes/actions and async API/queue/
        worker/provider/host/build/publisher paths with populations and owners.
        Evidence: budget registry.
  - [ ] DONE.12.b Ratify latency/responsiveness/throughput/availability/
        correctness/durability/capacity/cost indicators and budgets by tier.
        Evidence: SLO records.
  - [ ] DONE.12.c Run representative/worst-supported sustained, burst, large-
        object, slow dependency, and failure/recovery tests. Evidence: load
        reports.
  - [ ] DONE.12.d Verify backpressure/quotas/fairness/degradation/cancellation,
        bounded resource use, tenant isolation, data integrity, and cost.
        Evidence: overload proof.
  - [ ] DONE.12.e Link operational telemetry/error budgets and block unresolved
        regressions or unmeasured critical paths. Evidence: final gate.
- [ ] DONE.13 Every supported standards/profile/host/target pair has its own
      conformance evidence and declared limitations. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.13.a Generate the exact supported standards/profile/producer/
        consumer/host/plugin/OS/target/version pair matrix with owners and
        support windows. Evidence: compatibility registry.
  - [ ] DONE.13.b Run pair-specific contract, known-value/fidelity, security,
        accessibility, performance, failure/recovery, and read-back tests.
        Evidence: conformance bundles.
  - [ ] DONE.13.c Record unsupported/partial/lossy/nondeterministic limitations,
        mitigations, UI/docs/API discovery, and expiry per pair. Evidence:
        limitations registry.
  - [ ] DONE.13.d Declare unavailable cells explicitly and block one pair/
        profile/host/target from inheriting another's proof. Evidence:
        independence audit.
- [ ] DONE.14 Security, privacy, safety, rights, integrity, accessibility, and
      supply-chain scans have zero unresolved release blockers. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.14.a Define current threat/data/safety/rights/accessibility/supply-
        chain scopes, severity policy, scanner/tool/config versions, owners, and
        exception rules. Evidence: assurance plan.
  - [ ] DONE.14.b Run code/dependency/container/IaC/secret/license/SBOM/
        provenance, application/host/agent adversarial, privacy leakage, safety/
        claim, rights/consent, integrity, and accessibility assessments.
        Evidence: current reports.
  - [ ] DONE.14.c Deduplicate/triage findings, prove remediation with targeted
        regression, and record residual limitations. Evidence: closure records.
  - [ ] DONE.14.d Verify zero unresolved release blockers and no expired,
        overbroad, conflicting, or self-approved exception. Evidence: release
        gate.
- [ ] DONE.15 Backup, restore, replay, rollback, revocation, dependency outage,
      and disaster-recovery drills meet ratified RPO/RTO. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.15.a Inventory authoritative databases/blobs/evidence/config/keys/
        indexes/queues/targets and ratify RPO/RTO, retention, restore order, and
        owners. Evidence: recovery plan.
  - [ ] DONE.15.b Execute backup integrity and clean-environment restore with
        checksums, keys/config, permissions, queues/indexes, and target
        reconciliation. Evidence: restore report.
  - [ ] DONE.15.c Execute event/job replay, release/deployment rollback,
        source/consent/correction revocation, and provider/host/target outage
        drills. Evidence: drill matrix.
  - [ ] DONE.15.d Measure data loss, downtime, duplicate/partial work, consumer/
        target inconsistency, and achieved RPO/RTO. Evidence: scorecard.
  - [ ] DONE.15.e Remediate and rerun every missed objective or unresolved
        recovery path. Evidence: closure proof.
- [ ] DONE.16 Documentation, generated API references, compatibility matrices,
      user help, runbooks, and architecture diagrams match the shipped system.
      Evidence: final release-evidence index and requirement audit.
  - [ ] DONE.16.a Inventory required operator/developer/user/policy/runbook/
        architecture/compatibility documents with owners, audience, source, and
        release version. Evidence: documentation registry.
  - [ ] DONE.16.b Regenerate API/event/SDK/CLI/host contracts and compatibility/
        limitation matrices from authoritative sources. Evidence: drift-free
        artifacts.
  - [ ] DONE.16.c Verify commands, routes, permissions, examples, screenshots,
        links, diagrams, versions, defaults, failure/recovery, and support
        contacts against the release candidate. Evidence: doc tests.
  - [ ] DONE.16.d Run representative user/operator/developer tasks from docs in
        clean environments and correct discrepancies. Evidence: walkthroughs.
  - [ ] DONE.16.e Block stale, contradictory, inaccessible, unlocalized where
        required, or orphaned documentation. Evidence: final docs gate.
- [ ] DONE.17 Feature flags, fixtures, compatibility shims, temporary
      migrations, deprecated routes, and honesty debt each have an owner and
      removal date. Evidence: final release-evidence index and requirement
      audit.
  - [ ] DONE.17.a Inventory every flag, fixture/demo path, shim/adapter,
        temporary dual read/write/backfill, deprecated route/API/schema, and
        misleading/placeholder state. Evidence: debt registry.
  - [ ] DONE.17.b Record purpose, exact scope, owner, introduced date,
        dependencies, risk, telemetry, removal criterion/date, and rollback.
        Evidence: complete rows.
  - [ ] DONE.17.c Prove production fixtures are visibly labeled/non-mutating,
        flags fail safely, shims honor compatibility, and deprecations notify
        consumers. Evidence: behavior tests.
  - [ ] DONE.17.d Create tracked removal work and alert/escalate overdue or
        ownerless entries; block release blockers and expired shims. Evidence:
        governance gate.
- [ ] DONE.18 Engineering-readiness packets are current and reproducible from
      repository evidence. Evidence: final release-evidence index and
      requirement audit.
  - [ ] DONE.18.a Define packet schema for release identity, scope,
        traceability, contracts/migrations, verification, assurance,
        SLO/performance, recovery, runbooks, limitations, approvals, and
        provenance. Evidence: schema.
  - [ ] DONE.18.b Assemble packets from immutable repository/test/build/target
        artifacts and approved external evidence with exact revisions/
        checksums. Evidence: generated packets.
  - [ ] DONE.18.c Validate links/checksums/freshness/coverage/signatures and
        surface missing/failed/stale/waived/unavailable proof. Evidence: packet
        audit.
  - [ ] DONE.18.d Reproduce packets from a clean checkout/environment and obtain
        identical canonical hashes except declared nondeterministic fields.
        Evidence: reproducibility test.
- [ ] DONE.19 Named human governance approvals exist for each audience, host,
      standard profile, and supported integration. Evidence: final
      release-evidence index and requirement audit.
  - [ ] DONE.19.a Register required approver roles/competence/independence,
        scope, evidence prerequisites, decision options, conditions, expiry, and
        invalidation triggers. Evidence: approval policy.
  - [ ] DONE.19.b Generate exact audience/host/profile/integration coverage
        matrix and attach the immutable release/readiness evidence presented.
        Evidence: review packets.
  - [ ] DONE.19.c Capture named signed approve/reject/conditional decisions with
        role, time, exact revision/scope, rationale, conditions, and conflicts.
        Evidence: approval records.
  - [ ] DONE.19.d Block missing/inherited/expired/stale/conflicted approvals and
        reapprove after material change. Evidence: governance gate.
- [ ] DONE.20 The release commit is present on `origin/main`; no local-only
      commit or branch divergence remains. Evidence: final release-evidence
      index and requirement audit.
  - [ ] DONE.20.a Verify applicable checks pass on the exact staged diff and no
        unrelated/user changes are included. Evidence: verification/staging
        record.
  - [ ] DONE.20.b Commit with a task-scoped message and record commit identity,
        tree, authorship, verification, and release-evidence refs. Evidence:
        local commit.
  - [ ] DONE.20.c Fetch and integrate current `origin/main`, resolve conflicts,
        and rerun affected verification when remote history moved. Evidence:
        integration record.
  - [ ] DONE.20.d Push the exact release commit to `origin/main` and verify
        local `HEAD` equals the fetched remote branch tip. Evidence: remote
        parity check.
  - [ ] DONE.20.e Verify no relevant uncommitted change, local-only commit, or
        branch divergence remains. Evidence: final Git status.
- [ ] DONE.21 The atomic capability inventory is bidirectionally complete: every
      feature asserted or implemented by an app, route, component, package,
      contract, API/job/CLI, schema, event, test, inventory, deep dive,
      walkthrough, proposal, or release spec has a reviewed disposition and
      ledger mapping, and every ledger feature maps back to authoritative or
      explicitly net-new scope. Evidence: deterministic zero-orphan audit and
      seven named domain-owner approvals.
  - [ ] DONE.21.a Enumerate and normalize feature assertions from every named
        repository and documentation source with stable source location,
        symbol/route/contract ID, domain, owner, and checksum. Evidence:
        deterministic source inventory.
  - [ ] DONE.21.b Decompose compound assertions into independently reviewable
        capabilities without generating generic or keyword-derived task text.
        Evidence: semantic review record.
  - [ ] DONE.21.c Map every source capability to one or more exact ledger
        parents/children and a reviewed retain/refactor/migrate/replace/remove/
        external/defer/net-new disposition. Evidence: forward mapping.
  - [ ] DONE.21.d Map every ledger capability back to authoritative source items
        or an explicitly approved net-new requirement with rationale/
        owner/proof. Evidence: reverse mapping.
  - [ ] DONE.21.e Detect and resolve unmapped/orphaned/duplicate/conflicting/
        obsolete/ambiguous items deterministically and preserve reviewed
        exceptions. Evidence: zero-orphan audit.
  - [ ] DONE.21.f Obtain named Isis, Metis, Yemaya, Veritas, Euterpe, Aja, and
        Bellona owner approval of their inventories, dispositions, boundaries,
        and unresolved limitations. Evidence: seven signoffs.

This ledger is complete only when the evidence proves the full system. A large
amount of existing code, a green narrow test, an attractive demo, or the absence
of an obvious TODO is not proof of completion.
